From 549b3599ef7db9f222c64130aa4f1bf6678813f2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 20 Feb 2020 20:42:26 +0000 Subject: [PATCH 0001/1006] Update CHANGES. --- CHANGES | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 70bf61ae..f40dba56 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,17 @@ CHANGES FROM 3.1 TO 3.2 -XXX +* Add selection_active format for when the selection is present but not moving + with the cursor. + +* Fix dragging with modifier keys, so binding keys such as C-MouseDrag1Pane and + C-MouseDragEnd1Pane now work. + +* Add -a to list-keys to also list keys without notes with -N. + +* Do not jump to next word end if already on a word end when selecting a word; + fixes select-word with single character words and vi(1) keys. + +* Fix top and bottom pane calculation with pane border status enabled. CHANGES FROM 3.0a to 3.1 From f65b9c0d36e40ded66d4366b473cc2529e1aa8f8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 24 Feb 2020 09:53:59 +0000 Subject: [PATCH 0002/1006] Change mouse selection so that after selecting a word, dragging selects only words and similar for lines. From Anindya Mukherjee. --- window-copy.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 243 insertions(+), 17 deletions(-) diff --git a/window-copy.c b/window-copy.c index 301fc16e..8e3f63d1 100644 --- a/window-copy.c +++ b/window-copy.c @@ -119,8 +119,12 @@ static void window_copy_cursor_jump_to(struct window_mode_entry *); static void window_copy_cursor_jump_to_back(struct window_mode_entry *); static void window_copy_cursor_next_word(struct window_mode_entry *, const char *); +static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *, + const char *, u_int *, u_int *); static void window_copy_cursor_next_word_end(struct window_mode_entry *, const char *); +static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, + const char *, int, u_int *, u_int *); static void window_copy_cursor_previous_word(struct window_mode_entry *, const char *, int); static void window_copy_scroll_up(struct window_mode_entry *, u_int); @@ -227,6 +231,22 @@ struct window_copy_mode_data { int rectflag; /* in rectangle copy mode? */ int scroll_exit; /* exit on scroll to end? */ + enum { + SEL_CHAR, /* select one char at a time */ + SEL_WORD, /* select one word at a time */ + SEL_LINE, /* select one line at a time */ + } selflag; + + const char *ws; /* word separators */ + + u_int dx; /* drag start position */ + u_int dy; + + u_int selrx; /* selection reset positions */ + u_int selry; + u_int endselrx; + u_int endselry; + u_int cx; u_int cy; @@ -695,6 +715,9 @@ window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) struct client *c = cs->c; struct mouse_event *m = cs->m; struct window_copy_mode_data *data = wme->data; + struct options *oo = cs->s->options; + + data->ws = options_get_string(oo, "word-separators"); if (m != NULL) { window_copy_start_drag(c, m); @@ -1507,12 +1530,19 @@ window_copy_cmd_select_line(struct window_copy_cmd_state *cs) data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; + data->selflag = SEL_LINE; + data->dx = data->cx; + data->dy = screen_hsize(data->backing) + data->cy - data->oy; window_copy_cursor_start_of_line(wme); + data->selrx = data->cx; + data->selry = screen_hsize(data->backing) + data->cy - data->oy; window_copy_start_selection(wme); for (; np > 1; np--) window_copy_cursor_down(wme, 0); window_copy_cursor_end_of_line(wme); + data->endselrx = data->cx; + data->endselry = screen_hsize(data->backing) + data->cy - data->oy; return (WINDOW_COPY_CMD_REDRAW); } @@ -1524,26 +1554,33 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) struct session *s = cs->s; struct window_copy_mode_data *data = wme->data; const char *ws; - u_int px, py, xx; + u_int px, py; data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; + data->selflag = SEL_WORD; + data->dx = data->cx; + data->dy = screen_hsize(data->backing) + data->cy - data->oy; px = data->cx; py = screen_hsize(data->backing) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); ws = options_get_string(s->options, "word-separators"); window_copy_cursor_previous_word(wme, ws, 0); + data->selrx = data->cx; + data->selry = screen_hsize(data->backing) + data->cy - data->oy; window_copy_start_selection(wme); - if (px >= xx || !window_copy_in_set(wme, px + 1, py, ws)) + if (px >= window_copy_find_length(wme, py) || + !window_copy_in_set(wme, px + 1, py, ws)) window_copy_cursor_next_word_end(wme, ws); else { window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); } + data->endselrx = data->cx; + data->endselry = screen_hsize(data->backing) + data->cy - data->oy; return (WINDOW_COPY_CMD_REDRAW); } @@ -2788,6 +2825,7 @@ static void window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) { struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->backing->grid; u_int new_y, start, end; new_y = data->cy; @@ -2798,6 +2836,16 @@ window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) start = new_y; end = old_y; } + + /* + * In word selection mode the first word on the line below the cursor + * might be selected, so add this line to the redraw area. + */ + if (data->selflag == SEL_WORD) { + /* Last grid line in data coordinates. */ + if (end < gd->sy + data->oy - 1) + end++; + } window_copy_redraw_lines(wme, start, end - start + 1); } @@ -2825,22 +2873,82 @@ window_copy_redraw_screen(struct window_mode_entry *wme) } static void -window_copy_synchronize_cursor(struct window_mode_entry *wme) +window_copy_synchronize_cursor_end(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; u_int xx, yy; + int begin = 0; - xx = data->cx; yy = screen_hsize(data->backing) + data->cy - data->oy; + switch (data->selflag) { + case SEL_WORD: + xx = data->cx; + if (data->ws == NULL) + break; + if (data->dy > yy || (data->dy == yy && data->dx > xx)) { + /* Right to left selection. */ + window_copy_cursor_previous_word_pos(wme, data->ws, 0, + &xx, &yy); + begin = 1; + + /* Reset the end. */ + data->endselx = data->endselrx; + data->endsely = data->endselry; + } else { + /* Left to right selection. */ + if (xx >= window_copy_find_length(wme, yy) || + !window_copy_in_set(wme, xx + 1, yy, data->ws)) + window_copy_cursor_next_word_end_pos(wme, + data->ws, &xx, &yy); + + /* Reset the start. */ + data->selx = data->selrx; + data->sely = data->selry; + } + break; + case SEL_LINE: + if (data->dy > yy) { + /* Right to left selection. */ + xx = 0; + begin = 1; + + /* Reset the end. */ + data->endselx = data->endselrx; + data->endsely = data->endselry; + } else { + /* Left to right selection. */ + xx = window_copy_find_length(wme, yy); + + /* Reset the start. */ + data->selx = data->selrx; + data->sely = data->selry; + } + break; + case SEL_CHAR: + xx = data->cx; + break; + } + if (begin) { + data->selx = xx; + data->sely = yy; + } else { + data->endselx = xx; + data->endsely = yy; + } +} + +static void +window_copy_synchronize_cursor(struct window_mode_entry *wme) +{ + struct window_copy_mode_data *data = wme->data; switch (data->cursordrag) { case CURSORDRAG_ENDSEL: - data->endselx = xx; - data->endsely = yy; + window_copy_synchronize_cursor_end(wme); break; case CURSORDRAG_SEL: - data->selx = xx; - data->sely = yy; + data->selx = data->cx; + data->sely = screen_hsize(data->backing) + data->cy - data->oy; break; case CURSORDRAG_NONE: break; @@ -3709,6 +3817,55 @@ window_copy_cursor_next_word(struct window_mode_entry *wme, window_copy_redraw_lines(wme, data->cy, 1); } +static void +window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, + const char *separators, u_int *ppx, u_int *ppy) +{ + struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; + struct options *oo = wp->window->options; + struct screen *back_s = data->backing; + u_int px, py, xx, yy; + int keys, expected = 1; + + px = data->cx; + py = screen_hsize(back_s) + data->cy - data->oy; + xx = window_copy_find_length(wme, py); + yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; + + keys = options_get_number(oo, "mode-keys"); + if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators)) + px++; + + /* + * First skip past any word characters, then any non-word characters. + * + * expected is initially set to 1 for the former and then 0 for the + * latter. + */ + do { + while (px > xx || + window_copy_in_set(wme, px, py, separators) == expected) { + /* Move down if we're past the end of the line. */ + if (px > xx) { + if (py == yy) + return; + py++; + px = 0; + xx = window_copy_find_length(wme, py); + } else + px++; + } + expected = !expected; + } while (expected == 0); + + if (keys == MODEKEY_VI && px != 0) + px--; + + *ppx = px; + *ppy = py; +} + static void window_copy_cursor_next_word_end(struct window_mode_entry *wme, const char *separators) @@ -3761,6 +3918,54 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, window_copy_redraw_lines(wme, data->cy, 1); } +/* Compute the previous place where a word begins. */ +static void +window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, + const char *separators, int already, u_int *ppx, u_int *ppy) +{ + struct window_copy_mode_data *data = wme->data; + u_int px, py; + + px = data->cx; + py = screen_hsize(data->backing) + data->cy - data->oy; + + /* Move back to the previous word character. */ + if (already || window_copy_in_set(wme, px, py, separators)) { + for (;;) { + if (px > 0) { + px--; + if (!window_copy_in_set(wme, px, py, + separators)) + break; + } else { + if (data->cy == 0 && + (screen_hsize(data->backing) == 0 || + data->oy >= + screen_hsize(data->backing) - 1)) + goto out; + py--; + + py = screen_hsize(data->backing) + data->cy - + data->oy; + px = window_copy_find_length(wme, py); + + /* Stop if separator at EOL. */ + if (px > 0 && window_copy_in_set(wme, px - 1, + py, separators)) + break; + } + } + } + + /* Move back to the beginning of this word. */ + while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators)) + px--; + +out: + *ppx = px; + *ppy = py; +} + /* Move to the previous place where a word begins. */ static void window_copy_cursor_previous_word(struct window_mode_entry *wme, @@ -3777,21 +3982,24 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, for (;;) { if (px > 0) { px--; - if (!window_copy_in_set(wme, px, py, separators)) + if (!window_copy_in_set(wme, px, py, + separators)) break; } else { if (data->cy == 0 && (screen_hsize(data->backing) == 0 || - data->oy >= screen_hsize(data->backing) - 1)) + data->oy >= + screen_hsize(data->backing) - 1)) goto out; window_copy_cursor_up(wme, 0); - py = screen_hsize(data->backing) + data->cy - data->oy; + py = screen_hsize(data->backing) + data->cy - + data->oy; px = window_copy_find_length(wme, py); /* Stop if separator at EOL. */ - if (px > 0 && - window_copy_in_set(wme, px - 1, py, separators)) + if (px > 0 && window_copy_in_set(wme, px - 1, + py, separators)) break; } } @@ -3913,6 +4121,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) { struct window_pane *wp; struct window_mode_entry *wme; + struct window_copy_mode_data *data; u_int x, y; if (c == NULL) @@ -3933,10 +4142,27 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) c->tty.mouse_drag_update = window_copy_drag_update; c->tty.mouse_drag_release = window_copy_drag_release; - window_copy_update_cursor(wme, x, y); - window_copy_start_selection(wme); - window_copy_redraw_screen(wme); + data = wme->data; + switch (data->selflag) { + case SEL_WORD: + if (data->ws) { + window_copy_update_cursor(wme, x, y); + window_copy_cursor_previous_word_pos(wme, + data->ws, 0, &x, &y); + y -= screen_hsize(data->backing) - data->oy; + } + window_copy_update_cursor(wme, x, y); + break; + case SEL_LINE: + window_copy_update_cursor(wme, 0, y); + break; + case SEL_CHAR: + window_copy_update_cursor(wme, x, y); + window_copy_start_selection(wme); + break; + } + window_copy_redraw_screen(wme); window_copy_drag_update(c, m); } From 8be179de4669a76464d664d9af6b3301ca3f286d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 2 Mar 2020 08:30:30 +0000 Subject: [PATCH 0003/1006] Use current session for cwd of new sessions, not the new session which doesn't have one yet. GitHub issue 2091. --- spawn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spawn.c b/spawn.c index 933f0c6a..6bcea168 100644 --- a/spawn.c +++ b/spawn.c @@ -230,9 +230,9 @@ spawn_pane(struct spawn_context *sc, char **cause) * the pane's stored one unless specified. */ if (sc->cwd != NULL) - cwd = format_single(item, sc->cwd, c, s, NULL, NULL); + cwd = format_single(item, sc->cwd, c, item->target.s, NULL, NULL); else if (~sc->flags & SPAWN_RESPAWN) - cwd = xstrdup(server_client_get_cwd(c, s)); + cwd = xstrdup(server_client_get_cwd(c, item->target.s)); else cwd = NULL; From add75a06cd9dfa7c01608e81ab418b37ec17533c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 6 Mar 2020 15:35:03 +0000 Subject: [PATCH 0004/1006] Update latest client for target session on switch-client. --- cmd-switch-client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 309a7e7c..61677761 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -142,10 +142,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); - recalculate_sizes(); server_check_unattached(); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; + s->curw->window->latest = c; + recalculate_sizes(); alerts_check_session(s); return (CMD_RETURN_NORMAL); From 2991f4aad0d010db57ce979d151191836036cb5f Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 7 Mar 2020 10:58:32 +0000 Subject: [PATCH 0005/1006] Use correct width of right marker so it doesn't draw over status right when more than one character. Reported by Tyler Culp. --- format-draw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/format-draw.c b/format-draw.c index bb16e0dd..85248aa6 100644 --- a/format-draw.c +++ b/format-draw.c @@ -142,7 +142,8 @@ format_draw_put_list(struct screen_write_ctx *octx, width -= list_left->cx; } if (start + width < list->cx && width > list_right->cx) { - screen_write_cursormove(octx, ocx + offset + width - 1, ocy, 0); + screen_write_cursormove(octx, ocx + offset + width - + list_right->cx, ocy, 0); screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx, 1); width -= list_right->cx; From 444e9f3c582b2d20800b0fe4aa363fb7e801cbc2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 11 Mar 2020 06:38:43 +0000 Subject: [PATCH 0006/1006] Bump 3.1-rc up to master. --- CHANGES | 4 +--- configure.ac | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index f40dba56..728bac60 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,4 @@ -CHANGES FROM 3.1 TO 3.2 +CHANGES FROM 3.0a TO 3.1 * Add selection_active format for when the selection is present but not moving with the cursor. @@ -13,8 +13,6 @@ CHANGES FROM 3.1 TO 3.2 * Fix top and bottom pane calculation with pane border status enabled. -CHANGES FROM 3.0a to 3.1 - * Add support for adding a note to a key binding (with bind-key -N) and use this to add descriptions to the default key bindings. A new -N flag to list-keys shows key bindings with notes. Change the default ? binding to use diff --git a/configure.ac b/configure.ac index 380eea73..d98b72f2 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], next-3.2) +AC_INIT([tmux], 3.1-rc2) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 07bf5cbd274442df877d2033b551a0c2f90798ef Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 11 Mar 2020 06:41:13 +0000 Subject: [PATCH 0007/1006] 3.2 next. --- CHANGES | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 728bac60..7639cf28 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +CHANGES FROM 3.1 TO 3.2 + +XXX + CHANGES FROM 3.0a TO 3.1 * Add selection_active format for when the selection is present but not moving diff --git a/configure.ac b/configure.ac index d98b72f2..380eea73 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.1-rc2) +AC_INIT([tmux], next-3.2) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From c820585dd0b5060d57296ae1086908e0b67b47b5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 11 Mar 2020 14:17:55 +0000 Subject: [PATCH 0008/1006] Add some number operators for formats, from Tyler Culp. --- Makefile | 4 +- format.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- tmux.1 | 34 +++++++++++++++- tmux.h | 1 + 4 files changed, 150 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 6e29c4fe..6645bbfc 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,7 @@ CDIAGFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align CFLAGS += -I${.CURDIR} -LDADD= -lutil -lcurses -levent -DPADD= ${LIBUTIL} ${LIBCURSES} ${LIBEVENT} +LDADD= -lutil -lcurses -levent -lm +DPADD= ${LIBUTIL} ${LIBCURSES} ${LIBEVENT} ${LIBM} .include diff --git a/format.c b/format.c index 4b859501..14d56126 100644 --- a/format.c +++ b/format.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +50,6 @@ static void format_add_tv(struct format_tree *, const char *, struct timeval *); static int format_replace(struct format_tree *, 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 *); @@ -1528,7 +1528,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) } /* Now try single character with arguments. */ - if (strchr("mCs=p", cp[0]) == NULL) + if (strchr("mCs=pe", cp[0]) == NULL) break; c = cp[0]; @@ -1784,6 +1784,108 @@ format_loop_panes(struct format_tree *ft, const char *fmt) return (value); } +static char * +format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, + 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; + enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator; + + if (strcmp(mexp->argv[0], "+") == 0) + operator = ADD; + else if (strcmp(mexp->argv[0], "-") == 0) + operator = SUBTRACT; + else if (strcmp(mexp->argv[0], "*") == 0) + operator = MULTIPLY; + else if (strcmp(mexp->argv[0], "/") == 0) + operator = DIVIDE; + else if (strcmp(mexp->argv[0], "%") == 0 || + strcmp(mexp->argv[0], "m") == 0) + operator = MODULUS; + else { + format_log(ft, "expression has no valid operator: '%s'", + mexp->argv[0]); + goto fail; + } + + /* The second argument may be flags. */ + if (argc >= 2 && strchr(mexp->argv[1], 'f') != NULL) { + use_fp = 1; + prec = 2; + } + + /* The third argument may be precision. */ + if (argc >= 3) { + prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) { + format_log (ft, "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"); + goto fail; + } + + mleft = strtod(left, &endch); + if (*endch != '\0') { + format_log(ft, "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); + goto fail; + } + + if (!use_fp) { + 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); + + switch (operator) { + case ADD: + result = mleft + mright; + break; + case SUBTRACT: + result = mleft - mright; + break; + case MULTIPLY: + result = mleft * mright; + break; + case DIVIDE: + result = mleft / mright; + break; + case MODULUS: + result = fmod(mleft, mright); + break; + } + if (use_fp) + xasprintf(&value, "%.*f", prec, result); + else + xasprintf(&value, "%.*f", prec, (double)(long long)result); + format_log(ft, "expression result is %s", value); + + free(right); + free(left); + return value; + +fail: + free(right); + free(left); + return (NULL); +} + /* Replace a key. */ static int format_replace(struct format_tree *ft, const char *key, size_t keylen, @@ -1796,7 +1898,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, 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; + struct format_modifier **sub = NULL, *mexp = NULL; u_int i, count, nsub = 0; /* Make a copy of the key. */ @@ -1848,6 +1950,11 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, if (errptr != NULL) width = 0; break; + case 'e': + if (fm->argc < 1 || fm->argc > 3) + break; + mexp = fm; + break; case 'l': modifiers |= FORMAT_LITERAL; break; @@ -2024,6 +2131,10 @@ 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); + if (value == NULL) + value = xstrdup(""); } else { /* Neither: look up directly. */ value = format_find(ft, copy, modifiers); diff --git a/tmux.1 b/tmux.1 index c36ad1c5..42d0da2b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4107,7 +4107,7 @@ specifies an .Xr fnmatch 3 or regular expression comparison. The first argument is the pattern and the second the string to compare. -An optional third argument specifies flags: +An optional argument specifies flags: .Ql r means the pattern is a regular expression instead of the default .Xr fnmatch 3 @@ -4134,6 +4134,38 @@ ignores case. For example: .Ql #{C/r:^Start} .Pp +Numeric operators may be performed by prefixing two comma-separated alternatives with an +.Ql e +and an operator. +An optional +.Ql f +flag may be given after the operator to use floating point numbers, otherwise integers are used. +This may be followed by a number giving the number of decimal places to use for the result. +The available operators are: +addition +.Ql + , +subtraction +.Ql - , +multiplication +.Ql * , +division +.Ql / , +and modulus +.Ql m +or +.Ql % +(note that +.Ql % +must be escaped as +.Ql %% +in formats which are also expanded by +.Xr strftime 3 ) . +For example, +.Ql #{e|*|f|4:5.5,3} +multiplies 5.5 by 3 for a result with four decimal places and +.Ql #{e|%%:7,3} +returns the modulus of 7 and 3. +.Pp A limit may be placed on the length of the resultant string by prefixing it by an .Ql = , diff --git a/tmux.h b/tmux.h index bc4b0d6c..67cecd6b 100644 --- a/tmux.h +++ b/tmux.h @@ -1822,6 +1822,7 @@ char *paste_make_sample(struct paste_buffer *); #define FORMAT_PANE 0x80000000U #define FORMAT_WINDOW 0x40000000U struct format_tree; +struct format_modifier; const char *format_skip(const char *, const char *); int format_true(const char *); struct format_tree *format_create(struct client *, struct cmdq_item *, int, From 8d1d7fd775f8c92f4d6c7e6a4fa462f992ae381e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 11 Mar 2020 14:41:25 +0000 Subject: [PATCH 0009/1006] Lock much more quickly. --- .github/lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/lock.yml b/.github/lock.yml index 89126482..08cf2fc0 100644 --- a/.github/lock.yml +++ b/.github/lock.yml @@ -1,4 +1,4 @@ -daysUntilLock: 180 +daysUntilLock: 30 skipCreatedBefore: false exemptLabels: [] lockLabel: false From 6d9beccb41ae7070f27ce47b7e7bebb950ebd2e9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 11 Mar 2020 16:33:55 +0000 Subject: [PATCH 0010/1006] Will need fmod. --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index 380eea73..74600233 100644 --- a/configure.ac +++ b/configure.ac @@ -87,6 +87,9 @@ AC_CHECK_HEADERS([ \ util.h \ ]) +# Look for fmod. +AC_CHECK_LIB(m, fmod) + # Look for library needed for flock. AC_SEARCH_LIBS(flock, bsd) From e4898de98d4f5b3060f40fe64a66d54865588275 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 11 Mar 2020 18:41:14 +0000 Subject: [PATCH 0011/1006] Only need one lm. --- configure.ac | 3 --- 1 file changed, 3 deletions(-) diff --git a/configure.ac b/configure.ac index 81baa0c7..74600233 100644 --- a/configure.ac +++ b/configure.ac @@ -125,9 +125,6 @@ AC_REPLACE_FUNCS([ \ ]) AC_FUNC_STRNLEN -# Look for libm -AC_SEARCH_LIBS(sqrt, m) - # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) From 4eba98313c6f1ddf1070bea8aaf638e8ea455231 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 11 Mar 2020 18:46:42 +0000 Subject: [PATCH 0012/1006] Start a new selection if outside the existing selection after a word has been selected. From Anindya Mukherjee. --- window-copy.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index 8e3f63d1..3e6b3e68 100644 --- a/window-copy.c +++ b/window-copy.c @@ -4122,7 +4122,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) struct window_pane *wp; struct window_mode_entry *wme; struct window_copy_mode_data *data; - u_int x, y; + u_int x, y, yg; if (c == NULL) return; @@ -4143,6 +4143,9 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) c->tty.mouse_drag_release = window_copy_drag_release; data = wme->data; + yg = screen_hsize(data->backing) + y - data->oy; + if (x < data->selrx || x > data->endselrx || yg != data->selry) + data->selflag = SEL_CHAR; switch (data->selflag) { case SEL_WORD: if (data->ws) { From 2a5702a936829c30b1c427028fdb75a21e2e6771 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Mar 2020 09:26:34 +0000 Subject: [PATCH 0013/1006] When the server socket is given by the user with -S, create it with umask 177 instead of 117 because it may not be in a safe directory like the default directory in /tmp. The user can chmod it more open after it is created if they want. --- client.c | 23 +++++++++++------------ server.c | 17 +++++++++++------ tmux.c | 13 ++++++++----- tmux.h | 4 +++- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/client.c b/client.c index 0c2a6ee2..f819e924 100644 --- a/client.c +++ b/client.c @@ -97,7 +97,7 @@ client_get_lock(char *lockfile) /* Connect client to server. */ static int -client_connect(struct event_base *base, const char *path, int start_server) +client_connect(struct event_base *base, const char *path, int flags) { struct sockaddr_un sa; size_t size; @@ -122,7 +122,7 @@ retry: log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; - if (!start_server) + if (~flags & CLIENT_STARTSERVER) goto failed; close(fd); @@ -154,7 +154,7 @@ retry: close(lockfd); return (-1); } - fd = server_start(client_proc, base, lockfd, lockfile); + fd = server_start(client_proc, flags, base, lockfd, lockfile); } if (locked && lockfd >= 0) { @@ -238,7 +238,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) struct cmd_parse_result *pr; struct cmd *cmd; struct msg_command *data; - int cmdflags, fd, i; + int fd, i; const char *ttynam, *cwd; pid_t ppid; enum msgtype msg; @@ -248,17 +248,13 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ signal(SIGCHLD, SIG_IGN); - /* Save the flags. */ - client_flags = flags; - /* Set up the initial command. */ - cmdflags = 0; if (shell_command != NULL) { msg = MSG_SHELL; - cmdflags = CMD_STARTSERVER; + flags = CLIENT_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; - cmdflags = CMD_STARTSERVER; + flags |= CLIENT_STARTSERVER; } else { msg = MSG_COMMAND; @@ -271,19 +267,22 @@ client_main(struct event_base *base, int argc, char **argv, int flags) if (pr->status == CMD_PARSE_SUCCESS) { TAILQ_FOREACH(cmd, &pr->cmdlist->list, qentry) { if (cmd->entry->flags & CMD_STARTSERVER) - cmdflags |= CMD_STARTSERVER; + flags |= CLIENT_STARTSERVER; } cmd_list_free(pr->cmdlist); } else free(pr->error); } + /* Save the flags. */ + client_flags = flags; + /* Create client process structure (starts logging). */ client_proc = proc_start("client"); proc_set_signals(client_proc, client_signal); /* Initialize the client socket and start the server. */ - fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); + fd = client_connect(base, socket_path, client_flags); if (fd == -1) { if (errno == ECONNREFUSED) { fprintf(stderr, "no server running on %s\n", diff --git a/server.c b/server.c index e75b02da..55430e73 100644 --- a/server.c +++ b/server.c @@ -45,6 +45,7 @@ struct clients clients; struct tmuxproc *server_proc; static int server_fd = -1; +static int server_client_flags; static int server_exit; static struct event server_ev_accept; @@ -98,7 +99,7 @@ server_check_marked(void) /* Create server socket. */ static int -server_create_socket(char **cause) +server_create_socket(int flags, char **cause) { struct sockaddr_un sa; size_t size; @@ -117,7 +118,10 @@ server_create_socket(char **cause) if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) goto fail; - mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); + if (flags & CLIENT_DEFAULTSOCKET) + mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); + else + mask = umask(S_IXUSR|S_IRWXG|S_IRWXO); if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { saved_errno = errno; close(fd); @@ -146,8 +150,8 @@ fail: /* Fork new server. */ int -server_start(struct tmuxproc *client, struct event_base *base, int lockfd, - char *lockfile) +server_start(struct tmuxproc *client, int flags, struct event_base *base, + int lockfd, char *lockfile) { int pair[2]; sigset_t set, oldset; @@ -156,6 +160,7 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd, if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) fatal("socketpair failed"); + server_client_flags = flags; sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); @@ -193,7 +198,7 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd, gettimeofday(&start_time, NULL); - server_fd = server_create_socket(&cause); + server_fd = server_create_socket(flags, &cause); if (server_fd != -1) server_update_socket(); c = server_client_create(pair[1]); @@ -396,7 +401,7 @@ server_signal(int sig) break; case SIGUSR1: event_del(&server_ev_accept); - fd = server_create_socket(NULL); + fd = server_create_socket(server_client_flags, NULL); if (fd != -1) { close(server_fd); server_fd = fd; diff --git a/tmux.c b/tmux.c index 6572ad4e..572e705c 100644 --- a/tmux.c +++ b/tmux.c @@ -379,12 +379,15 @@ main(int argc, char **argv) path[strcspn(path, ",")] = '\0'; } } - if (path == NULL && (path = make_label(label, &cause)) == NULL) { - if (cause != NULL) { - fprintf(stderr, "%s\n", cause); - free(cause); + if (path == NULL) { + if ((path = make_label(label, &cause)) == NULL) { + if (cause != NULL) { + fprintf(stderr, "%s\n", cause); + free(cause); + } + exit(1); } - exit(1); + flags |= CLIENT_DEFAULTSOCKET; } socket_path = path; free(label); diff --git a/tmux.h b/tmux.h index 67cecd6b..e4e23fcf 100644 --- a/tmux.h +++ b/tmux.h @@ -1577,6 +1577,8 @@ struct client { #define CLIENT_REDRAWSTATUSALWAYS 0x1000000 #define CLIENT_REDRAWOVERLAY 0x2000000 #define CLIENT_CONTROL_NOOUTPUT 0x4000000 +#define CLIENT_DEFAULTSOCKET 0x8000000 +#define CLIENT_STARTSERVER 0x10000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -2200,7 +2202,7 @@ void server_clear_marked(void); int server_is_marked(struct session *, struct winlink *, struct window_pane *); int server_check_marked(void); -int server_start(struct tmuxproc *, struct event_base *, int, char *); +int server_start(struct tmuxproc *, int, struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); From b8b48e2e37b9dcc1cfebaecbd89d557eca82a0be Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Mar 2020 09:49:43 +0000 Subject: [PATCH 0014/1006] Add C-g to cancel command prompt with vi(1) keys as well as emacs, and q in command mode. --- status.c | 4 ++++ tmux.1 | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/status.c b/status.c index 33f6c47a..6beadb81 100644 --- a/status.c +++ b/status.c @@ -733,6 +733,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) if (c->prompt_mode == PROMPT_ENTRY) { switch (key) { case '\003': /* C-c */ + case '\007': /* C-g */ case '\010': /* C-h */ case '\011': /* Tab */ case '\025': /* C-u */ @@ -813,6 +814,9 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) case 'p': *new_key = '\031'; /* C-y */ return (1); + case 'q': + *new_key = '\003'; /* C-c */ + return (1); case 's': case KEYC_DC: case 'x': diff --git a/tmux.1 b/tmux.1 index 42d0da2b..cd0a9236 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4837,7 +4837,7 @@ on the value of the option: .Bl -column "FunctionXXXXXXXXXXXXXXXXXXXXXXXXX" "viXXXX" "emacsX" -offset indent .It Sy "Function" Ta Sy "vi" Ta Sy "emacs" -.It Li "Cancel command prompt" Ta "Escape" Ta "Escape" +.It Li "Cancel command prompt" Ta "q" Ta "Escape" .It Li "Delete from cursor to start of word" Ta "" Ta "C-w" .It Li "Delete entire command" Ta "d" Ta "C-u" .It Li "Delete from cursor to end" Ta "D" Ta "C-k" From f7bc753442ef23ec96266dad738cf2dc22343118 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Mar 2020 13:16:16 +0000 Subject: [PATCH 0015/1006] Change how double and triple clicks works so that one or the other is fired - a double click is no longer triggered on the way to a triple click. --- server-client.c | 35 +++++++++++++++++++++++++---------- tmux.h | 2 ++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/server-client.c b/server-client.c index 123cc531..5e6c6947 100644 --- a/server-client.c +++ b/server-client.c @@ -419,7 +419,6 @@ server_client_check_mouse(struct client *c, struct key_event *event) struct winlink *wl; struct window_pane *wp; u_int x, y, b, sx, sy, px, py; - int flag; key_code key; struct timeval tv; struct style_range *sr; @@ -443,7 +442,11 @@ server_client_check_mouse(struct client *c, struct key_event *event) m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag); /* What type of event is this? */ - if ((m->sgr_type != ' ' && + if (event->key == KEYC_DOUBLECLICK) { + type = DOUBLE; + x = m->x, y = m->y, b = m->b; + log_debug("double-click at %u,%u", x, y); + } else if ((m->sgr_type != ' ' && MOUSE_DRAG(m->sgr_b) && MOUSE_BUTTONS(m->sgr_b) == 3) || (m->sgr_type == ' ' && @@ -477,10 +480,8 @@ server_client_check_mouse(struct client *c, struct key_event *event) evtimer_del(&c->click_timer); c->flags &= ~CLIENT_DOUBLECLICK; if (m->b == c->click_button) { - type = DOUBLE; - x = m->x, y = m->y, b = m->b; - log_debug("double-click at %u,%u", x, y); - flag = CLIENT_TRIPLECLICK; + type = NOTYPE; + c->flags |= CLIENT_TRIPLECLICK; goto add_timer; } } else if (c->flags & CLIENT_TRIPLECLICK) { @@ -497,11 +498,11 @@ server_client_check_mouse(struct client *c, struct key_event *event) type = DOWN; x = m->x, y = m->y, b = m->b; log_debug("down at %u,%u", x, y); - flag = CLIENT_DOUBLECLICK; + c->flags |= CLIENT_DOUBLECLICK; add_timer: if (KEYC_CLICK_TIMEOUT != 0) { - c->flags |= flag; + memcpy(&c->click_event, m, sizeof c->click_event); c->click_button = m->b; tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000; @@ -1047,7 +1048,7 @@ server_client_key_callback(struct cmdq_item *item, void *data) /* Check for mouse keys. */ m->valid = 0; - if (key == KEYC_MOUSE) { + if (key == KEYC_MOUSE || key == KEYC_DOUBLECLICK) { if (c->flags & CLIENT_READONLY) goto out; key = server_client_check_mouse(c, event); @@ -1549,8 +1550,22 @@ server_client_repeat_timer(__unused int fd, __unused short events, void *data) static void server_client_click_timer(__unused int fd, __unused short events, void *data) { - struct client *c = data; + struct client *c = data; + struct key_event *event; + log_debug("click timer expired"); + + if (c->flags & CLIENT_TRIPLECLICK) { + /* + * Waiting for a third click that hasn't happened, so this must + * have been a double click. + */ + event = xmalloc(sizeof *event); + event->key = KEYC_DOUBLECLICK; + memcpy(&event->m, &c->click_event, sizeof event->m); + if (!server_client_handle_key(c, event)) + free(event); + } c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK); } diff --git a/tmux.h b/tmux.h index e4e23fcf..ac1296dc 100644 --- a/tmux.h +++ b/tmux.h @@ -166,6 +166,7 @@ enum { /* Mouse keys. */ KEYC_MOUSE, /* unclassified mouse event */ KEYC_DRAGGING, /* dragging in progress */ + KEYC_DOUBLECLICK, /* double click complete */ KEYC_MOUSE_KEY(MOUSEMOVE), KEYC_MOUSE_KEY(MOUSEDOWN1), KEYC_MOUSE_KEY(MOUSEDOWN2), @@ -1547,6 +1548,7 @@ struct client { struct event click_timer; u_int click_button; + struct mouse_event click_event; struct status_line status; From 7863445e5d6823ab21c49904b8e5ec5028264d39 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Mar 2020 13:19:20 +0000 Subject: [PATCH 0016/1006] Add a copy-mode -H flag to hide the position marker in the top right. --- cmd-copy-mode.c | 4 ++-- tmux.1 | 5 ++++- window-copy.c | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index b35d0af1..9c0015bf 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = { .name = "copy-mode", .alias = NULL, - .args = { "Met:u", 0, 0 }, - .usage = "[-Mu] " CMD_TARGET_PANE_USAGE, + .args = { "eHMt:u", 0, 0 }, + .usage = "[-eHMu] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/tmux.1 b/tmux.1 index cd0a9236..ac6d4260 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1565,7 +1565,7 @@ The synopsis for the command is: .Bl -tag -width Ds .It Xo Ic copy-mode -.Op Fl Meu +.Op Fl eHMu .Op Fl t Ar target-pane .Xc Enter copy mode. @@ -1575,6 +1575,9 @@ option scrolls one page up. .Fl M begins a mouse drag (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . +.Fl H +hides the position indicator in the top right. +.Pp .Fl e specifies that scrolling to the bottom of the history (to the visible screen) should exit copy mode. diff --git a/window-copy.c b/window-copy.c index 3e6b3e68..1aa1734f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -230,6 +230,7 @@ struct window_copy_mode_data { } lineflag; /* line selection mode */ int rectflag; /* in rectangle copy mode? */ int scroll_exit; /* exit on scroll to end? */ + int hide_position; /* hide position marker */ enum { SEL_CHAR, /* select one char at a time */ @@ -345,6 +346,7 @@ window_copy_init(struct window_mode_entry *wme, data->cy = data->backing->cy; data->scroll_exit = args_has(args, 'e'); + data->hide_position = args_has(args, 'H'); data->screen.cx = data->cx; data->screen.cy = data->cy; @@ -2774,7 +2776,7 @@ window_copy_write_line(struct window_mode_entry *wme, style_apply(&gc, oo, "mode-style"); gc.flags |= GRID_FLAG_NOPALETTE; - if (py == 0 && s->rupper < s->rlower) { + if (py == 0 && s->rupper < s->rlower && !data->hide_position) { if (data->searchmark == NULL) { size = xsnprintf(hdr, sizeof hdr, "[%u/%u]", data->oy, screen_hsize(data->backing)); From 516f6099fc2e928587e573176cd753ce3de5806b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Mar 2020 13:25:45 +0000 Subject: [PATCH 0017/1006] Add a -d flag to run-shell to wait for delay before running the command, also allow run-shell to accept no command to just delay. --- cmd-run-shell.c | 60 +++++++++++++++++++++++++++++++++++++++++-------- tmux.1 | 11 ++++++--- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 2f45f492..5f207bbb 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -31,6 +31,7 @@ static enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmdq_item *); +static void cmd_run_shell_timer(int, short, void *); static void cmd_run_shell_callback(struct job *); static void cmd_run_shell_free(void *); static void cmd_run_shell_print(struct job *, const char *); @@ -39,8 +40,8 @@ const struct cmd_entry cmd_run_shell_entry = { .name = "run-shell", .alias = "run", - .args = { "bt:", 1, 1 }, - .usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command", + .args = { "bd:t:", 0, 1 }, + .usage = "[-b] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -50,8 +51,11 @@ const struct cmd_entry cmd_run_shell_entry = { struct cmd_run_shell_data { char *cmd; + char *cwd; struct cmdq_item *item; + struct session *s; int wp_id; + struct event timer; }; static void @@ -91,9 +95,14 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct window_pane *wp = item->target.wp; + const char *delay; + double d; + struct timeval tv; + char *end; cdata = xcalloc(1, sizeof *cdata); - cdata->cmd = format_single(item, args->argv[0], c, s, wl, wp); + if (args->argc != 0) + cdata->cmd = format_single(item, args->argv[0], c, s, wl, wp); if (args_has(args, 't') && wp != NULL) cdata->wp_id = wp->id; @@ -103,18 +112,48 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'b')) cdata->item = item; - if (job_run(cdata->cmd, s, server_client_get_cwd(item->client, s), NULL, - cmd_run_shell_callback, cmd_run_shell_free, cdata, 0) == NULL) { - cmdq_error(item, "failed to run command: %s", cdata->cmd); - free(cdata); - return (CMD_RETURN_ERROR); - } + cdata->cwd = xstrdup(server_client_get_cwd(item->client, s)); + cdata->s = s; + session_add_ref(s, __func__); + + evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata); + + if ((delay = args_get(args, 'd')) != NULL) { + d = strtod(delay, &end); + if (*end != '\0') { + cmdq_error(item, "invalid delay time: %s", delay); + cmd_run_shell_free(cdata); + return (CMD_RETURN_ERROR); + } + timerclear(&tv); + tv.tv_sec = (time_t)d; + tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U; + evtimer_add(&cdata->timer, &tv); + } else + cmd_run_shell_timer(-1, 0, cdata); if (args_has(args, 'b')) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } +static void +cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) +{ + struct cmd_run_shell_data *cdata = arg; + + if (cdata->cmd != NULL) { + if (job_run(cdata->cmd, cdata->s, cdata->cwd, NULL, + cmd_run_shell_callback, cmd_run_shell_free, cdata, + 0) == NULL) + cmd_run_shell_free(cdata); + } else { + if (cdata->item != NULL) + cmdq_continue(cdata->item); + cmd_run_shell_free(cdata); + } +} + static void cmd_run_shell_callback(struct job *job) { @@ -163,6 +202,9 @@ cmd_run_shell_free(void *data) { struct cmd_run_shell_data *cdata = data; + evtimer_del(&cdata->timer); + session_remove_ref(cdata->s, __func__); + free(cdata->cwd); free(cdata->cmd); free(cdata); } diff --git a/tmux.1 b/tmux.1 index ac6d4260..d6024ac1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5202,8 +5202,9 @@ Lock each client individually by running the command specified by the option. .It Xo Ic run-shell .Op Fl b +.Op Fl d Ar delay .Op Fl t Ar target-pane -.Ar shell-command +.Ar [shell-command] .Xc .D1 (alias: Ic run ) Execute @@ -5216,8 +5217,12 @@ section. With .Fl b , the command is run in the background. -After it finishes, any output to stdout is displayed in copy mode (in the pane -specified by +.Fl d +waits for +.Ar delay +seconds before starting the command. +After the command finishes, any output to stdout is displayed in view mode (in +the pane specified by .Fl t or the current pane if omitted). If the command doesn't return success, the exit status is also displayed. From 6571dd50f86927595b6edd2d6fe4a8982b61d8c6 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Mar 2020 13:48:32 +0000 Subject: [PATCH 0018/1006] Tidy up the default mouse key bindings and: - Add double and triple click bindings to copy a word or line outside copy mode. The text is selected for a short period to show what has been copied. This is in line with the existing mouse selection where the text is copied and the selection is cleared when the mouse button is released. - Change the existing double and triple click bindings in copy mode to behave in the same way. - Add a button 2 binding to paste the top buffer. --- key-bindings.c | 58 +++++++++++++++++++++++++++++++++++++++----------- tmux.1 | 2 +- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index 4387c011..57b71c0f 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -229,6 +229,7 @@ void key_bindings_init(void) { static const char *defaults[] = { + /* Prefix keys. */ "bind -N 'Send the prefix key' C-b send-prefix", "bind -N 'Rotate through the panes' C-o rotate-window", "bind -N 'Suspend the current client' C-z suspend-client", @@ -312,21 +313,51 @@ key_bindings_init(void) "bind -N 'Resize the pane left' -r C-Left resize-pane -L", "bind -N 'Resize the pane right' -r C-Right resize-pane -R", - "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", + /* Menu keys */ + "bind < display-menu -xW -yS -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, + "bind > display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU, + + /* Mouse button 1 down on pane. */ + "bind -n MouseDown1Pane select-pane -t=\\; send -M", + + /* Mouse button 1 drag on pane. */ + "bind -n MouseDrag1Pane if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M }", + + /* Mouse wheel up on pane. */ + "bind -n WheelUpPane if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -e }", + + /* Mouse button 2 down on pane. */ + "bind -n MouseDown2Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { paste -p }", + + /* Mouse button 1 double click on pane. */ + "bind -n DoubleClick1Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-word; run -d0.3; send -X copy-selection-and-cancel }", + + /* Mouse button 1 triple click on pane. */ + "bind -n TripleClick1Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-selection-and-cancel }", + + /* Mouse button 1 drag on border. */ "bind -n MouseDrag1Border resize-pane -M", + + /* Mouse button 1 down on status line. */ "bind -n MouseDown1Status select-window -t=", + + /* Mouse wheel down on status line. */ "bind -n WheelDownStatus next-window", + + /* Mouse wheel up on status line. */ "bind -n WheelUpStatus previous-window", - "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", - "bind -n WheelUpPane if -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", - "bind -n MouseDown3StatusLeft display-menu -t= -xM -yS -T \"#[align=centre]#{session_name}\" " DEFAULT_SESSION_MENU, - "bind -n MouseDown3Status display-menu -t= -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU, - "bind < display-menu -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU, - "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{pane_in_mode}}' 'select-pane -t=; send-keys -M' {display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU "}", - "bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU, - "bind > display-menu -xP -yP -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU, + /* Mouse button 3 down on status left. */ + "bind -n MouseDown3StatusLeft display-menu -t= -xM -yS -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU, + /* Mouse button 3 down on status line. */ + "bind -n MouseDown3Status display-menu -t= -xW -yS -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, + + /* Mouse button 3 down on pane. */ + "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{pane_in_mode}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", + "bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU, + + /* Copy mode (emacs) keys. */ "bind -Tcopy-mode C-Space send -X begin-selection", "bind -Tcopy-mode C-a send -X start-of-line", "bind -Tcopy-mode C-c send -X cancel", @@ -361,8 +392,8 @@ key_bindings_init(void) "bind -Tcopy-mode MouseDragEnd1Pane send -X copy-selection-and-cancel", "bind -Tcopy-mode WheelUpPane select-pane\\; send -N5 -X scroll-up", "bind -Tcopy-mode WheelDownPane select-pane\\; send -N5 -X scroll-down", - "bind -Tcopy-mode DoubleClick1Pane select-pane\\; send -X select-word", - "bind -Tcopy-mode TripleClick1Pane select-pane\\; send -X select-line", + "bind -Tcopy-mode DoubleClick1Pane select-pane\\; send -X select-word\\; run -d0.3\\; send -X copy-selection-and-cancel", + "bind -Tcopy-mode TripleClick1Pane select-pane\\; send -X select-line\\; run -d0.3\\; send -X copy-selection-and-cancel", "bind -Tcopy-mode NPage send -X page-down", "bind -Tcopy-mode PPage send -X page-up", "bind -Tcopy-mode Up send -X cursor-up", @@ -396,6 +427,7 @@ key_bindings_init(void) "bind -Tcopy-mode C-Up send -X scroll-up", "bind -Tcopy-mode C-Down send -X scroll-down", + /* Copy mode (vi) keys. */ "bind -Tcopy-mode-vi '#' send -FX search-backward '#{copy_cursor_word}'", "bind -Tcopy-mode-vi * send -FX search-forward '#{copy_cursor_word}'", "bind -Tcopy-mode-vi C-c send -X cancel", @@ -465,8 +497,8 @@ key_bindings_init(void) "bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel", "bind -Tcopy-mode-vi WheelUpPane select-pane\\; send -N5 -X scroll-up", "bind -Tcopy-mode-vi WheelDownPane select-pane\\; send -N5 -X scroll-down", - "bind -Tcopy-mode-vi DoubleClick1Pane select-pane\\; send -X select-word", - "bind -Tcopy-mode-vi TripleClick1Pane select-pane\\; send -X select-line", + "bind -Tcopy-mode-vi DoubleClick1Pane select-pane\\; send -X select-word\\; run -d0.3\\; send -X copy-selection-and-cancel", + "bind -Tcopy-mode-vi TripleClick1Pane select-pane\\; send -X select-line\\; run -d0.3\\; send -X copy-selection-and-cancel", "bind -Tcopy-mode-vi BSpace send -X cursor-left", "bind -Tcopy-mode-vi NPage send -X page-down", "bind -Tcopy-mode-vi PPage send -X page-up", diff --git a/tmux.1 b/tmux.1 index d6024ac1..7ed6b334 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5204,7 +5204,7 @@ option. .Op Fl b .Op Fl d Ar delay .Op Fl t Ar target-pane -.Ar [shell-command] +.Op Ar shell-command .Xc .D1 (alias: Ic run ) Execute From fa36e9bc88cab7f721911bc56b397b9fd55dc038 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Mar 2020 06:19:33 +0000 Subject: [PATCH 0019/1006] Do not add a reference to the session if no session is present. --- cmd-run-shell.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 5f207bbb..bc21cc9c 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -114,7 +114,8 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->cwd = xstrdup(server_client_get_cwd(item->client, s)); cdata->s = s; - session_add_ref(s, __func__); + if (s != NULL) + session_add_ref(s, __func__); evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata); @@ -203,7 +204,8 @@ cmd_run_shell_free(void *data) struct cmd_run_shell_data *cdata = data; evtimer_del(&cdata->timer); - session_remove_ref(cdata->s, __func__); + if (cdata->s != NULL) + session_remove_ref(cdata->s, __func__); free(cdata->cwd); free(cdata->cmd); free(cdata); From fa3871b1be21a23ec14b4e8a3779f30dc04ef256 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 15 Mar 2020 20:35:52 +0000 Subject: [PATCH 0020/1006] Fix C-Space key string. --- key-string.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/key-string.c b/key-string.c index d2b31e03..38e5b8a7 100644 --- a/key-string.c +++ b/key-string.c @@ -257,6 +257,10 @@ key_string_lookup_key(key_code key) return (out); } + /* Display C-@ as C-Space. */ + if ((key & KEYC_MASK_KEY) == 0) + key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); + /* Fill in the modifiers. */ if (key & KEYC_CTRL) strlcat(out, "C-", sizeof out); @@ -329,15 +333,6 @@ key_string_lookup_key(key_code key) return (out); } - /* - * Special case: display C-@ as C-Space. Could do this below in - * the (key >= 0 && key <= 32), but this way we let it be found - * in key_string_table, for the unlikely chance that we might - * change its name. - */ - if ((key & KEYC_MASK_KEY) == 0) - key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); - /* Try the key against the string table. */ for (i = 0; i < nitems(key_string_table); i++) { if (key == key_string_table[i].key) From 882d0b785d52843b76a387ff3c6bf2cae46fc7ec Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 15 Mar 2020 20:44:19 +0000 Subject: [PATCH 0021/1006] Reset selection flag when clearing or stopping selection, from Mark Kelly. --- window-copy.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/window-copy.c b/window-copy.c index 1aa1734f..76cea4e6 100644 --- a/window-copy.c +++ b/window-copy.c @@ -305,6 +305,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; if (wp->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; @@ -739,6 +740,7 @@ window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs) data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; return (WINDOW_COPY_CMD_NOTHING); } @@ -3360,6 +3362,7 @@ window_copy_clear_selection(struct window_mode_entry *wme) data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); From d162ff48f3069c8371a40ddbcf987946dbddf953 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Mar 2020 06:12:42 +0000 Subject: [PATCH 0022/1006] Send mouse down event immediately rather than waiting for double click to finish which would now mean it was out of order. Reported by Mark Kelly. --- input-keys.c | 11 +++++------ server-client.c | 10 +++++++--- tmux.h | 2 ++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/input-keys.c b/input-keys.c index 5c7e8b1f..d09bf8fb 100644 --- a/input-keys.c +++ b/input-keys.c @@ -254,12 +254,12 @@ static void input_key_mouse(struct window_pane *wp, struct mouse_event *m) { struct screen *s = wp->screen; - int mode = s->mode; char buf[40]; size_t len; u_int x, y; - if ((mode & ALL_MOUSE_MODES) == 0) + /* Ignore events if no mouse mode or the pane is not visible. */ + if (m->ignore || (s->mode & ALL_MOUSE_MODES) == 0) return; if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; @@ -267,8 +267,7 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) return; /* If this pane is not in button or all mode, discard motion events. */ - if (MOUSE_DRAG(m->b) && - (mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL)) == 0) + if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0) return; /* @@ -280,13 +279,13 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) if (m->sgr_type != ' ') { if (MOUSE_DRAG(m->sgr_b) && MOUSE_BUTTONS(m->sgr_b) == 3 && - (~mode & MODE_MOUSE_ALL)) + (~s->mode & MODE_MOUSE_ALL)) return; } else { if (MOUSE_DRAG(m->b) && MOUSE_BUTTONS(m->b) == 3 && MOUSE_BUTTONS(m->lb) == 3 && - (~mode & MODE_MOUSE_ALL)) + (~s->mode & MODE_MOUSE_ALL)) return; } diff --git a/server-client.c b/server-client.c index 5e6c6947..02113386 100644 --- a/server-client.c +++ b/server-client.c @@ -419,6 +419,7 @@ server_client_check_mouse(struct client *c, struct key_event *event) struct winlink *wl; struct window_pane *wp; u_int x, y, b, sx, sy, px, py; + int ignore = 0; key_code key; struct timeval tv; struct style_range *sr; @@ -445,6 +446,7 @@ server_client_check_mouse(struct client *c, struct key_event *event) if (event->key == KEYC_DOUBLECLICK) { type = DOUBLE; x = m->x, y = m->y, b = m->b; + ignore = 1; log_debug("double-click at %u,%u", x, y); } else if ((m->sgr_type != ' ' && MOUSE_DRAG(m->sgr_b) && @@ -491,16 +493,17 @@ server_client_check_mouse(struct client *c, struct key_event *event) type = TRIPLE; x = m->x, y = m->y, b = m->b; log_debug("triple-click at %u,%u", x, y); + ignore = 1; goto have_event; } - } + } else + c->flags |= CLIENT_DOUBLECLICK; + add_timer: type = DOWN; x = m->x, y = m->y, b = m->b; log_debug("down at %u,%u", x, y); - c->flags |= CLIENT_DOUBLECLICK; - add_timer: if (KEYC_CLICK_TIMEOUT != 0) { memcpy(&c->click_event, m, sizeof c->click_event); c->click_button = m->b; @@ -519,6 +522,7 @@ have_event: /* Save the session. */ m->s = s->id; m->w = -1; + m->ignore = ignore; /* Is this on the status line? */ m->statusat = status_at_line(c); diff --git a/tmux.h b/tmux.h index ac1296dc..786cb0dd 100644 --- a/tmux.h +++ b/tmux.h @@ -561,6 +561,7 @@ struct msg_write_close { #define ALL_MODES 0xffffff #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) +#define MOTION_MOUSE_MODES (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) /* * A single UTF-8 character. UTF8_SIZE must be big enough to hold @@ -1117,6 +1118,7 @@ RB_HEAD(sessions, session); /* Mouse input. */ struct mouse_event { int valid; + int ignore; key_code key; From 9abeff7f0b8fcfab96af299a071de6483bf02a8f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Mar 2020 08:23:24 +0000 Subject: [PATCH 0023/1006] FIx type for %u, from Thomas Adam. --- window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/window.c b/window.c index a4b452eb..ab1d9217 100644 --- a/window.c +++ b/window.c @@ -425,8 +425,8 @@ window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) ypixel = DEFAULT_YPIXEL; log_debug("%s: @%u resize %ux%u (%ux%u)", __func__, w->id, sx, sy, - xpixel == -1 ? w->xpixel : xpixel, - ypixel == -1 ? w->ypixel : ypixel); + xpixel == -1 ? w->xpixel : (u_int)xpixel, + ypixel == -1 ? w->ypixel : (u_int)ypixel); w->sx = sx; w->sy = sy; if (xpixel != -1) From 7815b30c7d15cc5a728687de3a54f032c77cb4e3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Mar 2020 09:12:44 +0000 Subject: [PATCH 0024/1006] Terminate the output buffer for control mode output - it is now used as a string. GitHub issue 2114. --- control-notify.c | 1 + 1 file changed, 1 insertion(+) diff --git a/control-notify.c b/control-notify.c index babfcf2d..a513c147 100644 --- a/control-notify.c +++ b/control-notify.c @@ -54,6 +54,7 @@ control_notify_input(struct client *c, struct window_pane *wp, else evbuffer_add_printf(message, "%c", buf[i]); } + evbuffer_add(message, "", 1); control_write(c, "%s", EVBUFFER_DATA(message)); evbuffer_free(message); } From 7cae4e8e89a98329843e3df8b3644f501288256b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Mar 2020 09:18:47 +0000 Subject: [PATCH 0025/1006] Turn off mouse mode 1003 as well as the rest when exiting. --- tty.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tty.c b/tty.c index 3bab556d..8efe57b5 100644 --- a/tty.c +++ b/tty.c @@ -330,8 +330,10 @@ tty_start_tty(struct tty *tty) log_debug("%s: using UTF-8 for ACS", c->name); tty_putcode(tty, TTYC_CNORM); - if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); + if (tty_term_has(tty->term, TTYC_KMOUS)) { + tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l"); + tty_puts(tty, "\033[?1006l\033[?1005l"); + } if (tty_term_flag(tty->term, TTYC_XT)) { if (options_get_number(global_options, "focus-events")) { @@ -404,8 +406,10 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); - if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); + if (tty_term_has(tty->term, TTYC_KMOUS)) { + tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l"); + tty_raw(tty, "\033[?1006l\033[?1005l"); + } if (tty_term_flag(tty->term, TTYC_XT)) { if (tty->flags & TTY_FOCUS) { From 37b7a29ccac2e8f37a37b8c4ad8e9ceedee25ee9 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Mar 2020 10:49:06 +0000 Subject: [PATCH 0026/1006] VTE treats each mouse mode bit as independent, so turning off 1000 doesn't also turn off 1001, so don't rely on that behaviour. GitHub issue 2116. --- tty.c | 52 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/tty.c b/tty.c index 8efe57b5..ef66ebbb 100644 --- a/tty.c +++ b/tty.c @@ -658,7 +658,8 @@ tty_force_cursor_colour(struct tty *tty, const char *ccolour) void tty_update_mode(struct tty *tty, int mode, struct screen *s) { - int changed; + struct client *c = tty->client; + int changed; if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) tty_force_cursor_colour(tty, s->ccolour); @@ -667,6 +668,10 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; + if (changed == 0) + return; + log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); + if (changed & MODE_BLINKING) { if (tty_term_has(tty->term, TTYC_CVVIS)) tty_putcode(tty, TTYC_CVVIS); @@ -690,28 +695,31 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } tty->cstyle = s->cstyle; } - if (changed & ALL_MOUSE_MODES) { - if (mode & ALL_MOUSE_MODES) { - /* - * Enable the SGR (1006) extension unconditionally, as - * it is safe from misinterpretation. - */ - tty_puts(tty, "\033[?1006h"); - if (mode & MODE_MOUSE_ALL) - tty_puts(tty, "\033[?1003h"); - else if (mode & MODE_MOUSE_BUTTON) - tty_puts(tty, "\033[?1002h"); - else if (mode & MODE_MOUSE_STANDARD) - tty_puts(tty, "\033[?1000h"); - } else { - if (tty->mode & MODE_MOUSE_ALL) - tty_puts(tty, "\033[?1003l"); - else if (tty->mode & MODE_MOUSE_BUTTON) - tty_puts(tty, "\033[?1002l"); - else if (tty->mode & MODE_MOUSE_STANDARD) - tty_puts(tty, "\033[?1000l"); + if ((changed & ALL_MOUSE_MODES) && + tty_term_has(tty->term, TTYC_KMOUS)) { + if ((mode & ALL_MOUSE_MODES) == 0) tty_puts(tty, "\033[?1006l"); - } + if ((changed & MODE_MOUSE_STANDARD) && + (~mode & MODE_MOUSE_STANDARD)) + tty_puts(tty, "\033[?1000l"); + if ((changed & MODE_MOUSE_BUTTON) && + (~mode & MODE_MOUSE_BUTTON)) + tty_puts(tty, "\033[?1002l"); + if ((changed & MODE_MOUSE_ALL) && + (~mode & MODE_MOUSE_ALL)) + tty_puts(tty, "\033[?1003l"); + + if (mode & ALL_MOUSE_MODES) + tty_puts(tty, "\033[?1006h"); + if ((changed & MODE_MOUSE_STANDARD) && + (mode & MODE_MOUSE_STANDARD)) + tty_puts(tty, "\033[?1000h"); + if ((changed & MODE_MOUSE_BUTTON) && + (mode & MODE_MOUSE_BUTTON)) + tty_puts(tty, "\033[?1002h"); + if ((changed & MODE_MOUSE_ALL) && + (mode & MODE_MOUSE_ALL)) + tty_puts(tty, "\033[?1003h"); } if (changed & MODE_BRACKETPASTE) { if (mode & MODE_BRACKETPASTE) From 7021757c9d74eda8a5c1d7940b0d33bbad41fd29 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Mar 2020 14:17:56 +0000 Subject: [PATCH 0027/1006] Adjust selection correctly when scrolling, from Anindya Mukherjee. --- window-copy.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/window-copy.c b/window-copy.c index 76cea4e6..6864d8f9 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2877,15 +2877,15 @@ window_copy_redraw_screen(struct window_mode_entry *wme) } static void -window_copy_synchronize_cursor_end(struct window_mode_entry *wme) +window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin) { struct window_copy_mode_data *data = wme->data; u_int xx, yy; - int begin = 0; yy = screen_hsize(data->backing) + data->cy - data->oy; switch (data->selflag) { case SEL_WORD: + begin = 0; xx = data->cx; if (data->ws == NULL) break; @@ -2911,6 +2911,7 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme) } break; case SEL_LINE: + begin = 0; if (data->dy > yy) { /* Right to left selection. */ xx = 0; @@ -2948,11 +2949,10 @@ window_copy_synchronize_cursor(struct window_mode_entry *wme) switch (data->cursordrag) { case CURSORDRAG_ENDSEL: - window_copy_synchronize_cursor_end(wme); + window_copy_synchronize_cursor_end(wme, 0); break; case CURSORDRAG_SEL: - data->selx = data->cx; - data->sely = screen_hsize(data->backing) + data->cy - data->oy; + window_copy_synchronize_cursor_end(wme, 1); break; case CURSORDRAG_NONE: break; From 69eff51538076815c981477d527a10185b8212a6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 16 Mar 2020 15:11:34 +0000 Subject: [PATCH 0028/1006] Add. --- CHANGES | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 7639cf28..e2bda92f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,23 @@ CHANGES FROM 3.1 TO 3.2 -XXX +* Change double and triple click bindings so that only one is fired (previously + double click was fired on the way to triple click). Also add default double + and triple click binding to copy the word or line under the cursor and change + the existing bindings in copy mode to do the same. + +* Add -d flag to run-shell to delay before running the command and allow it to + run without a command so it just delays. + +* Add C-g to cancel command prompt with vi(1) keys as well as emacs, and q in + command mode. + +* When the server socket is given with -S, create it with umask 177 instead of + 117 (because it may not be in a safe directory like the default directory in + /tmp). + +* Add a copy-mode -H flag to hide the position marker in the top right. + +* Add number operators for formats (+, -, *, / and m), CHANGES FROM 3.0a TO 3.1 From 372841f70ab239e960f58096bf36eabff6dac1d0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 16 Mar 2020 15:12:16 +0000 Subject: [PATCH 0029/1006] Add to CHANGES. --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index e2bda92f..a177524b 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,8 @@ CHANGES FROM 3.1 TO 3.2 and triple click binding to copy the word or line under the cursor and change the existing bindings in copy mode to do the same. +* Add a default binding for button 2 to paste. + * Add -d flag to run-shell to delay before running the command and allow it to run without a command so it just delays. From 62c646ac32caeaabe6f570d9d482409462db4c28 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 16 Mar 2020 15:13:35 +0000 Subject: [PATCH 0030/1006] Add couple of CHANGES tweaks. --- CHANGES | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index a177524b..c938e28c 100644 --- a/CHANGES +++ b/CHANGES @@ -2,15 +2,15 @@ CHANGES FROM 3.1 TO 3.2 * Change double and triple click bindings so that only one is fired (previously double click was fired on the way to triple click). Also add default double - and triple click binding to copy the word or line under the cursor and change - the existing bindings in copy mode to do the same. + and triple click bindings to copy the word or line under the cursor and + change the existing bindings in copy mode to do the same. * Add a default binding for button 2 to paste. * Add -d flag to run-shell to delay before running the command and allow it to run without a command so it just delays. -* Add C-g to cancel command prompt with vi(1) keys as well as emacs, and q in +* Add C-g to cancel command prompt with vi keys as well as emacs, and q in command mode. * When the server socket is given with -S, create it with umask 177 instead of From fb396286ff8330f35eae54f47b7f233839a23e6f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Mar 2020 18:08:39 +0000 Subject: [PATCH 0031/1006] Do not attempt to close a NULL pane when failing to create a new one. --- cmd-split-window.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd-split-window.c b/cmd-split-window.c index 748ae49b..e8c2050e 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -154,7 +154,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) sc.flags |= SPAWN_DETACHED; if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { - layout_close_pane(new_wp); cmdq_error(item, "create pane failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); From 115bb33257ece9eec1c890cc04683227678a895f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Mar 2020 11:10:12 +0000 Subject: [PATCH 0032/1006] Ignore default-shell (and use /bin/sh) if it invalid not just if it is tmux itself, also refuse to set the option to something invalid in the first place. GitHub issue 2120. --- cmd-set-option.c | 7 +++++++ server-client.c | 4 +++- spawn.c | 2 +- tmux.c | 6 +++--- tmux.h | 2 +- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 23b45230..2709dcdc 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -309,6 +309,13 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, old = xstrdup(options_get_string(oo, oe->name)); options_set_string(oo, oe->name, append, "%s", value); new = options_get_string(oo, oe->name); + if (strcmp(oe->name, "default-shell") == 0 && + !checkshell(new)) { + options_set_string(oo, oe->name, 0, "%s", old); + free(old); + cmdq_error(item, "not a suitable shell: %s", value); + return (-1); + } if (oe->pattern != NULL && fnmatch(oe->pattern, new, 0) != 0) { options_set_string(oo, oe->name, 0, "%s", old); free(old); diff --git a/server-client.c b/server-client.c index 02113386..f3a2b044 100644 --- a/server-client.c +++ b/server-client.c @@ -400,6 +400,8 @@ server_client_exec(struct client *c, const char *cmd) shell = options_get_string(s->options, "default-shell"); else shell = options_get_string(global_s_options, "default-shell"); + if (!checkshell(shell)) + shell = _PATH_BSHELL; shellsize = strlen(shell) + 1; msg = xmalloc(cmdsize + shellsize); @@ -2009,7 +2011,7 @@ server_client_dispatch_shell(struct client *c) const char *shell; shell = options_get_string(global_s_options, "default-shell"); - if (*shell == '\0' || areshell(shell)) + if (!checkshell(shell)) shell = _PATH_BSHELL; proc_send(c->peer, MSG_SHELL, -1, shell, strlen(shell) + 1); diff --git a/spawn.c b/spawn.c index 6bcea168..9d2ccdf1 100644 --- a/spawn.c +++ b/spawn.c @@ -320,7 +320,7 @@ spawn_pane(struct spawn_context *sc, char **cause) /* Then the shell. If respawning, use the old one. */ if (~sc->flags & SPAWN_RESPAWN) { tmp = options_get_string(s->options, "default-shell"); - if (*tmp == '\0' || areshell(tmp)) + if (!checkshell(tmp)) tmp = _PATH_BSHELL; free(new_wp->shell); new_wp->shell = xstrdup(tmp); diff --git a/tmux.c b/tmux.c index 572e705c..60b35e5d 100644 --- a/tmux.c +++ b/tmux.c @@ -49,8 +49,8 @@ const char *shell_command; static __dead void usage(void); static char *make_label(const char *, char **); +static int areshell(const char *); static const char *getshell(void); -static int checkshell(const char *); static __dead void usage(void) @@ -79,7 +79,7 @@ getshell(void) return (_PATH_BSHELL); } -static int +int checkshell(const char *shell) { if (shell == NULL || *shell != '/') @@ -91,7 +91,7 @@ checkshell(const char *shell) return (1); } -int +static int areshell(const char *shell) { const char *progname, *ptr; diff --git a/tmux.h b/tmux.h index 786cb0dd..72e0e9b9 100644 --- a/tmux.h +++ b/tmux.h @@ -1771,7 +1771,7 @@ extern const char *socket_path; extern const char *shell_command; extern int ptm_fd; extern const char *shell_command; -int areshell(const char *); +int checkshell(const char *); void setblocking(int, int); const char *find_cwd(void); const char *find_home(void); From 1ddc128860597bdded53a60c074596a4d39cc153 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Mar 2020 12:20:12 +0000 Subject: [PATCH 0033/1006] Do not return early if no bits changed because may still need to change the style. --- tty.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tty.c b/tty.c index ef66ebbb..862a1266 100644 --- a/tty.c +++ b/tty.c @@ -668,8 +668,6 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; - if (changed == 0) - return; log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); if (changed & MODE_BLINKING) { From bd0342b0a7ac4b5dc00ffc9cce7f9b0fbeb7ec91 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Mar 2020 16:02:38 +0000 Subject: [PATCH 0034/1006] getopt is not required to set optarg to NULL when there is no argument and some do not, so set it explicitly each time. --- arguments.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arguments.c b/arguments.c index acdaf8aa..cb527bcd 100644 --- a/arguments.c +++ b/arguments.c @@ -75,6 +75,7 @@ args_parse(const char *template, int argc, char **argv) optreset = 1; optind = 1; + optarg = NULL; while ((opt = getopt(argc, argv, template)) != -1) { if (opt < 0) @@ -84,6 +85,7 @@ args_parse(const char *template, int argc, char **argv) return (NULL); } args_set(args, opt, optarg); + optarg = NULL; } argc -= optind; argv += optind; From 80f20b8e4e552eaa6b5b96aa8745c5935406c1b0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 17 Mar 2020 16:06:30 +0000 Subject: [PATCH 0035/1006] getopt varies too much between platforms, and we already use compat/getopt.c for Linux so just use it everywhere. --- compat.h | 2 -- configure.ac | 40 ++++++---------------------------------- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/compat.h b/compat.h index 70801d0d..b7ec5a69 100644 --- a/compat.h +++ b/compat.h @@ -370,7 +370,6 @@ int utf8proc_mbtowc(wchar_t *, const char *, size_t); int utf8proc_wctomb(char *, wchar_t); #endif -#ifndef HAVE_GETOPT /* getopt.c */ extern int BSDopterr; extern int BSDoptind; @@ -384,6 +383,5 @@ int BSDgetopt(int, char *const *, const char *); #define optopt BSDoptopt #define optreset BSDoptreset #define optarg BSDoptarg -#endif #endif /* COMPAT_H */ diff --git a/configure.ac b/configure.ac index 74600233..e29ebdc7 100644 --- a/configure.ac +++ b/configure.ac @@ -128,6 +128,12 @@ AC_FUNC_STRNLEN # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) +# Always use our getopt because 1) glibc's doesn't enforce argument order 2) +# musl does not set optarg to NULL for flags without arguments (although it is +# not required to, but it is helpful) 3) there are probably other weird +# implementations. +AC_LIBOBJ(getopt) + # Look for libevent. PKG_CHECK_MODULES( LIBEVENT, @@ -427,40 +433,6 @@ else AC_LIBOBJ(unvis) fi -# Look for getopt. glibc's getopt does not enforce argument order and the ways -# of making it do so are stupid, so just use our own instead. -AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no) -if test "x$found_getopt" != xno; then - AC_MSG_CHECKING(if getopt is suitable) - AC_EGREP_CPP( - yes, - [ - #include - #ifdef __GLIBC__ - yes - #endif - ], - [ - found_getopt=no - AC_MSG_RESULT(no) - ], - AC_MSG_RESULT(yes)) -fi -if test "x$found_getopt" != xno; then - AC_CHECK_DECLS( - [optarg, optind, optreset], - , - found_getopt=no, - [ - #include - ]) -fi -if test "x$found_getopt" != xno; then - AC_DEFINE(HAVE_GETOPT) -else - AC_LIBOBJ(getopt) -fi - # Look for fdforkpty and forkpty in libutil. AC_SEARCH_LIBS(fdforkpty, util, found_fdforkpty=yes, found_fdforkpty=no) if test "x$found_fdforkpty" = xyes; then From b21a9b1c4eb175938b07855e670a39302726a03d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 17 Mar 2020 16:06:30 +0000 Subject: [PATCH 0036/1006] getopt varies too much between platforms, and we already use compat/getopt.c for Linux so just use it everywhere. --- compat.h | 2 -- configure.ac | 40 ++++++---------------------------------- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/compat.h b/compat.h index 70801d0d..b7ec5a69 100644 --- a/compat.h +++ b/compat.h @@ -370,7 +370,6 @@ int utf8proc_mbtowc(wchar_t *, const char *, size_t); int utf8proc_wctomb(char *, wchar_t); #endif -#ifndef HAVE_GETOPT /* getopt.c */ extern int BSDopterr; extern int BSDoptind; @@ -384,6 +383,5 @@ int BSDgetopt(int, char *const *, const char *); #define optopt BSDoptopt #define optreset BSDoptreset #define optarg BSDoptarg -#endif #endif /* COMPAT_H */ diff --git a/configure.ac b/configure.ac index d98b72f2..947a0a2f 100644 --- a/configure.ac +++ b/configure.ac @@ -125,6 +125,12 @@ AC_FUNC_STRNLEN # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) +# Always use our getopt because 1) glibc's doesn't enforce argument order 2) +# musl does not set optarg to NULL for flags without arguments (although it is +# not required to, but it is helpful) 3) there are probably other weird +# implementations. +AC_LIBOBJ(getopt) + # Look for libevent. PKG_CHECK_MODULES( LIBEVENT, @@ -424,40 +430,6 @@ else AC_LIBOBJ(unvis) fi -# Look for getopt. glibc's getopt does not enforce argument order and the ways -# of making it do so are stupid, so just use our own instead. -AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no) -if test "x$found_getopt" != xno; then - AC_MSG_CHECKING(if getopt is suitable) - AC_EGREP_CPP( - yes, - [ - #include - #ifdef __GLIBC__ - yes - #endif - ], - [ - found_getopt=no - AC_MSG_RESULT(no) - ], - AC_MSG_RESULT(yes)) -fi -if test "x$found_getopt" != xno; then - AC_CHECK_DECLS( - [optarg, optind, optreset], - , - found_getopt=no, - [ - #include - ]) -fi -if test "x$found_getopt" != xno; then - AC_DEFINE(HAVE_GETOPT) -else - AC_LIBOBJ(getopt) -fi - # Look for fdforkpty and forkpty in libutil. AC_SEARCH_LIBS(fdforkpty, util, found_fdforkpty=yes, found_fdforkpty=no) if test "x$found_fdforkpty" = xyes; then From c0d74661b7a176340a3ceaa77622d6c3747a2984 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Mar 2020 18:08:39 +0000 Subject: [PATCH 0037/1006] Do not attempt to close a NULL pane when failing to create a new one. --- cmd-split-window.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd-split-window.c b/cmd-split-window.c index eaf1f74c..a5fa3acc 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -153,7 +153,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) sc.flags |= SPAWN_DETACHED; if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { - layout_close_pane(new_wp); cmdq_error(item, "create pane failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); From 4ffbebedce48d1758e082a82cbe9292fee358992 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Mar 2020 09:12:44 +0000 Subject: [PATCH 0038/1006] Terminate the output buffer for control mode output - it is now used as a string. GitHub issue 2114. --- control-notify.c | 1 + 1 file changed, 1 insertion(+) diff --git a/control-notify.c b/control-notify.c index babfcf2d..a513c147 100644 --- a/control-notify.c +++ b/control-notify.c @@ -54,6 +54,7 @@ control_notify_input(struct client *c, struct window_pane *wp, else evbuffer_add_printf(message, "%c", buf[i]); } + evbuffer_add(message, "", 1); control_write(c, "%s", EVBUFFER_DATA(message)); evbuffer_free(message); } From f16085a362129f3b6801005fdff038d6845f4613 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 15 Mar 2020 20:35:52 +0000 Subject: [PATCH 0039/1006] Fix C-Space key string. --- key-string.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/key-string.c b/key-string.c index d2b31e03..38e5b8a7 100644 --- a/key-string.c +++ b/key-string.c @@ -257,6 +257,10 @@ key_string_lookup_key(key_code key) return (out); } + /* Display C-@ as C-Space. */ + if ((key & KEYC_MASK_KEY) == 0) + key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); + /* Fill in the modifiers. */ if (key & KEYC_CTRL) strlcat(out, "C-", sizeof out); @@ -329,15 +333,6 @@ key_string_lookup_key(key_code key) return (out); } - /* - * Special case: display C-@ as C-Space. Could do this below in - * the (key >= 0 && key <= 32), but this way we let it be found - * in key_string_table, for the unlikely chance that we might - * change its name. - */ - if ((key & KEYC_MASK_KEY) == 0) - key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); - /* Try the key against the string table. */ for (i = 0; i < nitems(key_string_table); i++) { if (key == key_string_table[i].key) From 617136c234f1a39c06e0ec4eed5c3ebea02cc85d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Mar 2020 09:18:47 +0000 Subject: [PATCH 0040/1006] Turn off mouse mode 1003 as well as the rest when exiting. --- tty.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tty.c b/tty.c index 3bab556d..8efe57b5 100644 --- a/tty.c +++ b/tty.c @@ -330,8 +330,10 @@ tty_start_tty(struct tty *tty) log_debug("%s: using UTF-8 for ACS", c->name); tty_putcode(tty, TTYC_CNORM); - if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); + if (tty_term_has(tty->term, TTYC_KMOUS)) { + tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l"); + tty_puts(tty, "\033[?1006l\033[?1005l"); + } if (tty_term_flag(tty->term, TTYC_XT)) { if (options_get_number(global_options, "focus-events")) { @@ -404,8 +406,10 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); - if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); + if (tty_term_has(tty->term, TTYC_KMOUS)) { + tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l"); + tty_raw(tty, "\033[?1006l\033[?1005l"); + } if (tty_term_flag(tty->term, TTYC_XT)) { if (tty->flags & TTY_FOCUS) { From 0c06409c9d75d6f4560778113db834ae6e03591c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Mar 2020 16:02:38 +0000 Subject: [PATCH 0041/1006] getopt is not required to set optarg to NULL when there is no argument and some do not, so set it explicitly each time. --- arguments.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arguments.c b/arguments.c index 026272af..e2d18980 100644 --- a/arguments.c +++ b/arguments.c @@ -74,6 +74,7 @@ args_parse(const char *template, int argc, char **argv) optreset = 1; optind = 1; + optarg = NULL; while ((opt = getopt(argc, argv, template)) != -1) { if (opt < 0) @@ -83,6 +84,7 @@ args_parse(const char *template, int argc, char **argv) return (NULL); } args_set(args, opt, optarg); + optarg = NULL; } argc -= optind; argv += optind; From af4b62d10bf798088a97494e8963f209d1c03e36 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Mar 2020 07:55:33 +0000 Subject: [PATCH 0042/1006] 3.1-rc3. --- CHANGES | 2 ++ configure.ac | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 728bac60..d70167d4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,7 @@ CHANGES FROM 3.0a TO 3.1 +* Turn off mouse mode 1003 as well as the rest when exiting. + * Add selection_active format for when the selection is present but not moving with the cursor. diff --git a/configure.ac b/configure.ac index 947a0a2f..78fbc8c9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.1-rc2) +AC_INIT([tmux], 3.1-rc3) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 7b0e688a9695cbbe5588502a31758c7c5caceb2e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Mar 2020 09:13:49 +0000 Subject: [PATCH 0043/1006] Break position calculation into a helper function. --- cmd-display-menu.c | 149 ++++++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 69 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index ac7a4cfe..302b9f81 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -44,6 +44,81 @@ const struct cmd_entry cmd_display_menu_entry = { .exec = cmd_display_menu_exec }; +static void +cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, + struct args *args, u_int *px, u_int *py, u_int w, u_int h) +{ + struct winlink *wl = item->target.wl; + struct window_pane *wp = item->target.wp; + struct style_range *sr; + const char *xp, *yp; + int at = status_at_line(c); + u_int ox, oy, sx, sy; + + xp = args_get(args, 'x'); + if (xp == NULL) + *px = 0; + else if (strcmp(xp, "R") == 0) + *px = c->tty.sx - 1; + else if (strcmp(xp, "P") == 0) { + tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); + if (wp->xoff >= ox) + *px = wp->xoff - ox; + else + *px = 0; + } else if (strcmp(xp, "M") == 0 && item->shared->mouse.valid) { + if (item->shared->mouse.x > w / 2) + *px = item->shared->mouse.x - w / 2; + else + *px = 0; + } else if (strcmp(xp, "W") == 0) { + if (at == -1) + *px = 0; + else { + TAILQ_FOREACH(sr, &c->status.entries[0].ranges, entry) { + if (sr->type != STYLE_RANGE_WINDOW) + continue; + if (sr->argument == (u_int)wl->idx) + break; + } + if (sr != NULL) + *px = sr->start; + else + *px = 0; + } + } else + *px = strtoul(xp, NULL, 10); + if ((*px) + w >= c->tty.sx) + *px = c->tty.sx - w; + + yp = args_get(args, 'y'); + if (yp == NULL) + *py = 0; + else if (strcmp(yp, "P") == 0) { + tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); + if (wp->yoff + wp->sy >= oy) + *py = wp->yoff + wp->sy - oy; + else + *py = 0; + } else if (strcmp(yp, "M") == 0 && item->shared->mouse.valid) + *py = item->shared->mouse.y + h; + else if (strcmp(yp, "S") == 0) { + if (at == -1) + *py = c->tty.sy; + else if (at == 0) + *py = status_line_size(c) + h; + else + *py = at; + } else + *py = strtoul(yp, NULL, 10); + if (*py < h) + *py = 0; + else + *py -= h; + if ((*py) + h >= c->tty.sy) + *py = c->tty.sy - h; +} + static enum cmd_retval cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) { @@ -54,18 +129,16 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *wp = item->target.wp; struct cmd_find_state *fs = &item->target; struct menu *menu = NULL; - struct style_range *sr; struct menu_item menu_item; - const char *xp, *yp, *key; + const char *key; char *title, *name; - int at, flags, i; - u_int px, py, ox, oy, sx, sy; + int flags, i; + u_int px, py; if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); if (c->overlay_draw != NULL) return (CMD_RETURN_NORMAL); - at = status_at_line(c); if (args_has(args, 'T')) title = format_single(NULL, args_get(args, 'T'), c, s, wl, wp); @@ -104,70 +177,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) menu_free(menu); return (CMD_RETURN_NORMAL); } - - xp = args_get(args, 'x'); - if (xp == NULL) - px = 0; - else if (strcmp(xp, "R") == 0) - px = c->tty.sx - 1; - else if (strcmp(xp, "P") == 0) { - tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); - if (wp->xoff >= ox) - px = wp->xoff - ox; - else - px = 0; - } else if (strcmp(xp, "M") == 0 && item->shared->mouse.valid) { - if (item->shared->mouse.x > (menu->width + 4) / 2) - px = item->shared->mouse.x - (menu->width + 4) / 2; - else - px = 0; - } - else if (strcmp(xp, "W") == 0) { - if (at == -1) - px = 0; - else { - TAILQ_FOREACH(sr, &c->status.entries[0].ranges, entry) { - if (sr->type != STYLE_RANGE_WINDOW) - continue; - if (sr->argument == (u_int)wl->idx) - break; - } - if (sr != NULL) - px = sr->start; - else - px = 0; - } - } else - px = strtoul(xp, NULL, 10); - if (px + menu->width + 4 >= c->tty.sx) - px = c->tty.sx - menu->width - 4; - - yp = args_get(args, 'y'); - if (yp == NULL) - py = 0; - else if (strcmp(yp, "P") == 0) { - tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); - if (wp->yoff + wp->sy >= oy) - py = wp->yoff + wp->sy - oy; - else - py = 0; - } else if (strcmp(yp, "M") == 0 && item->shared->mouse.valid) - py = item->shared->mouse.y + menu->count + 2; - else if (strcmp(yp, "S") == 0) { - if (at == -1) - py = c->tty.sy; - else if (at == 0) - py = status_line_size(c) + menu->count + 2; - else - py = at; - } else - py = strtoul(yp, NULL, 10); - if (py < menu->count + 2) - py = 0; - else - py -= menu->count + 2; - if (py + menu->count + 2 >= c->tty.sy) - py = c->tty.sy - menu->count - 2; + cmd_display_menu_get_position(c, item, args, &px, &py, menu->width + 4, + menu->count + 2); flags = 0; if (!item->shared->mouse.valid) From 2cd8ea76800e65d787c42425739fec837a90c0cc Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Mar 2020 13:28:52 +0000 Subject: [PATCH 0044/1006] Various fixes to copying with select-word and select-line, including making it consistent with keys and with the mouse, and using other-end. From Anindya Mukherjee. --- window-copy.c | 109 +++++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/window-copy.c b/window-copy.c index 6864d8f9..056eac65 100644 --- a/window-copy.c +++ b/window-copy.c @@ -87,9 +87,10 @@ static void window_copy_update_cursor(struct window_mode_entry *, u_int, static void window_copy_start_selection(struct window_mode_entry *); static int window_copy_adjust_selection(struct window_mode_entry *, u_int *, u_int *); -static int window_copy_set_selection(struct window_mode_entry *, int); -static int window_copy_update_selection(struct window_mode_entry *, int); -static void window_copy_synchronize_cursor(struct window_mode_entry *); +static int window_copy_set_selection(struct window_mode_entry *, int, int); +static int window_copy_update_selection(struct window_mode_entry *, int, + int); +static void window_copy_synchronize_cursor(struct window_mode_entry *, int); static void *window_copy_get_selection(struct window_mode_entry *, size_t *); static void window_copy_copy_buffer(struct window_mode_entry *, const char *, void *, size_t); @@ -505,7 +506,7 @@ window_copy_pageup1(struct window_mode_entry *wme, int half_page) window_copy_cursor_end_of_line(wme); } - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -553,7 +554,7 @@ window_copy_pagedown(struct window_mode_entry *wme, int half_page, if (scroll_exit && data->oy == 0) return (1); - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); return (0); } @@ -718,9 +719,6 @@ window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) struct client *c = cs->c; struct mouse_event *m = cs->m; struct window_copy_mode_data *data = wme->data; - struct options *oo = cs->s->options; - - data->ws = options_get_string(oo, "word-separators"); if (m != NULL) { window_copy_start_drag(c, m); @@ -728,6 +726,7 @@ window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) } data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; window_copy_start_selection(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -753,7 +752,7 @@ window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs) data->cx = 0; data->cy = screen_size_y(&data->screen) - 1; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -810,12 +809,14 @@ window_copy_cmd_copy_line(struct window_copy_cmd_state *cs) struct session *s = cs->s; struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; char *prefix = NULL; if (cs->args->argc == 2) prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + data->selflag = SEL_CHAR; window_copy_cursor_start_of_line(wme); window_copy_start_selection(wme); for (; np > 1; np--) @@ -995,7 +996,7 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) data->cx = window_copy_find_length(wme, data->cy); data->oy = 0; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1014,7 +1015,7 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs) data->cx = 0; data->oy = screen_hsize(data->backing); - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1083,7 +1084,7 @@ window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) data->cx = 0; data->cy = (screen_size_y(&data->screen) - 1) / 2; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1557,7 +1558,6 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct session *s = cs->s; struct window_copy_mode_data *data = wme->data; - const char *ws; u_int px, py; data->lineflag = LINE_SEL_LEFT_RIGHT; @@ -1569,18 +1569,18 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) px = data->cx; py = screen_hsize(data->backing) + data->cy - data->oy; - ws = options_get_string(s->options, "word-separators"); - window_copy_cursor_previous_word(wme, ws, 0); + data->ws = options_get_string(s->options, "word-separators"); + window_copy_cursor_previous_word(wme, data->ws, 0); data->selrx = data->cx; data->selry = screen_hsize(data->backing) + data->cy - data->oy; window_copy_start_selection(wme); if (px >= window_copy_find_length(wme, py) || - !window_copy_in_set(wme, px + 1, py, ws)) - window_copy_cursor_next_word_end(wme, ws); + !window_copy_in_set(wme, px + 1, py, data->ws)) + window_copy_cursor_next_word_end(wme, data->ws); else { window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } data->endselrx = data->cx; @@ -1607,7 +1607,7 @@ window_copy_cmd_top_line(struct window_copy_cmd_state *cs) data->cx = 0; data->cy = 0; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -2121,7 +2121,7 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py) data->oy = gd->hsize - offset; } - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -2759,7 +2759,7 @@ window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) lineno = screen_hsize(data->backing); data->oy = lineno; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -2877,7 +2877,8 @@ window_copy_redraw_screen(struct window_mode_entry *wme) } static void -window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin) +window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, + int no_reset) { struct window_copy_mode_data *data = wme->data; u_int xx, yy; @@ -2885,10 +2886,10 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin) yy = screen_hsize(data->backing) + data->cy - data->oy; switch (data->selflag) { case SEL_WORD: - begin = 0; xx = data->cx; - if (data->ws == NULL) + if (no_reset) break; + begin = 0; if (data->dy > yy || (data->dy == yy && data->dx > xx)) { /* Right to left selection. */ window_copy_cursor_previous_word_pos(wme, data->ws, 0, @@ -2911,6 +2912,10 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin) } break; case SEL_LINE: + if (no_reset) { + xx = data->cx; + break; + } begin = 0; if (data->dy > yy) { /* Right to left selection. */ @@ -2943,16 +2948,16 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin) } static void -window_copy_synchronize_cursor(struct window_mode_entry *wme) +window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset) { struct window_copy_mode_data *data = wme->data; switch (data->cursordrag) { case CURSORDRAG_ENDSEL: - window_copy_synchronize_cursor_end(wme, 0); + window_copy_synchronize_cursor_end(wme, 0, no_reset); break; case CURSORDRAG_SEL: - window_copy_synchronize_cursor_end(wme, 1); + window_copy_synchronize_cursor_end(wme, 1, no_reset); break; case CURSORDRAG_NONE: break; @@ -2994,7 +2999,7 @@ window_copy_start_selection(struct window_mode_entry *wme) data->cursordrag = CURSORDRAG_ENDSEL; - window_copy_set_selection(wme, 1); + window_copy_set_selection(wme, 1, 0); } static int @@ -3031,18 +3036,20 @@ window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx, } static int -window_copy_update_selection(struct window_mode_entry *wme, int may_redraw) +window_copy_update_selection(struct window_mode_entry *wme, int may_redraw, + int no_reset) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) return (0); - return (window_copy_set_selection(wme, may_redraw)); + return (window_copy_set_selection(wme, may_redraw, no_reset)); } static int -window_copy_set_selection(struct window_mode_entry *wme, int may_redraw) +window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, + int no_reset) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -3052,7 +3059,7 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw) u_int sx, sy, cy, endsx, endsy; int startrelpos, endrelpos; - window_copy_synchronize_cursor(wme); + window_copy_synchronize_cursor(wme, no_reset); /* Adjust the selection. */ sx = data->selx; @@ -3408,7 +3415,7 @@ window_copy_cursor_start_of_line(struct window_mode_entry *wme) } } window_copy_update_cursor(wme, 0, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -3431,7 +3438,7 @@ window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) } window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -3464,7 +3471,7 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme) } window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -3515,7 +3522,7 @@ window_copy_other_end(struct window_mode_entry *wme) } else data->cy = cy + sely - yy; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 1); window_copy_redraw_screen(wme); } @@ -3539,7 +3546,7 @@ window_copy_cursor_left(struct window_mode_entry *wme) window_copy_cursor_end_of_line(wme); } else if (cx > 0) { window_copy_update_cursor(wme, cx - 1, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } } @@ -3571,7 +3578,7 @@ window_copy_cursor_right(struct window_mode_entry *wme) cx++; } window_copy_update_cursor(wme, cx, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } } @@ -3604,7 +3611,7 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) } } else { window_copy_update_cursor(wme, data->lastcx, data->cy - 1); - if (window_copy_update_selection(wme, 1)) { + if (window_copy_update_selection(wme, 1, 0)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wme, data->cy, 1); else @@ -3650,7 +3657,7 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) window_copy_redraw_lines(wme, data->cy - 1, 2); } else { window_copy_update_cursor(wme, data->lastcx, data->cy + 1); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy - 1, 2); } @@ -3685,7 +3692,7 @@ window_copy_cursor_jump(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3712,7 +3719,7 @@ window_copy_cursor_jump_back(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3739,7 +3746,7 @@ window_copy_cursor_jump_to(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px - 1, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3769,7 +3776,7 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px + 1, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3818,7 +3825,7 @@ window_copy_cursor_next_word(struct window_mode_entry *wme, } while (expected == 1); window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -3919,7 +3926,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, px--; window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -4016,7 +4023,7 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, out: window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -4034,7 +4041,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) return; data->oy -= ny; - window_copy_update_selection(wme, 0); + window_copy_update_selection(wme, 0, 0); screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); @@ -4068,7 +4075,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) return; data->oy += ny; - window_copy_update_selection(wme, 0); + window_copy_update_selection(wme, 0, 0); screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); @@ -4095,7 +4102,7 @@ window_copy_rectangle_toggle(struct window_mode_entry *wme) if (data->cx > px) window_copy_update_cursor(wme, px, data->cy); - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -4206,7 +4213,7 @@ window_copy_drag_update(struct client *c, struct mouse_event *m) old_cy = data->cy; window_copy_update_cursor(wme, x, y); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_selection(wme, old_cy); if (old_cy != data->cy || old_cx == data->cx) { if (y == 0) { From 581ed718e737fd3875323e74b674bcf7f9d0bcb3 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Mar 2020 13:32:49 +0000 Subject: [PATCH 0045/1006] Add C position for terminal centre with display-menu -x and -y. --- cmd-display-menu.c | 7 +++++-- tmux.1 | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 302b9f81..2ba674a3 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -60,6 +60,8 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, *px = 0; else if (strcmp(xp, "R") == 0) *px = c->tty.sx - 1; + else if (strcmp(xp, "C") == 0) + *px = (c->tty.sx - 1) / 2 - w / 2; else if (strcmp(xp, "P") == 0) { tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); if (wp->xoff >= ox) @@ -94,6 +96,8 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, yp = args_get(args, 'y'); if (yp == NULL) *py = 0; + else if (strcmp(yp, "C") == 0) + *py = (c->tty.sy - 1) / 2 + h / 2; else if (strcmp(yp, "P") == 0) { tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); if (wp->yoff + wp->sy >= oy) @@ -132,7 +136,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) struct menu_item menu_item; const char *key; char *title, *name; - int flags, i; + int flags = 0, i; u_int px, py; if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) @@ -180,7 +184,6 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) cmd_display_menu_get_position(c, item, args, &px, &py, menu->width + 4, menu->count + 2); - flags = 0; if (!item->shared->mouse.valid) flags |= MENU_NOMOUSE; if (menu_display(menu, flags, item, px, py, c, fs, NULL, NULL) != 0) diff --git a/tmux.1 b/tmux.1 index 7ed6b334..dff28144 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4918,6 +4918,7 @@ give the position of the menu. Both may be a row or column number, or one of the following special values: .Bl -column "XXXXX" "XXXX" -offset indent .It Sy "Value" Ta Sy "Flag" Ta Sy "Meaning" +.It Li "C" Ta "Both" Ta "The centre of the terminal" .It Li "R" Ta Fl x Ta "The right side of the terminal" .It Li "P" Ta "Both" Ta "The bottom left of the pane" .It Li "M" Ta "Both" Ta "The mouse position" From e8273a993ec79a58ffff16eba17d0551c690c4db Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Mar 2020 13:43:18 +0000 Subject: [PATCH 0046/1006] Add a flag to run a background process in a pty as well, not used for anything yet. --- cmd-if-shell.c | 3 +- cmd-run-shell.c | 4 +-- format.c | 2 +- job.c | 80 ++++++++++++++++++++++++++++++------------------- tmux.h | 5 +++- window-copy.c | 4 +-- 6 files changed, 61 insertions(+), 37 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 2befbc0c..b008241d 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -144,7 +144,8 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cmd_find_copy_state(&cdata->input.fs, fs); if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, - cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) { + cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1, + -1) == NULL) { cmdq_error(item, "failed to run command: %s", shellcmd); free(shellcmd); free(cdata); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index bc21cc9c..1cf97d51 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -145,8 +145,8 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) if (cdata->cmd != NULL) { if (job_run(cdata->cmd, cdata->s, cdata->cwd, NULL, - cmd_run_shell_callback, cmd_run_shell_free, cdata, - 0) == NULL) + cmd_run_shell_callback, cmd_run_shell_free, cdata, 0, -1, + -1) == NULL) cmd_run_shell_free(cdata); } else { if (cdata->item != NULL) diff --git a/format.c b/format.c index 14d56126..fe8e9c68 100644 --- a/format.c +++ b/format.c @@ -354,7 +354,7 @@ format_job_get(struct format_tree *ft, const char *cmd) if (force || (fj->job == NULL && fj->last != t)) { fj->job = job_run(expanded, NULL, server_client_get_cwd(ft->client, NULL), format_job_update, - format_job_complete, NULL, fj, JOB_NOWAIT); + format_job_complete, NULL, fj, JOB_NOWAIT, -1, -1); if (fj->job == NULL) { free(fj->out); xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); diff --git a/job.c b/job.c index 0c316fd8..c31d51b2 100644 --- a/job.c +++ b/job.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "tmux.h" @@ -69,18 +70,15 @@ static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs); struct job * job_run(const char *cmd, struct session *s, const char *cwd, job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, - void *data, int flags) + void *data, int flags, int sx, int sy) { struct job *job; struct environ *env; pid_t pid; - int nullfd, out[2]; + int nullfd, out[2], master; const char *home; sigset_t set, oldset; - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) - return (NULL); - log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd); + struct winsize ws; /* * Do not set TERM during .tmux.conf, it is nice to be able to use @@ -90,13 +88,26 @@ job_run(const char *cmd, struct session *s, const char *cwd, sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); - switch (pid = fork()) { + + if (flags & JOB_PTY) { + memset(&ws, 0, sizeof ws); + ws.ws_col = sx; + ws.ws_row = sy; + pid = fdforkpty(ptm_fd, &master, NULL, NULL, &ws); + } else { + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) + goto fail; + pid = fork(); + } + log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd); + + switch (pid) { case -1: - sigprocmask(SIG_SETMASK, &oldset, NULL); - environ_free(env); - close(out[0]); - close(out[1]); - return (NULL); + if (~flags & JOB_PTY) { + close(out[0]); + close(out[1]); + } + goto fail; case 0: proc_clear_signals(server_proc, 1); sigprocmask(SIG_SETMASK, &oldset, NULL); @@ -109,22 +120,23 @@ job_run(const char *cmd, struct session *s, const char *cwd, environ_push(env); environ_free(env); - if (dup2(out[1], STDIN_FILENO) == -1) - fatal("dup2 failed"); - if (dup2(out[1], STDOUT_FILENO) == -1) - fatal("dup2 failed"); - if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) - close(out[1]); - close(out[0]); - - nullfd = open(_PATH_DEVNULL, O_RDWR, 0); - if (nullfd == -1) - fatal("open failed"); - if (dup2(nullfd, STDERR_FILENO) == -1) - fatal("dup2 failed"); - if (nullfd != STDERR_FILENO) - close(nullfd); + if (~flags & JOB_PTY) { + if (dup2(out[1], STDIN_FILENO) == -1) + fatal("dup2 failed"); + if (dup2(out[1], STDOUT_FILENO) == -1) + fatal("dup2 failed"); + if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) + close(out[1]); + close(out[0]); + nullfd = open(_PATH_DEVNULL, O_RDWR, 0); + if (nullfd == -1) + fatal("open failed"); + if (dup2(nullfd, STDERR_FILENO) == -1) + fatal("dup2 failed"); + if (nullfd != STDERR_FILENO) + close(nullfd); + } closefrom(STDERR_FILENO + 1); execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); @@ -133,7 +145,6 @@ job_run(const char *cmd, struct session *s, const char *cwd, sigprocmask(SIG_SETMASK, &oldset, NULL); environ_free(env); - close(out[1]); job = xmalloc(sizeof *job); job->state = JOB_RUNNING; @@ -150,7 +161,11 @@ job_run(const char *cmd, struct session *s, const char *cwd, job->freecb = freecb; job->data = data; - job->fd = out[0]; + if (~flags & JOB_PTY) { + close(out[1]); + job->fd = out[0]; + } else + job->fd = master; setblocking(job->fd, 0); job->event = bufferevent_new(job->fd, job_read_callback, @@ -161,6 +176,11 @@ job_run(const char *cmd, struct session *s, const char *cwd, log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); return (job); + +fail: + sigprocmask(SIG_SETMASK, &oldset, NULL); + environ_free(env); + return (NULL); } /* Kill and free an individual job. */ @@ -209,7 +229,7 @@ job_write_callback(__unused struct bufferevent *bufev, void *data) log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, (long) job->pid, len); - if (len == 0) { + if (len == 0 && (~job->flags & JOB_KEEPWRITE)) { shutdown(job->fd, SHUT_WR); bufferevent_disable(job->event, EV_WRITE); } diff --git a/tmux.h b/tmux.h index 72e0e9b9..043cb070 100644 --- a/tmux.h +++ b/tmux.h @@ -1929,8 +1929,11 @@ typedef void (*job_update_cb) (struct job *); typedef void (*job_complete_cb) (struct job *); typedef void (*job_free_cb) (void *); #define JOB_NOWAIT 0x1 +#define JOB_KEEPWRITE 0x2 +#define JOB_PTY 0x4 struct job *job_run(const char *, struct session *, const char *, - job_update_cb, job_complete_cb, job_free_cb, void *, int); + job_update_cb, job_complete_cb, job_free_cb, void *, int, + int, int); void job_free(struct job *); void job_check_died(pid_t, int); int job_get_status(struct job *); diff --git a/window-copy.c b/window-copy.c index 056eac65..abde1c48 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3240,7 +3240,7 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, static void window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, - const char *prefix, const char *command) + const char *prefix, const char *cmd) { void *buf; size_t len; @@ -3250,7 +3250,7 @@ window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, if (buf == NULL) return; - job = job_run(command, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); + job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT, -1, -1); bufferevent_write(job_get_event(job), buf, len); window_copy_copy_buffer(wme, prefix, buf, len); } From ce61bf931b1af18064ab84542ee8f4a64e498fed Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Mar 2020 13:46:10 +0000 Subject: [PATCH 0047/1006] Do not set the history flag if there is no history. --- grid.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index b2031045..9e18a73d 100644 --- a/grid.c +++ b/grid.c @@ -258,7 +258,10 @@ grid_create(u_int sx, u_int sy, u_int hlimit) gd->sx = sx; gd->sy = sy; - gd->flags = GRID_HISTORY; + if (hlimit != 0) + gd->flags = GRID_HISTORY; + else + gd->flags = 0; gd->hscrolled = 0; gd->hsize = 0; From de34436d4c2d4ba72f37b80ed3804d057b95c0dc Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Mar 2020 14:03:48 +0000 Subject: [PATCH 0048/1006] Change input path so it doesn't require a pane. --- cmd-capture-pane.c | 2 +- cmd-send-keys.c | 2 +- input-keys.c | 52 +++++++----- input.c | 200 ++++++++++++++++++++++++++++----------------- spawn.c | 3 +- tmux.h | 19 +++-- window.c | 10 +-- 7 files changed, 175 insertions(+), 113 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 18be3f77..2c8dd920 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -80,7 +80,7 @@ cmd_capture_pane_pending(struct args *args, struct window_pane *wp, size_t linelen; u_int i; - pending = input_pending(wp); + pending = input_pending(wp->ictx); if (pending == NULL) return (xstrdup("")); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index cc04a73f..5770572a 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -192,7 +192,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'R')) { window_pane_reset_palette(wp); - input_reset(wp, 1); + input_reset(wp->ictx, 1); } for (; np != 0; np--) { diff --git a/input-keys.c b/input-keys.c index d09bf8fb..9d4043ef 100644 --- a/input-keys.c +++ b/input-keys.c @@ -149,9 +149,25 @@ input_split2(u_int c, u_char *dst) return (1); } +/* Translate a key code into an output key sequence for a pane. */ +int +input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) +{ + log_debug("writing key 0x%llx (%s) to %%%u", key, + key_string_lookup_key(key), wp->id); + + if (KEYC_IS_MOUSE(key)) { + if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) + input_key_mouse(wp, m); + return (0); + } + return (input_key(wp, wp->screen, wp->event, key)); +} + /* Translate a key code into an output key sequence. */ int -input_key(struct window_pane *wp, key_code key, struct mouse_event *m) +input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, + key_code key) { const struct input_key_ent *ike; u_int i; @@ -160,20 +176,14 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) key_code justkey, newkey; struct utf8_data ud; - log_debug("writing key 0x%llx (%s) to %%%u", key, - key_string_lookup_key(key), wp->id); - - /* If this is a mouse key, pass off to mouse function. */ - if (KEYC_IS_MOUSE(key)) { - if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) - input_key_mouse(wp, m); + /* Mouse keys need a pane. */ + if (KEYC_IS_MOUSE(key)) return (0); - } /* Literal keys go as themselves (can't be more than eight bits). */ if (key & KEYC_LITERAL) { ud.data[0] = (u_char)key; - bufferevent_write(wp->event, &ud.data[0], 1); + bufferevent_write(bev, &ud.data[0], 1); return (0); } @@ -192,17 +202,17 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) justkey = (key & ~(KEYC_XTERM|KEYC_ESCAPE)); if (justkey <= 0x7f) { if (key & KEYC_ESCAPE) - bufferevent_write(wp->event, "\033", 1); + bufferevent_write(bev, "\033", 1); ud.data[0] = justkey; - bufferevent_write(wp->event, &ud.data[0], 1); + bufferevent_write(bev, &ud.data[0], 1); return (0); } if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) return (-1); if (key & KEYC_ESCAPE) - bufferevent_write(wp->event, "\033", 1); - bufferevent_write(wp->event, ud.data, ud.size); + bufferevent_write(bev, "\033", 1); + bufferevent_write(bev, ud.data, ud.size); return (0); } @@ -210,9 +220,9 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) * Then try to look this up as an xterm key, if the flag to output them * is set. */ - if (options_get_number(wp->window->options, "xterm-keys")) { + if (wp == NULL || options_get_number(wp->window->options, "xterm-keys")) { if ((out = xterm_keys_lookup(key)) != NULL) { - bufferevent_write(wp->event, out, strlen(out)); + bufferevent_write(bev, out, strlen(out)); free(out); return (0); } @@ -223,11 +233,9 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) for (i = 0; i < nitems(input_keys); i++) { ike = &input_keys[i]; - if ((ike->flags & INPUTKEY_KEYPAD) && - !(wp->screen->mode & MODE_KKEYPAD)) + if ((ike->flags & INPUTKEY_KEYPAD) && (~s->mode & MODE_KKEYPAD)) continue; - if ((ike->flags & INPUTKEY_CURSOR) && - !(wp->screen->mode & MODE_KCURSOR)) + if ((ike->flags & INPUTKEY_CURSOR) && (~s->mode & MODE_KCURSOR)) continue; if ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key) @@ -244,8 +252,8 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) - bufferevent_write(wp->event, "\033", 1); - bufferevent_write(wp->event, ike->data, dlen); + bufferevent_write(bev, "\033", 1); + bufferevent_write(bev, ike->data, dlen); return (0); } diff --git a/input.c b/input.c index 82d2b398..c7d67b82 100644 --- a/input.c +++ b/input.c @@ -128,7 +128,7 @@ struct input_transition; static int input_split(struct input_ctx *); static int input_get(struct input_ctx *, u_int, int, int); static void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...); -static void input_set_state(struct window_pane *, +static void input_set_state(struct input_ctx *, const struct input_transition *); static void input_reset_cell(struct input_ctx *); @@ -731,10 +731,9 @@ static void input_timer_callback(__unused int fd, __unused short events, void *arg) { struct input_ctx *ictx = arg; - struct window_pane *wp = ictx->wp; - log_debug("%s: %%%u %s expired" , __func__, wp->id, ictx->state->name); - input_reset(wp, 0); + log_debug("%s: %s expired" , __func__, ictx->state->name); + input_reset(ictx, 0); } /* Start the timer. */ @@ -788,12 +787,13 @@ input_restore_state(struct input_ctx *ictx) } /* Initialise input parser. */ -void +struct input_ctx * input_init(struct window_pane *wp) { struct input_ctx *ictx; - ictx = wp->ictx = xcalloc(1, sizeof *ictx); + ictx = xcalloc(1, sizeof *ictx); + ictx->wp = wp; ictx->input_space = INPUT_BUF_START; ictx->input_buf = xmalloc(INPUT_BUF_START); @@ -804,15 +804,15 @@ input_init(struct window_pane *wp) evtimer_set(&ictx->timer, input_timer_callback, ictx); - input_reset(wp, 0); + input_reset(ictx, 0); + return (ictx); } /* Destroy input parser. */ void -input_free(struct window_pane *wp) +input_free(struct input_ctx *ictx) { - struct input_ctx *ictx = wp->ictx; - u_int i; + u_int i; for (i = 0; i < ictx->param_list_len; i++) { if (ictx->param_list[i].type == INPUT_STRING) @@ -825,19 +825,18 @@ input_free(struct window_pane *wp) evbuffer_free(ictx->since_ground); free(ictx); - wp->ictx = NULL; } /* Reset input state and clear screen. */ void -input_reset(struct window_pane *wp, int clear) +input_reset(struct input_ctx *ictx, int clear) { - struct input_ctx *ictx = wp->ictx; struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; input_reset_cell(ictx); - if (clear) { + if (clear && wp != NULL) { if (TAILQ_EMPTY(&wp->modes)) screen_write_start(sctx, wp, &wp->base); else @@ -856,17 +855,15 @@ input_reset(struct window_pane *wp, int clear) /* Return pending data. */ struct evbuffer * -input_pending(struct window_pane *wp) +input_pending(struct input_ctx *ictx) { - return (wp->ictx->since_ground); + return (ictx->since_ground); } /* Change input state. */ static void -input_set_state(struct window_pane *wp, const struct input_transition *itr) +input_set_state(struct input_ctx *ictx, const struct input_transition *itr) { - struct input_ctx *ictx = wp->ictx; - if (ictx->state->exit != NULL) ictx->state->exit(ictx); ictx->state = itr->state; @@ -874,46 +871,15 @@ input_set_state(struct window_pane *wp, const struct input_transition *itr) ictx->state->enter(ictx); } -/* Parse input. */ -void -input_parse(struct window_pane *wp) +/* Parse data. */ +static void +input_parse(struct input_ctx *ictx, u_char *buf, size_t len) { - struct evbuffer *evb = wp->event->input; - - input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb)); - evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); -} - -/* Parse given input. */ -void -input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) -{ - struct input_ctx *ictx = wp->ictx; struct screen_write_ctx *sctx = &ictx->ctx; const struct input_state *state = NULL; const struct input_transition *itr = NULL; size_t off = 0; - if (len == 0) - return; - - window_update_activity(wp->window); - wp->flags |= PANE_CHANGED; - notify_input(wp, buf, len); - - /* - * Open the screen. Use NULL wp if there is a mode set as don't want to - * update the tty. - */ - if (TAILQ_EMPTY(&wp->modes)) - screen_write_start(sctx, wp, &wp->base); - else - screen_write_start(sctx, NULL, &wp->base); - ictx->wp = wp; - - log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, - ictx->state->name, len, (int)len, buf); - /* Parse the input. */ while (off < len) { ictx->ch = buf[off++]; @@ -956,14 +922,63 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) /* And switch state, if necessary. */ if (itr->state != NULL) - input_set_state(wp, itr); + input_set_state(ictx, itr); /* If not in ground state, save input. */ if (ictx->state != &input_state_ground) evbuffer_add(ictx->since_ground, &ictx->ch, 1); } +} - /* Close the screen. */ +/* Parse input from pane. */ +void +input_parse_pane(struct window_pane *wp) +{ + struct evbuffer *evb = wp->event->input; + + input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb)); + evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); +} + +/* Parse given input. */ +void +input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) +{ + struct input_ctx *ictx = wp->ictx; + struct screen_write_ctx *sctx = &ictx->ctx; + + if (len == 0) + return; + + window_update_activity(wp->window); + wp->flags |= PANE_CHANGED; + notify_input(wp, buf, len); + + /* NULL wp if there is a mode set as don't want to update the tty. */ + if (TAILQ_EMPTY(&wp->modes)) + screen_write_start(sctx, wp, &wp->base); + else + screen_write_start(sctx, NULL, &wp->base); + + log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, + ictx->state->name, len, (int)len, buf); + + input_parse(ictx, buf, len); + screen_write_stop(sctx); +} + +/* Parse given input for screen. */ +void +input_parse_screen(struct input_ctx *ictx, struct screen *s, u_char *buf, + size_t len) +{ + struct screen_write_ctx *sctx = &ictx->ctx; + + if (len == 0) + return; + + screen_write_start(sctx, NULL, s); + input_parse(ictx, buf, len); screen_write_stop(sctx); } @@ -1043,14 +1058,18 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) static void input_reply(struct input_ctx *ictx, const char *fmt, ...) { - va_list ap; - char *reply; + struct window_pane *wp = ictx->wp; + va_list ap; + char *reply; + + if (wp == NULL) + return; va_start(ap, fmt); xvasprintf(&reply, fmt, ap); va_end(ap); - bufferevent_write(ictx->wp->event, reply, strlen(reply)); + bufferevent_write(wp->event, reply, strlen(reply)); free(reply); } @@ -1177,7 +1196,8 @@ input_c0_dispatch(struct input_ctx *ictx) case '\000': /* NUL */ break; case '\007': /* BEL */ - alerts_queue(wp->window, WINDOW_BELL); + if (wp != NULL) + alerts_queue(wp->window, WINDOW_BELL); break; case '\010': /* BS */ screen_write_backspace(sctx); @@ -1224,6 +1244,7 @@ static int input_esc_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; struct input_table_entry *entry; @@ -1240,7 +1261,8 @@ input_esc_dispatch(struct input_ctx *ictx) switch (entry->type) { case INPUT_ESC_RIS: - window_pane_reset_palette(ictx->wp); + if (wp != NULL) + window_pane_reset_palette(wp); input_reset_cell(ictx); screen_write_reset(sctx); break; @@ -1612,6 +1634,7 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; + struct grid_cell *gc = &ictx->cell.cell; u_int i; for (i = 0; i < ictx->param_list_len; i++) { @@ -1623,7 +1646,7 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) break; case 3: /* DECCOLM */ screen_write_cursormove(sctx, 0, 0, 1); - screen_write_clearscreen(sctx, ictx->cell.cell.bg); + screen_write_clearscreen(sctx, gc->bg); break; case 6: /* DECOM */ screen_write_mode_clear(sctx, MODE_ORIGIN); @@ -1655,10 +1678,12 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_off(wp, &ictx->cell.cell, 0); + if (wp != NULL) + window_pane_alternate_off(wp, gc, 0); break; case 1049: - window_pane_alternate_off(wp, &ictx->cell.cell, 1); + if (wp != NULL) + window_pane_alternate_off(wp, gc, 1); break; case 2004: screen_write_mode_clear(sctx, MODE_BRACKETPASTE); @@ -1700,6 +1725,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; + struct grid_cell *gc = &ictx->cell.cell; u_int i; for (i = 0; i < ictx->param_list_len; i++) { @@ -1742,7 +1768,8 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) if (sctx->s->mode & MODE_FOCUSON) break; screen_write_mode_set(sctx, MODE_FOCUSON); - wp->flags |= PANE_FOCUSPUSH; /* force update */ + if (wp != NULL) + wp->flags |= PANE_FOCUSPUSH; /* force update */ break; case 1005: screen_write_mode_set(sctx, MODE_MOUSE_UTF8); @@ -1752,10 +1779,12 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_on(wp, &ictx->cell.cell, 0); + if (wp != NULL) + window_pane_alternate_on(wp, gc, 0); break; case 1049: - window_pane_alternate_on(wp, &ictx->cell.cell, 1); + if (wp != NULL) + window_pane_alternate_on(wp, gc, 1); break; case 2004: screen_write_mode_set(sctx, MODE_BRACKETPASTE); @@ -1823,12 +1852,16 @@ input_csi_dispatch_winops(struct input_ctx *ictx) case 0: case 2: screen_pop_title(sctx->s); - server_status_window(ictx->wp->window); + if (wp != NULL) + server_status_window(wp->window); break; } break; case 18: - input_reply(ictx, "\033[8;%u;%ut", wp->sy, wp->sx); + if (wp != NULL) { + input_reply(ictx, "\033[8;%u;%ut", wp->sy, + wp->sx); + } break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -2193,6 +2226,7 @@ static void input_exit_osc(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; u_char *p = ictx->input_buf; u_int option; @@ -2213,7 +2247,7 @@ input_exit_osc(struct input_ctx *ictx) switch (option) { case 0: case 2: - if (screen_set_title(sctx->s, p)) + if (screen_set_title(sctx->s, p) && wp != NULL) server_status_window(ictx->wp->window); break; case 4: @@ -2222,7 +2256,8 @@ input_exit_osc(struct input_ctx *ictx) case 7: if (utf8_isvalid(p)) { screen_set_path(sctx->s, p); - server_status_window(ictx->wp->window); + if (wp != NULL) + server_status_window(wp->window); } break; case 10: @@ -2267,13 +2302,14 @@ static void input_exit_apc(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; if (ictx->flags & INPUT_DISCARD) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); - if (screen_set_title(sctx->s, ictx->input_buf)) - server_status_window(ictx->wp->window); + if (screen_set_title(sctx->s, ictx->input_buf) && wp != NULL) + server_status_window(wp->window); } /* Rename string started. */ @@ -2294,6 +2330,8 @@ input_exit_rename(struct input_ctx *ictx) struct window_pane *wp = ictx->wp; struct options_entry *oe; + if (wp == NULL) + return; if (ictx->flags & INPUT_DISCARD) return; if (!options_get_number(ictx->wp->options, "allow-rename")) @@ -2309,9 +2347,9 @@ input_exit_rename(struct input_ctx *ictx) options_remove(oe); return; } - window_set_name(ictx->wp->window, ictx->input_buf); - options_set_number(ictx->wp->window->options, "automatic-rename", 0); - server_status_window(ictx->wp->window); + window_set_name(wp->window, ictx->input_buf); + options_set_number(wp->window->options, "automatic-rename", 0); + server_status_window(wp->window); } /* Open UTF-8 character. */ @@ -2407,6 +2445,9 @@ input_osc_4(struct input_ctx *ictx, const char *p) long idx; u_int r, g, b; + if (wp == NULL) + return; + copy = s = xstrdup(p); while (s != NULL && *s != '\0') { idx = strtol(s, &next, 10); @@ -2441,6 +2482,8 @@ input_osc_10(struct input_ctx *ictx, const char *p) u_int r, g, b; char tmp[16]; + if (wp == NULL) + return; if (strcmp(p, "?") == 0) return; @@ -2465,6 +2508,8 @@ input_osc_11(struct input_ctx *ictx, const char *p) u_int r, g, b; char tmp[16]; + if (wp == NULL) + return; if (strcmp(p, "?") == 0) return; @@ -2494,6 +2539,8 @@ input_osc_52(struct input_ctx *ictx, const char *p) struct screen_write_ctx ctx; struct paste_buffer *pb; + if (wp == NULL) + return; state = options_get_number(global_options, "set-clipboard"); if (state != 2) return; @@ -2555,6 +2602,9 @@ input_osc_104(struct input_ctx *ictx, const char *p) char *copy, *s; long idx; + if (wp == NULL) + return; + if (*p == '\0') { window_pane_reset_palette(wp); return; diff --git a/spawn.c b/spawn.c index 9d2ccdf1..660ee47d 100644 --- a/spawn.c +++ b/spawn.c @@ -255,7 +255,8 @@ spawn_pane(struct spawn_context *sc, char **cause) } window_pane_reset_mode_all(sc->wp0); screen_reinit(&sc->wp0->base); - input_init(sc->wp0); + input_free(sc->wp0->ictx); + sc->wp0->ictx = input_init(sc->wp0); new_wp = sc->wp0; new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN); } else if (sc->lc == NULL) { diff --git a/tmux.h b/tmux.h index 043cb070..53372cfe 100644 --- a/tmux.h +++ b/tmux.h @@ -810,7 +810,6 @@ struct menu { u_int width; }; typedef void (*menu_choice_cb)(struct menu *, u_int, key_code, void *); -#define MENU_NOMOUSE 0x1 /* * Window mode. Windows can be in several modes and this is used to call the @@ -2288,15 +2287,19 @@ void recalculate_size(struct window *); void recalculate_sizes(void); /* input.c */ -void input_init(struct window_pane *); -void input_free(struct window_pane *); -void input_reset(struct window_pane *, int); -struct evbuffer *input_pending(struct window_pane *); -void input_parse(struct window_pane *); +struct input_ctx *input_init(struct window_pane *); +void input_free(struct input_ctx *); +void input_reset(struct input_ctx *, int); +struct evbuffer *input_pending(struct input_ctx *); +void input_parse_pane(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); +void input_parse_screen(struct input_ctx *, struct screen *, u_char *, + size_t); /* input-key.c */ -int input_key(struct window_pane *, key_code, struct mouse_event *); +int input_key_pane(struct window_pane *, key_code, struct mouse_event *); +int input_key(struct window_pane *, struct screen *, struct bufferevent *, + key_code); /* xterm-keys.c */ char *xterm_keys_lookup(key_code); @@ -2731,6 +2734,7 @@ __dead void printflike(1, 2) fatal(const char *, ...); __dead void printflike(1, 2) fatalx(const char *, ...); /* menu.c */ +#define MENU_NOMOUSE 0x1 struct menu *menu_create(const char *); void menu_add_items(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, @@ -2738,7 +2742,6 @@ void menu_add_items(struct menu *, const struct menu_item *, void menu_add_item(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); - void menu_free(struct menu *); int menu_display(struct menu *, int, struct cmdq_item *, u_int, u_int, struct client *, struct cmd_find_state *, diff --git a/window.c b/window.c index ab1d9217..754b9e59 100644 --- a/window.c +++ b/window.c @@ -891,7 +891,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) if (gethostname(host, sizeof host) == 0) screen_set_title(&wp->base, host); - input_init(wp); + wp->ictx = input_init(wp); return (wp); } @@ -907,7 +907,7 @@ window_pane_destroy(struct window_pane *wp) close(wp->fd); } - input_free(wp); + input_free(wp->ictx); screen_free(&wp->status_screen); @@ -949,7 +949,7 @@ window_pane_read_callback(__unused struct bufferevent *bufev, void *data) } log_debug("%%%u has %zu bytes", wp->id, size); - input_parse(wp); + input_parse_pane(wp); wp->pipe_off = EVBUFFER_LENGTH(evb); } @@ -1257,7 +1257,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) return (0); - if (input_key(wp, key, m) != 0) + if (input_key_pane(wp, key, m) != 0) return (-1); if (KEYC_IS_MOUSE(key)) @@ -1269,7 +1269,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, wp2->fd != -1 && (~wp2->flags & PANE_INPUTOFF) && window_pane_visible(wp2)) - input_key(wp2, key, NULL); + input_key_pane(wp2, key, NULL); } } return (0); From c15396459b4f73ac81e9947d242b695c0fc1dedb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 19 Mar 2020 14:06:37 +0000 Subject: [PATCH 0049/1006] No util.h. --- job.c | 1 - 1 file changed, 1 deletion(-) diff --git a/job.c b/job.c index 6f799ee5..37580095 100644 --- a/job.c +++ b/job.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "tmux.h" From 74ed17d41bb0f3113052fb4cccaef341d53f9e5b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Mar 2020 14:23:58 +0000 Subject: [PATCH 0050/1006] Little bit of tidying. --- menu.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/menu.c b/menu.c index 6024ba02..049c4836 100644 --- a/menu.c +++ b/menu.c @@ -138,16 +138,13 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) struct screen *s = &md->s; struct menu *menu = md->menu; struct screen_write_ctx ctx; - u_int i, px, py; + u_int i, px = md->px, py = md->py; screen_write_start(&ctx, NULL, s); screen_write_clearscreen(&ctx, 8); screen_write_menu(&ctx, menu, md->choice); screen_write_stop(&ctx); - px = md->px; - py = md->py; - for (i = 0; i < screen_size_y(&md->s); i++) tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i); @@ -270,7 +267,6 @@ chosen: pr = cmd_parse_from_string(item->command, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: - new_item = NULL; break; case CMD_PARSE_ERROR: new_item = cmdq_get_error(pr->error); From c3e96cce4eb0346e31d5c6474a5a59f7c441b7f1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 06:09:19 +0000 Subject: [PATCH 0051/1006] Another fix to make other-end forget the selection mode, from Anindya Mukherjee. --- window-copy.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/window-copy.c b/window-copy.c index abde1c48..aa6c3462 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1361,7 +1361,9 @@ window_copy_cmd_other_end(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; + struct window_copy_mode_data *data = wme->data; + data->selflag = SEL_CHAR; if ((np % 2) != 0) window_copy_other_end(wme); return (WINDOW_COPY_CMD_NOTHING); From a3ff5a9e258e84b76af53a136bf8473ae67a0bcd Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 13:12:04 +0000 Subject: [PATCH 0052/1006] select_word_end needs to forward no_reset flag or select-word selects too much. --- window-copy.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/window-copy.c b/window-copy.c index aa6c3462..c6d3612f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -123,7 +123,7 @@ static void window_copy_cursor_next_word(struct window_mode_entry *, static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *, const char *, u_int *, u_int *); static void window_copy_cursor_next_word_end(struct window_mode_entry *, - const char *); + const char *, int); static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, const char *, int, u_int *, u_int *); static void window_copy_cursor_previous_word(struct window_mode_entry *, @@ -1241,7 +1241,8 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_next_word_end(wme, "{[( "); + window_copy_cursor_next_word_end(wme, "{[( ", + 0); continue; } /* For vi, continue searching for bracket until EOL. */ @@ -1324,7 +1325,7 @@ window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, " "); + window_copy_cursor_next_word_end(wme, " ", 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1352,7 +1353,7 @@ window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, ws); + window_copy_cursor_next_word_end(wme, ws, 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1579,10 +1580,10 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) if (px >= window_copy_find_length(wme, py) || !window_copy_in_set(wme, px + 1, py, data->ws)) - window_copy_cursor_next_word_end(wme, data->ws); + window_copy_cursor_next_word_end(wme, data->ws, 1); else { window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1, 0)) + if (window_copy_update_selection(wme, 1, 1)) window_copy_redraw_lines(wme, data->cy, 1); } data->endselrx = data->cx; @@ -3882,7 +3883,7 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, static void window_copy_cursor_next_word_end(struct window_mode_entry *wme, - const char *separators) + const char *separators, int no_reset) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -3928,7 +3929,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, px--; window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1, 0)) + if (window_copy_update_selection(wme, 1, no_reset)) window_copy_redraw_lines(wme, data->cy, 1); } From 7bbca4939537bcb12d9488cbd0e88fb8bbb5dbc3 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 17:26:14 +0000 Subject: [PATCH 0053/1006] Fix positioning of menu in choose modes and a couple of keys in tree mode. --- menu.c | 4 ++++ mode-tree.c | 4 ++++ window-tree.c | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/menu.c b/menu.c index 049c4836..f70b1d18 100644 --- a/menu.c +++ b/menu.c @@ -295,6 +295,10 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) return (-1); + if (px + menu->width + 4 > c->tty.sx) + px = c->tty.sx - menu->width - 4; + if (py + menu->count + 2 > c->tty.sy) + py = c->tty.sy - menu->count - 2; md = xcalloc(1, sizeof *md); md->item = item; diff --git a/mode-tree.c b/mode-tree.c index b9fa5f65..843a74bc 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -847,6 +847,10 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x, mtm->itemdata = mti->itemdata; mtd->references++; + if (x >= (menu->width + 4) / 2) + x -= (menu->width + 4) / 2; + else + x = 0; if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback, mtm) != 0) menu_free(menu); diff --git a/window-tree.c b/window-tree.c index d163dd9e..4d5b4a1e 100644 --- a/window-tree.c +++ b/window-tree.c @@ -54,8 +54,8 @@ static void window_tree_key(struct window_mode_entry *, "}" static const struct menu_item window_tree_menu_items[] = { - { "Select", 'E', NULL }, - { "Expand", 'R', NULL }, + { "Select", '\r', NULL }, + { "Expand", KEYC_RIGHT, NULL }, { "", KEYC_NONE, NULL }, { "Tag", 't', NULL }, { "Tag All", '\024', NULL }, From 06c3079d66929e0c71575e877098fc533ae5f4a5 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 17:59:39 +0000 Subject: [PATCH 0054/1006] Make the mouse_word and mouse_line formats work in copy mode and enable the default pane menu in copy mode. --- cmd-copy-mode.c | 9 +++++++-- format.c | 28 ++++++++++++++++++++-------- key-bindings.c | 11 +++++++---- tmux.1 | 4 +++- tmux.h | 2 ++ window-copy.c | 25 +++++++++++++++++++++++-- 6 files changed, 62 insertions(+), 17 deletions(-) diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 9c0015bf..bdb8245e 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = { .name = "copy-mode", .alias = NULL, - .args = { "eHMt:u", 0, 0 }, - .usage = "[-eHMu] " CMD_TARGET_PANE_USAGE, + .args = { "eHMt:uq", 0, 0 }, + .usage = "[-eHMuq] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -61,6 +61,11 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) struct session *s; struct window_pane *wp = item->target.wp; + if (args_has(args, 'q')) { + window_pane_reset_mode_all(wp); + return (CMD_RETURN_NORMAL); + } + if (args_has(args, 'M')) { if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); diff --git a/format.c b/format.c index fe8e9c68..ef69b9b7 100644 --- a/format.c +++ b/format.c @@ -948,7 +948,6 @@ format_grid_word(struct grid *gd, u_int x, u_int y) ws = options_get_string(global_s_options, "word-separators"); - y = gd->hsize + y; for (;;) { grid_get_cell(gd, x, y, &gc); if (gc.flags & GRID_FLAG_PADDING) @@ -1009,6 +1008,7 @@ static void format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp; + struct grid *gd; u_int x, y; char *s; @@ -1017,12 +1017,18 @@ format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) wp = cmd_mouse_pane(&ft->m, NULL, NULL); if (wp == NULL) return; - if (!TAILQ_EMPTY (&wp->modes)) - return; if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) return; - s = format_grid_word(wp->base.grid, x, y); + if (!TAILQ_EMPTY(&wp->modes)) { + if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode) + s = window_copy_get_word(wp, x, y); + else + s = NULL; + } else { + gd = wp->base.grid; + s = format_grid_word(gd, x, gd->hsize + y); + } if (s != NULL) fe->value = s; } @@ -1037,7 +1043,6 @@ format_grid_line(struct grid *gd, u_int y) size_t size = 0; char *s = NULL; - y = gd->hsize + y; for (x = 0; x < grid_line_length(gd, y); x++) { grid_get_cell(gd, x, y, &gc); if (gc.flags & GRID_FLAG_PADDING) @@ -1059,6 +1064,7 @@ static void format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp; + struct grid *gd; u_int x, y; char *s; @@ -1067,12 +1073,18 @@ format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) wp = cmd_mouse_pane(&ft->m, NULL, NULL); if (wp == NULL) return; - if (!TAILQ_EMPTY (&wp->modes)) - return; if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) return; - s = format_grid_line(wp->base.grid, y); + if (!TAILQ_EMPTY(&wp->modes)) { + if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode) + s = window_copy_get_line(wp, y); + else + s = NULL; + } else { + gd = wp->base.grid; + s = format_grid_line(gd, gd->hsize + y); + } if (s != NULL) fe->value = s; } diff --git a/key-bindings.c b/key-bindings.c index 57b71c0f..a31c338a 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -46,10 +46,13 @@ " 'New After' 'w' {new-window -a}" \ " 'New At End' 'W' {new-window}" #define DEFAULT_PANE_MENU \ + " '#{?#{==:#{pane_mode},copy-mode},Go To Top,}' '<' {send -X history-top}" \ + " '#{?#{==:#{pane_mode},copy-mode},Go To Bottom,}' '<' {send -X history-bottom}" \ + " ''" \ " '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {copy-mode -t=; send -Xt= search-backward \"#{q:mouse_word}\"}" \ - " '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {send-keys -l -- \"#{q:mouse_word}\"}" \ - " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {set-buffer -- \"#{q:mouse_word}\"}" \ - " '#{?mouse_line,Copy Line,}' 'l' {set-buffer -- \"#{q:mouse_line}\"}" \ + " '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {copy-mode -q; send-keys -l -- \"#{q:mouse_word}\"}" \ + " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \ + " '#{?mouse_line,Copy Line,}' 'l' {copy-mode -q; set-buffer -- \"#{q:mouse_line}\"}" \ " ''" \ " 'Horizontal Split' 'h' {split-window -h}" \ " 'Vertical Split' 'v' {split-window -v}" \ @@ -354,7 +357,7 @@ key_bindings_init(void) "bind -n MouseDown3Status display-menu -t= -xW -yS -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, /* Mouse button 3 down on pane. */ - "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{pane_in_mode}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", + "bind -n MouseDown3Pane if -Ft= '#{mouse_any_flag}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", "bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU, /* Copy mode (emacs) keys. */ diff --git a/tmux.1 b/tmux.1 index dff28144..8cd4841f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1565,7 +1565,7 @@ The synopsis for the command is: .Bl -tag -width Ds .It Xo Ic copy-mode -.Op Fl eHMu +.Op Fl eHMqu .Op Fl t Ar target-pane .Xc Enter copy mode. @@ -1577,6 +1577,8 @@ begins a mouse drag (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . .Fl H hides the position indicator in the top right. +.Fl q +cancels copy mode and any other modes. .Pp .Fl e specifies that scrolling to the bottom of the history (to the visible screen) diff --git a/tmux.h b/tmux.h index 53372cfe..f202315b 100644 --- a/tmux.h +++ b/tmux.h @@ -2636,6 +2636,8 @@ void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); void window_copy_pageup(struct window_pane *, int); void window_copy_start_drag(struct client *, struct mouse_event *); +char *window_copy_get_word(struct window_pane *, u_int, u_int); +char *window_copy_get_line(struct window_pane *, u_int); /* names.c */ void check_window_name(struct window *); diff --git a/window-copy.c b/window-copy.c index c6d3612f..9fe4fcef 100644 --- a/window-copy.c +++ b/window-copy.c @@ -596,10 +596,31 @@ window_copy_next_paragraph(struct window_mode_entry *wme) window_copy_scroll_to(wme, ox, oy); } +char * +window_copy_get_word(struct window_pane *wp, u_int x, u_int y) +{ + struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->screen.grid; + + return (format_grid_word(gd, x, gd->hsize + y)); +} + +char * +window_copy_get_line(struct window_pane *wp, u_int y) +{ + struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->screen.grid; + + return (format_grid_line(gd, gd->hsize + y)); +} + static void window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) { struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->screen.grid; char *s; format_add(ft, "scroll_position", "%d", data->oy); @@ -619,13 +640,13 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) } else format_add(ft, "selection_active", "%d", 0); - s = format_grid_word(data->screen.grid, data->cx, data->cy); + s = format_grid_word(gd, data->cx, gd->hsize + data->cy); if (s != NULL) { format_add(ft, "copy_cursor_word", "%s", s); free(s); } - s = format_grid_line(data->screen.grid, data->cy); + s = format_grid_line(gd, gd->hsize + data->cy); if (s != NULL) { format_add(ft, "copy_cursor_line", "%s", s); free(s); From 005cd486202c35327774857e9cef8e9d446dc092 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 18:05:22 +0000 Subject: [PATCH 0055/1006] Oops, typo in key binding. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index a31c338a..dc3b7b9b 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -47,7 +47,7 @@ " 'New At End' 'W' {new-window}" #define DEFAULT_PANE_MENU \ " '#{?#{==:#{pane_mode},copy-mode},Go To Top,}' '<' {send -X history-top}" \ - " '#{?#{==:#{pane_mode},copy-mode},Go To Bottom,}' '<' {send -X history-bottom}" \ + " '#{?#{==:#{pane_mode},copy-mode},Go To Bottom,}' '>' {send -X history-bottom}" \ " ''" \ " '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {copy-mode -t=; send -Xt= search-backward \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {copy-mode -q; send-keys -l -- \"#{q:mouse_word}\"}" \ From 68cf61aa4649e29f434750ff0ec45f78e9164320 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 18:11:56 +0000 Subject: [PATCH 0056/1006] Still want the per-mode menus outside copy mode. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index dc3b7b9b..99ce34ca 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -357,7 +357,7 @@ key_bindings_init(void) "bind -n MouseDown3Status display-menu -t= -xW -yS -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, /* Mouse button 3 down on pane. */ - "bind -n MouseDown3Pane if -Ft= '#{mouse_any_flag}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", + "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{!=:#{pane_mode},copy-mode}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", "bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU, /* Copy mode (emacs) keys. */ From 4d6805284b502c710662985f286803d993572403 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 18:19:22 +0000 Subject: [PATCH 0057/1006] Disable swap entries if nothing to swap with. --- key-bindings.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index 99ce34ca..7dcaa570 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -34,8 +34,8 @@ " 'New Session' 's' {new-session}" \ " 'New Window' 'w' {new-window}" #define DEFAULT_WINDOW_MENU \ - " 'Swap Left' 'l' {swap-window -t:-1}" \ - " 'Swap Right' 'r' {swap-window -t:+1}" \ + " '#{?#{>:#{session_windows},1},,-}Swap Left' 'l' {swap-window -t:-1}" \ + " '#{?#{>:#{session_windows},1},,-}Swap Right' 'r' {swap-window -t:+1}" \ " '#{?pane_marked_set,,-}Swap Marked' 's' {swap-window}" \ " ''" \ " 'Kill' 'X' {kill-window}" \ @@ -57,8 +57,8 @@ " 'Horizontal Split' 'h' {split-window -h}" \ " 'Vertical Split' 'v' {split-window -v}" \ " ''" \ - " 'Swap Up' 'u' {swap-pane -U}" \ - " 'Swap Down' 'd' {swap-pane -D}" \ + " '#{?#{>:#{window_panes},1},,-}Swap Up' 'u' {swap-pane -U}" \ + " '#{?#|>:#{window_panes},1},,-}Swap Down' 'd' {swap-pane -D}" \ " '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \ " ''" \ " 'Kill' 'X' {kill-pane}" \ From b66501df0c5de63d2883e7c3c4c1b8d7c31f9fad Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 18:20:58 +0000 Subject: [PATCH 0058/1006] Put swap down back in the right place. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index 7dcaa570..0a2163bf 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -58,7 +58,7 @@ " 'Vertical Split' 'v' {split-window -v}" \ " ''" \ " '#{?#{>:#{window_panes},1},,-}Swap Up' 'u' {swap-pane -U}" \ - " '#{?#|>:#{window_panes},1},,-}Swap Down' 'd' {swap-pane -D}" \ + " '#{?#{>:#{window_panes},1},,-}Swap Down' 'd' {swap-pane -D}" \ " '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \ " ''" \ " 'Kill' 'X' {kill-pane}" \ From 7c25f22074bc453898dc9c65318ca73b0014996d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 18:22:37 +0000 Subject: [PATCH 0059/1006] Similarly, disable zoom if only one pane. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index 0a2163bf..9730fb1b 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -64,7 +64,7 @@ " 'Kill' 'X' {kill-pane}" \ " 'Respawn' 'R' {respawn-pane -k}" \ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ - " '#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}" + " '#{?#{>:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}" static int key_bindings_cmp(struct key_binding *, struct key_binding *); RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp); From 1a4e64ba69c7c5f287558333b3acb0e98e767081 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 18:35:53 +0000 Subject: [PATCH 0060/1006] Apply same menu items to view mode like copy mode. --- format.c | 6 ++++-- key-bindings.c | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/format.c b/format.c index ef69b9b7..da241c48 100644 --- a/format.c +++ b/format.c @@ -1021,7 +1021,8 @@ format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) return; if (!TAILQ_EMPTY(&wp->modes)) { - if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode) + if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || + TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) s = window_copy_get_word(wp, x, y); else s = NULL; @@ -1077,7 +1078,8 @@ format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) return; if (!TAILQ_EMPTY(&wp->modes)) { - if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode) + if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || + TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) s = window_copy_get_line(wp, y); else s = NULL; diff --git a/key-bindings.c b/key-bindings.c index 9730fb1b..a528a6c0 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -46,10 +46,10 @@ " 'New After' 'w' {new-window -a}" \ " 'New At End' 'W' {new-window}" #define DEFAULT_PANE_MENU \ - " '#{?#{==:#{pane_mode},copy-mode},Go To Top,}' '<' {send -X history-top}" \ - " '#{?#{==:#{pane_mode},copy-mode},Go To Bottom,}' '>' {send -X history-bottom}" \ + " '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \ + " '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \ " ''" \ - " '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {copy-mode -t=; send -Xt= search-backward \"#{q:mouse_word}\"}" \ + " '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {if -F '#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}' 'copy-mode -t='; send -Xt= search-backward \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {copy-mode -q; send-keys -l -- \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \ " '#{?mouse_line,Copy Line,}' 'l' {copy-mode -q; set-buffer -- \"#{q:mouse_line}\"}" \ @@ -357,7 +357,7 @@ key_bindings_init(void) "bind -n MouseDown3Status display-menu -t= -xW -yS -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, /* Mouse button 3 down on pane. */ - "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{!=:#{pane_mode},copy-mode}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", + "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", "bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU, /* Copy mode (emacs) keys. */ From 9a55f65702b5d32e0127c78ea9762c7bb47e3477 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Mar 2020 20:12:39 +0000 Subject: [PATCH 0061/1006] Fix select-word when not on a word, from Anindya Mukherjee. --- window-copy.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/window-copy.c b/window-copy.c index 9fe4fcef..53faa5a8 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1590,13 +1590,12 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) data->dx = data->cx; data->dy = screen_hsize(data->backing) + data->cy - data->oy; - px = data->cx; - py = screen_hsize(data->backing) + data->cy - data->oy; - data->ws = options_get_string(s->options, "word-separators"); window_copy_cursor_previous_word(wme, data->ws, 0); - data->selrx = data->cx; - data->selry = screen_hsize(data->backing) + data->cy - data->oy; + px = data->cx; + py = screen_hsize(data->backing) + data->cy - data->oy; + data->selrx = px; + data->selry = py; window_copy_start_selection(wme); if (px >= window_copy_find_length(wme, py) || @@ -1609,6 +1608,8 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) } data->endselrx = data->cx; data->endselry = screen_hsize(data->backing) + data->cy - data->oy; + if (data->dx > data->endselrx) + data->dx = data->endselrx; return (WINDOW_COPY_CMD_REDRAW); } From 5aeab5ab4075c7740c223678df1548dc4a402d83 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Mar 2020 13:15:38 +0000 Subject: [PATCH 0062/1006] Preserve exit status from run-shell and pass to the client. --- cmd-run-shell.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 1cf97d51..a57beb83 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -160,6 +160,7 @@ cmd_run_shell_callback(struct job *job) { struct cmd_run_shell_data *cdata = job_get_data(job); struct bufferevent *event = job_get_event(job); + struct cmdq_item *item = cdata->item; char *cmd = cdata->cmd, *msg = NULL, *line; size_t size; int retcode, status; @@ -189,13 +190,17 @@ cmd_run_shell_callback(struct job *job) } else if (WIFSIGNALED(status)) { retcode = WTERMSIG(status); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); + retcode += 128; } if (msg != NULL) cmd_run_shell_print(job, msg); free(msg); - if (cdata->item != NULL) - cmdq_continue(cdata->item); + if (item != NULL) { + if (item->client != NULL && item->client->session == NULL) + item->client->retval = retcode; + cmdq_continue(item); + } } static void From 8828b958f04747cbf6126ec3443007b29570413a Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Mar 2020 13:16:15 +0000 Subject: [PATCH 0063/1006] Break code to convert an argument as a percentage into a common function. --- arguments.c | 50 ++++++++++++++++++++++++++++++++++ cmd-join-pane.c | 51 +++++++++++++---------------------- cmd-resize-pane.c | 68 +++++++++++------------------------------------ tmux.h | 2 ++ 4 files changed, 85 insertions(+), 86 deletions(-) diff --git a/arguments.c b/arguments.c index cb527bcd..e573249a 100644 --- a/arguments.c +++ b/arguments.c @@ -344,3 +344,53 @@ args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, *cause = NULL; return (ll); } + +/* Convert an argument to a number which may be a percentage. */ +long long +args_percentage(struct args *args, u_char ch, long long minval, + long long maxval, long long curval, char **cause) +{ + const char *errstr; + long long ll; + struct args_entry *entry; + struct args_value *value; + size_t valuelen; + char *copy; + + if ((entry = args_find(args, ch)) == NULL) { + *cause = xstrdup("missing"); + return (0); + } + value = TAILQ_LAST(&entry->values, args_values); + valuelen = strlen(value->value); + + if (value->value[valuelen - 1] == '%') { + copy = xstrdup(value->value); + copy[valuelen - 1] = '\0'; + + ll = strtonum(copy, 0, 100, &errstr); + free(copy); + if (errstr != NULL) { + *cause = xstrdup(errstr); + return (0); + } + ll = (curval * ll) / 100; + if (ll < minval) { + *cause = xstrdup("too large"); + return (0); + } + if (ll > maxval) { + *cause = xstrdup("too small"); + return (0); + } + } else { + ll = strtonum(value->value, minval, maxval, &errstr); + if (errstr != NULL) { + *cause = xstrdup(errstr); + return (0); + } + } + + *cause = NULL; + return (ll); +} diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 94f3ae13..108eab3a 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -51,7 +51,7 @@ const struct cmd_entry cmd_move_pane_entry = { .alias = "movep", .args = { "bdhvp:l:s:t:", 0, 0 }, - .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, + .usage = "[-bdhv] [-l size] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .target = { 't', CMD_FIND_PANE, 0 }, @@ -69,9 +69,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *src_wp, *dst_wp; - char *cause, *copy; - const char *errstr, *p; - size_t plen; + char *cause = NULL; int size, percentage, dst_idx, not_same_window; int flags; enum layout_type type; @@ -108,40 +106,27 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) type = LAYOUT_LEFTRIGHT; size = -1; - if ((p = args_get(args, 'l')) != NULL) { - plen = strlen(p); - if (p[plen - 1] == '%') { - copy = xstrdup(p); - copy[plen - 1] = '\0'; - percentage = strtonum(copy, 0, INT_MAX, &errstr); - free(copy); - if (errstr != NULL) { - cmdq_error(item, "percentage %s", errstr); - return (CMD_RETURN_ERROR); - } + if (args_has(args, 'l')) { + if (type == LAYOUT_TOPBOTTOM) { + size = args_percentage(args, 'l', 0, INT_MAX, + dst_wp->sy, &cause); + } else { + size = args_percentage(args, 'l', 0, INT_MAX, + dst_wp->sx, &cause); + } + } else if (args_has(args, 'p')) { + percentage = args_strtonum(args, 'p', 0, 100, &cause); + if (cause == NULL) { if (type == LAYOUT_TOPBOTTOM) size = (dst_wp->sy * percentage) / 100; else size = (dst_wp->sx * percentage) / 100; - } else { - size = args_strtonum(args, 'l', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(item, "size %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } } - } else if (args_has(args, 'p')) { - percentage = args_strtonum(args, 'p', 0, 100, &cause); - if (cause != NULL) { - cmdq_error(item, "percentage %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - if (type == LAYOUT_TOPBOTTOM) - size = (dst_wp->sy * percentage) / 100; - else - size = (dst_wp->sx * percentage) / 100; + } + if (cause != NULL) { + cmdq_error(item, "size %s", cause); + free(cause); + return (CMD_RETURN_ERROR); } flags = 0; diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 3962546d..b0aa2756 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -56,11 +56,10 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) struct window *w = wl->window; struct client *c = item->client; struct session *s = item->target.s; - const char *errstr, *p; - char *cause, *copy; + const char *errstr; + char *cause; u_int adjust; - int x, y, percentage; - size_t plen; + int x, y; if (args_has(args, 'M')) { if (cmd_mouse_window(&shared->mouse, &s) == NULL) @@ -93,58 +92,21 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) } } - if ((p = args_get(args, 'x')) != NULL) { - plen = strlen(p); - if (p[plen - 1] == '%') { - copy = xstrdup(p); - copy[plen - 1] = '\0'; - percentage = strtonum(copy, 0, INT_MAX, &errstr); - free(copy); - if (errstr != NULL) { - cmdq_error(item, "width %s", errstr); - return (CMD_RETURN_ERROR); - } - x = (w->sx * percentage) / 100; - if (x < PANE_MINIMUM) - x = PANE_MINIMUM; - if (x > INT_MAX) - x = INT_MAX; - } else { - x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX, - &cause); - if (cause != NULL) { - cmdq_error(item, "width %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } + if (args_has(args, 'x')) { + x = args_percentage(args, 'x', 0, INT_MAX, w->sx, &cause); + if (cause != NULL) { + cmdq_error(item, "width %s", cause); + free(cause); + return (CMD_RETURN_ERROR); } layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x); } - if ((p = args_get(args, 'y')) != NULL) { - plen = strlen(p); - if (p[plen - 1] == '%') { - copy = xstrdup(p); - copy[plen - 1] = '\0'; - percentage = strtonum(copy, 0, INT_MAX, &errstr); - free(copy); - if (errstr != NULL) { - cmdq_error(item, "height %s", errstr); - return (CMD_RETURN_ERROR); - } - y = (w->sy * percentage) / 100; - if (y < PANE_MINIMUM) - y = PANE_MINIMUM; - if (y > INT_MAX) - y = INT_MAX; - } - else { - y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX, - &cause); - if (cause != NULL) { - cmdq_error(item, "height %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } + if (args_has(args, 'y')) { + y = args_percentage(args, 'y', 0, INT_MAX, w->sy, &cause); + if (cause != NULL) { + cmdq_error(item, "width %s", cause); + free(cause); + return (CMD_RETURN_ERROR); } layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y); } diff --git a/tmux.h b/tmux.h index f202315b..424a0ddf 100644 --- a/tmux.h +++ b/tmux.h @@ -2060,6 +2060,8 @@ const char *args_first_value(struct args *, u_char, struct args_value **); const char *args_next_value(struct args_value **); long long args_strtonum(struct args *, u_char, long long, long long, char **); +long long args_percentage(struct args *, u_char, long long, + long long, long long, char **); /* cmd-find.c */ int cmd_find_target(struct cmd_find_state *, struct cmdq_item *, From af6ae35900a066389c1c9b4dbd430b557fcb8816 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Mar 2020 13:19:56 +0000 Subject: [PATCH 0064/1006] Set end position correctly, GitHub issue 2129 from Anindya Mukherjee. --- window-copy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/window-copy.c b/window-copy.c index 53faa5a8..eeed210d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1566,12 +1566,12 @@ window_copy_cmd_select_line(struct window_copy_cmd_state *cs) window_copy_cursor_start_of_line(wme); data->selrx = data->cx; data->selry = screen_hsize(data->backing) + data->cy - data->oy; + data->endselrx = window_copy_find_length(wme, data->selry); + data->endselry = data->selry; window_copy_start_selection(wme); for (; np > 1; np--) window_copy_cursor_down(wme, 0); window_copy_cursor_end_of_line(wme); - data->endselrx = data->cx; - data->endselry = screen_hsize(data->backing) + data->cy - data->oy; return (WINDOW_COPY_CMD_REDRAW); } From edca27ae45db7be104bc56a4e48e55cddc40acdb Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Mar 2020 13:51:30 +0000 Subject: [PATCH 0065/1006] AIX colours are always stored as 90-97, not 100-107. From Johannes Altmanninger. --- grid.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/grid.c b/grid.c index 9e18a73d..29943e6b 100644 --- a/grid.c +++ b/grid.c @@ -758,15 +758,15 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values) case 8: values[n++] = 49; break; - case 100: - case 101: - case 102: - case 103: - case 104: - case 105: - case 106: - case 107: - values[n++] = gc->bg - 10; + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + values[n++] = gc->bg + 10; break; } } From 8a838b0372163e1a7c0379991545a55028bb9eba Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Mar 2020 08:09:43 +0000 Subject: [PATCH 0066/1006] Add support for overlay popup boxes to show text or output temporarily above the normal layout. These work similarly to menus and are created with the display-popup command. --- Makefile | 1 + cmd-display-menu.c | 93 +++++++++ cmd-display-panes.c | 2 +- cmd.c | 2 + job.c | 19 ++ menu.c | 17 +- popup.c | 447 ++++++++++++++++++++++++++++++++++++++++++++ screen-redraw.c | 2 + server-client.c | 72 ++++--- tmux.1 | 91 ++++++++- tmux.h | 20 +- tty.c | 19 +- 12 files changed, 749 insertions(+), 36 deletions(-) create mode 100644 popup.c diff --git a/Makefile b/Makefile index 6645bbfc..24117bf1 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,7 @@ SRCS= alerts.c \ options-table.c \ options.c \ paste.c \ + popup.c \ proc.c \ procname.c \ regsub.c \ diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 2ba674a3..aafe2447 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -29,6 +29,8 @@ static enum cmd_retval cmd_display_menu_exec(struct cmd *, struct cmdq_item *); +static enum cmd_retval cmd_display_popup_exec(struct cmd *, + struct cmdq_item *); const struct cmd_entry cmd_display_menu_entry = { .name = "display-menu", @@ -44,6 +46,21 @@ const struct cmd_entry cmd_display_menu_entry = { .exec = cmd_display_menu_exec }; +const struct cmd_entry cmd_display_popup_entry = { + .name = "display-popup", + .alias = "popup", + + .args = { "CEKc:d:h:R:t:w:x:y:", 0, -1 }, + .usage = "[-CEK] [-c target-client] [-d start-directory] [-h height] " + "[-R shell-command] " CMD_TARGET_PANE_USAGE " [-w width] " + "[-x position] [-y position] [command line ...]", + + .target = { 't', CMD_FIND_PANE, 0 }, + + .flags = CMD_AFTERHOOK, + .exec = cmd_display_popup_exec +}; + static void cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, struct args *args, u_int *px, u_int *py, u_int w, u_int h) @@ -190,3 +207,79 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } + +static enum cmd_retval +cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = self->args; + struct client *c; + struct cmd_find_state *fs = &item->target; + const char *value, *cmd = NULL, **lines = NULL; + const char *shellcmd = NULL; + char *cwd, *cause; + int flags = 0; + u_int px, py, w, h, nlines = 0; + + if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) + return (CMD_RETURN_ERROR); + if (args_has(args, 'C')) { + server_client_clear_overlay(c); + return (CMD_RETURN_NORMAL); + } + if (c->overlay_draw != NULL) + return (CMD_RETURN_NORMAL); + + if (args->argc >= 1) + cmd = args->argv[0]; + if (args->argc >= 2) { + lines = (const char **)args->argv + 1; + nlines = args->argc - 1; + } + + if (nlines != 0) + h = nlines + 2; + else + h = c->tty.sy / 2; + if (args_has(args, 'h')) { + h = args_percentage(args, 'h', 1, c->tty.sy, c->tty.sy, &cause); + if (cause != NULL) { + cmdq_error(item, "height %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + } + + if (nlines != 0) + w = popup_width(item, nlines, lines, c, fs) + 2; + else + w = c->tty.sx / 2; + if (args_has(args, 'w')) { + w = args_percentage(args, 'w', 1, c->tty.sx, c->tty.sx, &cause); + if (cause != NULL) { + cmdq_error(item, "width %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + } + + cmd_display_menu_get_position(c, item, args, &px, &py, w, h); + + value = args_get(args, 'd'); + if (value != NULL) + cwd = format_single(NULL, value, c, fs->s, fs->wl, fs->wp); + else + cwd = xstrdup(server_client_get_cwd(c, fs->s)); + + value = args_get(args, 'R'); + if (value != NULL) + shellcmd = format_single(NULL, value, c, fs->s, fs->wl, fs->wp); + + if (args_has(args, 'K')) + flags |= POPUP_WRITEKEYS; + if (args_has(args, 'E')) + flags |= POPUP_CLOSEEXIT; + if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, + cmd, cwd, c, fs) != 0) + return (CMD_RETURN_NORMAL); + return (CMD_RETURN_WAIT); +} diff --git a/cmd-display-panes.c b/cmd-display-panes.c index df97819c..d8d351c2 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -273,7 +273,7 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) else cdata->item = item; - server_client_set_overlay(c, delay, cmd_display_panes_draw, + server_client_set_overlay(c, delay, NULL, NULL, cmd_display_panes_draw, cmd_display_panes_key, cmd_display_panes_free, cdata); if (args_has(args, 'b')) diff --git a/cmd.c b/cmd.c index 03a17ab1..52cf5af1 100644 --- a/cmd.c +++ b/cmd.c @@ -44,6 +44,7 @@ extern const struct cmd_entry cmd_delete_buffer_entry; extern const struct cmd_entry cmd_detach_client_entry; extern const struct cmd_entry cmd_display_menu_entry; extern const struct cmd_entry cmd_display_message_entry; +extern const struct cmd_entry cmd_display_popup_entry; extern const struct cmd_entry cmd_display_panes_entry; extern const struct cmd_entry cmd_down_pane_entry; extern const struct cmd_entry cmd_find_window_entry; @@ -133,6 +134,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_detach_client_entry, &cmd_display_menu_entry, &cmd_display_message_entry, + &cmd_display_popup_entry, &cmd_display_panes_entry, &cmd_find_window_entry, &cmd_has_session_entry, diff --git a/job.c b/job.c index c31d51b2..997a6574 100644 --- a/job.c +++ b/job.c @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -205,6 +206,24 @@ job_free(struct job *job) free(job); } +/* Resize job. */ +void +job_resize(struct job *job, u_int sx, u_int sy) +{ + struct winsize ws; + + if (job->fd == -1 || (~job->flags & JOB_PTY)) + return; + + log_debug("resize job %p: %ux%u", job, sx, sy); + + memset(&ws, 0, sizeof ws); + ws.ws_col = sx; + ws.ws_row = sy; + if (ioctl(job->fd, TIOCSWINSZ, &ws) == -1) + fatal("ioctl failed"); +} + /* Job buffer read callback. */ static void job_read_callback(__unused struct bufferevent *bufev, void *data) diff --git a/menu.c b/menu.c index f70b1d18..7ca6253e 100644 --- a/menu.c +++ b/menu.c @@ -130,6 +130,16 @@ menu_free(struct menu *menu) free(menu); } +static int +menu_mode_cb(struct client *c, __unused u_int *cx, __unused u_int *cy) +{ + struct menu_data *md = c->overlay_data; + + if (~md->flags & MENU_NOMOUSE) + return (MODE_MOUSE_ALL); + return (0); +} + static void menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) { @@ -147,9 +157,6 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) for (i = 0; i < screen_size_y(&md->s); i++) tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i); - - if (~md->flags & MENU_NOMOUSE) - tty_update_mode(tty, MODE_MOUSE_ALL, NULL); } static void @@ -317,7 +324,7 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, md->cb = cb; md->data = data; - server_client_set_overlay(c, 0, menu_draw_cb, menu_key_cb, menu_free_cb, - md); + server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, + menu_key_cb, menu_free_cb, md); return (0); } diff --git a/popup.c b/popup.c new file mode 100644 index 00000000..16b7b260 --- /dev/null +++ b/popup.c @@ -0,0 +1,447 @@ +/* $OpenBSD$ */ + +/* + * 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 +#include +#include + +#include "tmux.h" + +struct popup_data { + struct client *c; + struct cmdq_item *item; + int flags; + + char **lines; + u_int nlines; + + char *cmd; + struct cmd_find_state fs; + struct screen s; + + struct job *job; + struct input_ctx *ictx; + int status; + + u_int px; + u_int py; + u_int sx; + u_int sy; + + enum { OFF, MOVE, SIZE } dragging; + u_int dx; + u_int dy; + + u_int lx; + u_int ly; + u_int lb; +}; + +static void +popup_write_screen(struct client *c, struct popup_data *pd) +{ + struct cmdq_item *item = pd->item; + struct screen_write_ctx ctx; + char *copy, *next, *loop, *tmp; + struct format_tree *ft; + u_int i, y; + + ft = format_create(item->client, item, FORMAT_NONE, 0); + if (cmd_find_valid_state(&pd->fs)) + format_defaults(ft, c, pd->fs.s, pd->fs.wl, pd->fs.wp); + else + format_defaults(ft, c, NULL, NULL, NULL); + + screen_write_start(&ctx, NULL, &pd->s); + screen_write_clearscreen(&ctx, 8); + + y = 0; + for (i = 0; i < pd->nlines; i++) { + if (y == pd->sy - 2) + break; + copy = next = xstrdup(pd->lines[i]); + while ((loop = strsep(&next, "\n")) != NULL) { + if (y == pd->sy - 2) + break; + tmp = format_expand(ft, loop); + screen_write_cursormove(&ctx, 0, y, 0); + format_draw(&ctx, &grid_default_cell, pd->sx - 2, tmp, + NULL); + free(tmp); + y++; + } + free(copy); + } + + format_free(ft); + screen_write_cursormove(&ctx, 0, y, 0); + screen_write_stop(&ctx); +} + +static int +popup_mode_cb(struct client *c, u_int *cx, u_int *cy) +{ + struct popup_data *pd = c->overlay_data; + + if (pd->ictx == NULL) + return (0); + *cx = pd->px + 1 + pd->s.cx; + *cy = pd->py + 1 + pd->s.cy; + return (pd->s.mode); +} + +static int +popup_check_cb(struct client *c, u_int px, u_int py) +{ + struct popup_data *pd = c->overlay_data; + + if (px < pd->px || px > pd->px + pd->sx - 1) + return (1); + if (py < pd->py || py > pd->py + pd->sy - 1) + return (1); + return (0); +} + +static void +popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) +{ + struct popup_data *pd = c->overlay_data; + struct tty *tty = &c->tty; + struct screen s; + struct screen_write_ctx ctx; + u_int i, px = pd->px, py = pd->py; + + screen_init(&s, pd->sx, pd->sy, 0); + screen_write_start(&ctx, NULL, &s); + screen_write_clearscreen(&ctx, 8); + screen_write_box(&ctx, pd->sx, pd->sy); + screen_write_cursormove(&ctx, 1, 1, 0); + screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, pd->sy - 2); + screen_write_stop(&ctx); + + c->overlay_check = NULL; + for (i = 0; i < pd->sy; i++) + tty_draw_line(tty, NULL, &s, 0, i, pd->sx, px, py + i); + c->overlay_check = popup_check_cb; +} + +static void +popup_free_cb(struct client *c) +{ + struct popup_data *pd = c->overlay_data; + struct cmdq_item *item = pd->item; + u_int i; + + if (item != NULL) { + if (pd->ictx != NULL && + item->client != NULL && + item->client->session == NULL) + item->client->retval = pd->status; + cmdq_continue(item); + } + server_client_unref(pd->c); + + if (pd->job != NULL) + job_free(pd->job); + if (pd->ictx != NULL) + input_free(pd->ictx); + + for (i = 0; i < pd->nlines; i++) + free(pd->lines[i]); + free(pd->lines); + + screen_free(&pd->s); + free(pd->cmd); + free(pd); +} + +static void +popup_handle_drag(struct client *c, struct popup_data *pd, + struct mouse_event *m) +{ + u_int px, py; + + if (!MOUSE_DRAG(m->b)) + pd->dragging = OFF; + else if (pd->dragging == MOVE) { + if (m->x < pd->dx) + px = 0; + else if (m->x - pd->dx + pd->sx > c->tty.sx) + px = c->tty.sx - pd->sx; + else + px = m->x - pd->dx; + if (m->y < pd->dy) + py = 0; + else if (m->y - pd->dy + pd->sy > c->tty.sy) + py = c->tty.sy - pd->sy; + else + py = m->y - pd->dy; + pd->px = px; + pd->py = py; + pd->dx = m->x - pd->px; + pd->dy = m->y - pd->py; + server_redraw_client(c); + } else if (pd->dragging == SIZE) { + if (m->x < pd->px + 2) + return; + if (m->y < pd->py + 2) + return; + pd->sx = m->x - pd->px; + pd->sy = m->y - pd->py; + + screen_resize(&pd->s, pd->sx, pd->sy, 0); + if (pd->ictx == NULL) + popup_write_screen(c, pd); + else if (pd->job != NULL) + job_resize(pd->job, pd->sx - 2, pd->sy - 2); + server_redraw_client(c); + } +} + +static int +popup_key_cb(struct client *c, struct key_event *event) +{ + struct popup_data *pd = c->overlay_data; + struct mouse_event *m = &event->m; + struct cmd_find_state *fs = &pd->fs; + struct cmdq_item *new_item; + struct cmd_parse_result *pr; + struct format_tree *ft; + const char *cmd; + + if (KEYC_IS_MOUSE(event->key)) { + if (pd->dragging != OFF) { + popup_handle_drag(c, pd, m); + goto out; + } + if (m->x < pd->px || + m->x > pd->px + pd->sx - 1 || + m->y < pd->py || + m->y > pd->py + pd->sy - 1) { + if (MOUSE_BUTTONS (m->b) == 1) + return (1); + return (0); + } + if ((m->b & MOUSE_MASK_META) || + m->x == pd->px || + m->x == pd->px + pd->sx - 1 || + m->y == pd->py || + m->y == pd->py + pd->sy - 1) { + if (!MOUSE_DRAG(m->b)) + goto out; + if (MOUSE_BUTTONS(m->lb) == 0) + pd->dragging = MOVE; + else if (MOUSE_BUTTONS(m->lb) == 2) + pd->dragging = SIZE; + pd->dx = m->lx - pd->px; + pd->dy = m->ly - pd->py; + goto out; + } + } + + if (pd->ictx != NULL && (pd->flags & POPUP_WRITEKEYS)) { + if (KEYC_IS_MOUSE(event->key)) + return (0); + if ((~pd->flags & POPUP_CLOSEEXIT) && + (event->key == '\033' || event->key == '\003')) + return (1); + if (pd->job == NULL) + return (0); + input_key(NULL, &pd->s, job_get_event(pd->job), event->key); + return (0); + } + + if (pd->cmd == NULL) + return (1); + + ft = format_create(NULL, pd->item, FORMAT_NONE, 0); + if (cmd_find_valid_state(fs)) + format_defaults(ft, c, fs->s, fs->wl, fs->wp); + else + format_defaults(ft, c, NULL, NULL, NULL); + format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key)); + if (KEYC_IS_MOUSE(event->key)) { + format_add(ft, "popup_mouse", "1"); + format_add(ft, "popup_mouse_x", "%u", m->x - pd->px); + format_add(ft, "popup_mouse_y", "%u", m->y - pd->py); + } + cmd = format_expand(ft, pd->cmd); + format_free(ft); + + pr = cmd_parse_from_string(cmd, NULL); + switch (pr->status) { + case CMD_PARSE_EMPTY: + break; + case CMD_PARSE_ERROR: + new_item = cmdq_get_error(pr->error); + free(pr->error); + cmdq_append(c, new_item); + break; + case CMD_PARSE_SUCCESS: + if (pd->item != NULL) + m = &pd->item->shared->mouse; + else + m = NULL; + new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); + cmd_list_free(pr->cmdlist); + cmdq_append(c, new_item); + break; + } + return (1); + +out: + pd->lx = m->x; + pd->ly = m->y; + pd->lb = m->b; + return (0); +} + +static void +popup_job_update_cb(struct job *job) +{ + struct popup_data *pd = job_get_data(job); + struct evbuffer *evb = job_get_event(job)->input; + struct screen *s = &pd->s; + void *data = EVBUFFER_DATA(evb); + size_t size = EVBUFFER_LENGTH(evb); + + if (size != 0) { + input_parse_screen(pd->ictx, s, data, size); + evbuffer_drain(evb, size); + pd->c->flags |= CLIENT_REDRAWOVERLAY; + } +} + +static void +popup_job_complete_cb(struct job *job) +{ + struct popup_data *pd = job_get_data(job); + int status; + + status = job_get_status(pd->job); + if (WIFEXITED(status)) + pd->status = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + pd->status = WTERMSIG(status); + else + pd->status = 0; + pd->job = NULL; + + if (pd->flags & POPUP_CLOSEEXIT) + server_client_clear_overlay(pd->c); +} + +u_int +popup_width(struct cmdq_item *item, u_int nlines, const char **lines, + struct client *c, struct cmd_find_state *fs) +{ + char *copy, *next, *loop, *tmp; + struct format_tree *ft; + u_int i, width = 0, tmpwidth; + + ft = format_create(item->client, item, FORMAT_NONE, 0); + if (fs != NULL && cmd_find_valid_state(fs)) + format_defaults(ft, c, fs->s, fs->wl, fs->wp); + else + format_defaults(ft, c, NULL, NULL, NULL); + + for (i = 0; i < nlines; i++) { + copy = next = xstrdup(lines[i]); + while ((loop = strsep(&next, "\n")) != NULL) { + tmp = format_expand(ft, loop); + tmpwidth = format_width(tmp); + if (tmpwidth > width) + width = tmpwidth; + free(tmp); + } + } + free(copy); + + format_free(ft); + return (width); +} + +int +popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, + u_int sy, u_int nlines, const char **lines, const char *shellcmd, + const char *cmd, const char *cwd, struct client *c, + struct cmd_find_state *fs) +{ + struct popup_data *pd; + u_int i; + struct session *s; + int jobflags; + + if (sx < 3 || sy < 3) + return (-1); + if (c->tty.sx < sx || c->tty.sy < sy) + return (-1); + if (nlines > sy - 2) + nlines = sy - 2; + + pd = xcalloc(1, sizeof *pd); + pd->item = item; + pd->flags = flags; + + pd->c = c; + pd->c->references++; + + pd->status = 128 + SIGHUP; + + if (fs != NULL) + cmd_find_copy_state(&pd->fs, fs); + screen_init(&pd->s, sx - 2, sy - 2, 0); + + if (cmd != NULL) + pd->cmd = xstrdup(cmd); + + pd->px = px; + pd->py = py; + pd->sx = sx; + pd->sy = sy; + + pd->nlines = nlines; + if (pd->nlines != 0) + pd->lines = xreallocarray(NULL, pd->nlines, sizeof *pd->lines); + + for (i = 0; i < pd->nlines; i++) + pd->lines[i] = xstrdup(lines[i]); + popup_write_screen(c, pd); + + if (shellcmd != NULL) { + pd->ictx = input_init(NULL); + + if (fs != NULL) + s = fs->s; + else + s = NULL; + jobflags = JOB_NOWAIT|JOB_PTY; + if (flags & POPUP_WRITEKEYS) + jobflags |= JOB_KEEPWRITE; + pd->job = job_run(shellcmd, s, cwd, popup_job_update_cb, + popup_job_complete_cb, NULL, pd, jobflags, pd->sx - 2, + pd->sy - 2); + } + + server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, + popup_draw_cb, popup_key_cb, popup_free_cb, pd); + return (0); +} diff --git a/screen-redraw.c b/screen-redraw.c index e7f4f077..211f7f79 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -482,6 +482,8 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j, u_int type, x = ctx->ox + i, y = ctx->oy + j; int flag, pane_status = ctx->pane_status; + if (c->overlay_check != NULL && !c->overlay_check(c, x, y)) + return; type = screen_redraw_check_cell(c, x, y, pane_status, &wp); if (type == CELL_INSIDE) return; diff --git a/server-client.c b/server-client.c index f3a2b044..cd33b547 100644 --- a/server-client.c +++ b/server-client.c @@ -43,7 +43,6 @@ static void server_client_check_redraw(struct client *); static void server_client_set_title(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); -static void server_client_clear_overlay(struct client *); static void server_client_resize_event(int, short, void *); static void server_client_dispatch(struct imsg *, void *); @@ -81,8 +80,10 @@ server_client_overlay_timer(__unused int fd, __unused short events, void *data) /* Set an overlay on client. */ void -server_client_set_overlay(struct client *c, u_int delay, overlay_draw_cb drawcb, - overlay_key_cb keycb, overlay_free_cb freecb, void *data) +server_client_set_overlay(struct client *c, u_int delay, + overlay_check_cb checkcb, overlay_mode_cb modecb, + overlay_draw_cb drawcb, overlay_key_cb keycb, overlay_free_cb freecb, + void *data) { struct timeval tv; @@ -98,17 +99,21 @@ server_client_set_overlay(struct client *c, u_int delay, overlay_draw_cb drawcb, if (delay != 0) evtimer_add(&c->overlay_timer, &tv); + c->overlay_check = checkcb; + c->overlay_mode = modecb; c->overlay_draw = drawcb; c->overlay_key = keycb; c->overlay_free = freecb; c->overlay_data = data; - c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); + c->tty.flags |= TTY_FREEZE; + if (c->overlay_mode == NULL) + c->tty.flags |= TTY_NOCURSOR; server_redraw_client(c); } /* Clear overlay mode on client. */ -static void +void server_client_clear_overlay(struct client *c) { if (c->overlay_draw == NULL) @@ -120,8 +125,12 @@ server_client_clear_overlay(struct client *c) if (c->overlay_free != NULL) c->overlay_free(c); + c->overlay_check = NULL; + c->overlay_mode = NULL; c->overlay_draw = NULL; c->overlay_key = NULL; + c->overlay_free = NULL; + c->overlay_data = NULL; c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); server_redraw_client(c); @@ -1485,35 +1494,48 @@ server_client_reset_state(struct client *c) { struct window *w = c->session->curw->window; struct window_pane *wp = w->active, *loop; - struct screen *s = wp->screen; + struct screen *s; struct options *oo = c->session->options; int mode, cursor = 0; u_int cx = 0, cy = 0, ox, oy, sx, sy; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; - if (c->overlay_draw != NULL) - return; - mode = s->mode; + /* Get mode from overlay if any, else from screen. */ + if (c->overlay_draw != NULL) { + s = NULL; + if (c->overlay_mode == NULL) + mode = 0; + else + mode = c->overlay_mode(c, &cx, &cy); + } else { + s = wp->screen; + mode = s->mode; + } + log_debug("%s: client %s mode %x", __func__, c->name, mode); + + /* Reset region and margin. */ tty_region_off(&c->tty); tty_margin_off(&c->tty); /* Move cursor to pane cursor and offset. */ - cursor = 0; - tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); - if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx && - wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) { - cursor = 1; + if (c->overlay_draw == NULL) { + cursor = 0; + tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); + if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx && + wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) { + cursor = 1; - cx = wp->xoff + s->cx - ox; - cy = wp->yoff + s->cy - oy; + cx = wp->xoff + s->cx - ox; + cy = wp->yoff + s->cy - oy; - if (status_at_line(c) == 0) - cy += status_line_size(c); + if (status_at_line(c) == 0) + cy += status_line_size(c); + } + if (!cursor) + mode &= ~MODE_CURSOR; } - if (!cursor) - mode &= ~MODE_CURSOR; tty_cursor(&c->tty, cx, cy); /* @@ -1522,16 +1544,18 @@ server_client_reset_state(struct client *c) */ if (options_get_number(oo, "mouse")) { mode &= ~ALL_MOUSE_MODES; - TAILQ_FOREACH(loop, &w->panes, entry) { - if (loop->screen->mode & MODE_MOUSE_ALL) - mode |= MODE_MOUSE_ALL; + if (c->overlay_draw == NULL) { + TAILQ_FOREACH(loop, &w->panes, entry) { + if (loop->screen->mode & MODE_MOUSE_ALL) + mode |= MODE_MOUSE_ALL; + } } if (~mode & MODE_MOUSE_ALL) mode |= MODE_MOUSE_BUTTON; } /* Clear bracketed paste mode if at the prompt. */ - if (c->prompt_string != NULL) + if (c->overlay_draw == NULL && c->prompt_string != NULL) mode &= ~MODE_BRACKETPASTE; /* Set the terminal mode and reset attributes. */ diff --git a/tmux.1 b/tmux.1 index 8cd4841f..263bedc1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4382,7 +4382,10 @@ The following variables are available, where appropriate: .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" -.It Li "pid" Ta "" Ta "Server PID" +.It Li "pid" Ta "" Ta "Server PID" +.It Li "popup_key" Ta "" Ta "Key pressed in popup" +.It Li "popup_mouse_x" Ta "" Ta "Mouse X position in popup" +.It Li "popup_mouse_y" Ta "" Ta "Mouse Y position in popup" .It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated" .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" @@ -4972,6 +4975,92 @@ lists the format variables and their values. .Fl I forwards any input read from stdin to the empty pane given by .Ar target-pane . +.It Xo Ic display-popup +.Op Fl CEK +.Op Fl c Ar target-client +.Op Fl d Ar start-directory +.Op Fl h Ar height +.Op Fl R Ar shell-command +.Op Fl t Ar target-pane +.Op Fl w Ar width +.Op Fl x Ar position +.Op Fl y Ar position +.Op Ar command Ar line Ar ... +.Xc +.D1 (alias: Ic popup ) +Display a popup on +.Ar target-client . +A popup is a rectangular box drawn over the top of any panes. +Panes are not updated while a popup is present. +The popup content may be given in two ways: +.Bl -enum -offset Ds +.It +A set of lines as arguments. +Each line is a format which is expanded using +.Ar target-pane +as the target. +If a line contains newlines it is split into multiple lines. +Lines may use styles, see the +.Sx STYLES +section. +.It +A shell command given by +.Fl R +which is run and any output shown in the pane. +.El +.Pp +The first argument, +.Ar command , +is a +.Nm +command which is run when a key is pressed. +The key is available in the +.Ql popup_key +format. +After +.Ar command +is run, the popup is closed. +It may be empty to discard any key presses. +If +.Fl K +is given together with +.Fl R, +key presses are instead passed to the +.Fl R +shell command. +.Fl E +closes the popup automatically when +.Ar shell-command +exits. +With +.Fl K , +.Ql Escape +and +.Ql C-c +close the popup unless +.Fl E +is also given. +.Pp +.Fl x +and +.Fl y +give the position of the popup, they have the same meaning as for the +.Ic display-menu +command. +.Fl w +and +.Fl h +give the width and height - both may be a percentage (followed by +.Ql % ) . +If omitted, without +.Fl R +they are calculated from the given lines and with +.Fl R +they use half the terminal size. +.Pp +The +.Fl C +flag closes any popup on the client. .El .Sh BUFFERS .Nm diff --git a/tmux.h b/tmux.h index 424a0ddf..f885287b 100644 --- a/tmux.h +++ b/tmux.h @@ -1515,6 +1515,8 @@ RB_HEAD(client_files, client_file); /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); +typedef int (*overlay_check_cb)(struct client *, u_int, u_int); +typedef int (*overlay_mode_cb)(struct client *, u_int *, u_int *); typedef void (*overlay_draw_cb)(struct client *, struct screen_redraw_ctx *); typedef int (*overlay_key_cb)(struct client *, struct key_event *); typedef void (*overlay_free_cb)(struct client *); @@ -1630,6 +1632,8 @@ struct client { u_int pan_ox; u_int pan_oy; + overlay_check_cb overlay_check; + overlay_mode_cb overlay_mode; overlay_draw_cb overlay_draw; overlay_key_cb overlay_key; overlay_free_cb overlay_free; @@ -1934,6 +1938,7 @@ struct job *job_run(const char *, struct session *, const char *, job_update_cb, job_complete_cb, job_free_cb, void *, int, int, int); void job_free(struct job *); +void job_resize(struct job *, u_int, u_int); void job_check_died(pid_t, int); int job_get_status(struct job *); void *job_get_data(struct job *); @@ -2216,8 +2221,10 @@ void server_add_accept(int); /* server-client.c */ u_int server_client_how_many(void); -void server_client_set_overlay(struct client *, u_int, overlay_draw_cb, - overlay_key_cb, overlay_free_cb, void *); +void server_client_set_overlay(struct client *, u_int, overlay_check_cb, + overlay_mode_cb, overlay_draw_cb, overlay_key_cb, + overlay_free_cb, void *); +void server_client_clear_overlay(struct client *); void server_client_set_key_table(struct client *, const char *); const char *server_client_get_key_table(struct client *); int server_client_check_nested(struct client *); @@ -2751,6 +2758,15 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int, u_int, struct client *, struct cmd_find_state *, menu_choice_cb, void *); +/* popup.c */ +#define POPUP_WRITEKEYS 0x1 +#define POPUP_CLOSEEXIT 0x2 +u_int popup_width(struct cmdq_item *, u_int, const char **, + struct client *, struct cmd_find_state *); +int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, + u_int, u_int, const char **, const char *, const char *, + const char *, struct client *, struct cmd_find_state *); + /* style.c */ int style_parse(struct style *,const struct grid_cell *, const char *); diff --git a/tty.c b/tty.c index 862a1266..46ab1283 100644 --- a/tty.c +++ b/tty.c @@ -1250,6 +1250,16 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) return (&new); } +static int +tty_check_overlay(struct tty *tty, u_int px, u_int py) +{ + struct client *c = tty->client; + + if (c->overlay_check == NULL) + return (1); + return (c->overlay_check(c, px, py)); +} + void tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, u_int px, u_int py, u_int nx, u_int atx, u_int aty) @@ -1329,7 +1339,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, grid_view_get_cell(gd, px + i, py, &gc); gcp = tty_check_codeset(tty, &gc); if (len != 0 && - ((gcp->attr & GRID_ATTR_CHARSET) || + (!tty_check_overlay(tty, atx + ux + width, aty) || + (gcp->attr & GRID_ATTR_CHARSET) || gcp->flags != last.flags || gcp->attr != last.attr || gcp->fg != last.fg || @@ -1358,7 +1369,9 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, screen_select_cell(s, &last, gcp); else memcpy(&last, gcp, sizeof last); - if (ux + gcp->data.width > nx) { + if (!tty_check_overlay(tty, atx + ux, aty)) + ux += gcp->data.width; + else if (ux + gcp->data.width > nx) { tty_attributes(tty, &last, wp); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.width; j++) { @@ -1372,7 +1385,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.size; j++) tty_putc(tty, gcp->data.data[j]); - ux += gc.data.width; + ux += gcp->data.width; } else { memcpy(buf + len, gcp->data.data, gcp->data.size); len += gcp->data.size; From 55b14cdc6aa23870843dd31239e56e8816bf90ff Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Mar 2020 08:09:43 +0000 Subject: [PATCH 0067/1006] Add support for overlay popup boxes to show text or output temporarily above the normal layout. These work similarly to menus and are created with the display-popup command. From 75a93207d4cc9bb35c5c875267ce28e770a8b520 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 21 Mar 2020 13:51:42 +0000 Subject: [PATCH 0068/1006] Update capture-pane test, from Johannes Altmanninger. --- regress/capture-pane-sgr0.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/regress/capture-pane-sgr0.sh b/regress/capture-pane-sgr0.sh index 79d96a38..0dd9cd82 100644 --- a/regress/capture-pane-sgr0.sh +++ b/regress/capture-pane-sgr0.sh @@ -13,11 +13,18 @@ $TMUX kill-server 2>/dev/null TMP=$(mktemp) trap "rm -f $TMP" 0 1 15 -$TMUX -f/dev/null new -d \ - "printf '\033[31;42;1mabc\033[0;31mdef'; $TMUX capturep -peS0 -E0 >$TMP" +$TMUX -f/dev/null new -d " + printf '\033[31;42;1mabc\033[0;31mdef\n' + printf '\033[m\033[100m bright bg \033[m' + $TMUX capturep -peS0 -E1 >>$TMP" + + sleep 1 -printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\033[39m\n'| \ - cmp - $TMP || exit 1 + +( + printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\033[39m\n' + printf '\033[100m bright bg \033[49m\n' +) | cmp - $TMP || exit 1 $TMUX has 2>/dev/null && exit 1 From e0b17e796b52bfad7d867bc876a9826bf5761be4 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 28 Mar 2020 09:39:27 +0000 Subject: [PATCH 0069/1006] Add formats for top paste buffer by default. Also a tmux.1 fix from jmc. --- format.c | 6 ++++++ tmux.1 | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index da241c48..630e75d6 100644 --- a/format.c +++ b/format.c @@ -2421,6 +2421,8 @@ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp) { + struct paste_buffer *pb; + if (c != NULL && c->name != NULL) log_debug("%s: c=%s", __func__, c->name); else @@ -2460,6 +2462,10 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s, format_defaults_winlink(ft, wl); if (wp != NULL) format_defaults_pane(ft, wp); + + pb = paste_get_top (NULL); + if (pb != NULL) + format_defaults_paste_buffer(ft, pb); } /* Set default format keys for a session. */ diff --git a/tmux.1 b/tmux.1 index 263bedc1..aec7f79e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5024,7 +5024,7 @@ It may be empty to discard any key presses. If .Fl K is given together with -.Fl R, +.Fl R , key presses are instead passed to the .Fl R shell command. From 4346098e977d8fb3bde0783e9858f3def7bc01a5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 28 Mar 2020 09:39:44 +0000 Subject: [PATCH 0070/1006] Fix how popup height is calculated to take embedded newlines into account. --- cmd-display-menu.c | 6 +++++- popup.c | 20 +++++++++++++++++--- tmux.h | 1 + 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index aafe2447..e12a24ab 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -237,7 +237,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) } if (nlines != 0) - h = nlines + 2; + h = popup_height(nlines, lines) + 2; else h = c->tty.sy / 2; if (args_has(args, 'h')) { @@ -262,6 +262,10 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) } } + if (w > c->tty.sx - 1) + w = c->tty.sx - 1; + if (h > c->tty.sy - 1) + h = c->tty.sy - 1; cmd_display_menu_get_position(c, item, args, &px, &py, w, h); value = args_get(args, 'd'); diff --git a/popup.c b/popup.c index 16b7b260..f4afbd85 100644 --- a/popup.c +++ b/popup.c @@ -349,6 +349,22 @@ popup_job_complete_cb(struct job *job) server_client_clear_overlay(pd->c); } +u_int +popup_height(u_int nlines, const char **lines) +{ + char *copy, *next, *loop; + u_int i, height = 0; + + for (i = 0; i < nlines; i++) { + copy = next = xstrdup(lines[i]); + while ((loop = strsep(&next, "\n")) != NULL) + height++; + free(copy); + } + + return (height); +} + u_int popup_width(struct cmdq_item *item, u_int nlines, const char **lines, struct client *c, struct cmd_find_state *fs) @@ -372,8 +388,8 @@ popup_width(struct cmdq_item *item, u_int nlines, const char **lines, width = tmpwidth; free(tmp); } + free(copy); } - free(copy); format_free(ft); return (width); @@ -394,8 +410,6 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, return (-1); if (c->tty.sx < sx || c->tty.sy < sy) return (-1); - if (nlines > sy - 2) - nlines = sy - 2; pd = xcalloc(1, sizeof *pd); pd->item = item; diff --git a/tmux.h b/tmux.h index f885287b..bcec0a23 100644 --- a/tmux.h +++ b/tmux.h @@ -2763,6 +2763,7 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int, #define POPUP_CLOSEEXIT 0x2 u_int popup_width(struct cmdq_item *, u_int, const char **, struct client *, struct cmd_find_state *); +u_int popup_height(u_int, const char **); int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, u_int, u_int, const char **, const char *, const char *, const char *, struct client *, struct cmd_find_state *); From 593fddf84b6ae661df46597a22107babcc77efd5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 28 Mar 2020 09:51:12 +0000 Subject: [PATCH 0071/1006] Make two -E only close popup automatically if the command exited with 0. --- popup.c | 5 ++++- tmux.1 | 5 +++++ tmux.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/popup.c b/popup.c index f4afbd85..f2b9bd11 100644 --- a/popup.c +++ b/popup.c @@ -260,7 +260,8 @@ popup_key_cb(struct client *c, struct key_event *event) if (pd->ictx != NULL && (pd->flags & POPUP_WRITEKEYS)) { if (KEYC_IS_MOUSE(event->key)) return (0); - if ((~pd->flags & POPUP_CLOSEEXIT) && + if (((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0 || + pd->job == NULL) && (event->key == '\033' || event->key == '\003')) return (1); if (pd->job == NULL) @@ -347,6 +348,8 @@ popup_job_complete_cb(struct job *job) if (pd->flags & POPUP_CLOSEEXIT) server_client_clear_overlay(pd->c); + if ((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0) + server_client_clear_overlay(pd->c); } u_int diff --git a/tmux.1 b/tmux.1 index aec7f79e..5ee1a5aa 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5032,6 +5032,11 @@ shell command. closes the popup automatically when .Ar shell-command exits. +Two +.Fl E +closes the popup only if +.Ar shell-command +exited with success. With .Fl K , .Ql Escape diff --git a/tmux.h b/tmux.h index bcec0a23..a9f4de8a 100644 --- a/tmux.h +++ b/tmux.h @@ -2761,6 +2761,7 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int, /* popup.c */ #define POPUP_WRITEKEYS 0x1 #define POPUP_CLOSEEXIT 0x2 +#define POPUP_CLOSEEXITZERO 0x4 u_int popup_width(struct cmdq_item *, u_int, const char **, struct client *, struct cmd_find_state *); u_int popup_height(u_int, const char **); From 852a2f2e1f9cc941e2a01028ac191e834fb76c32 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 28 Mar 2020 09:51:12 +0000 Subject: [PATCH 0072/1006] Make two -E only close popup automatically if the command exited with 0. From 8036d0f834bbcce1a0eeff983750086354aec1e2 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 28 Mar 2020 09:55:30 +0000 Subject: [PATCH 0073/1006] Change default position for menu and popup to centre rather than top left. --- cmd-display-menu.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index e12a24ab..3e756116 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -73,12 +73,10 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, u_int ox, oy, sx, sy; xp = args_get(args, 'x'); - if (xp == NULL) - *px = 0; + if (xp == NULL || strcmp(xp, "C") == 0) + *px = (c->tty.sx - 1) / 2 - w / 2; else if (strcmp(xp, "R") == 0) *px = c->tty.sx - 1; - else if (strcmp(xp, "C") == 0) - *px = (c->tty.sx - 1) / 2 - w / 2; else if (strcmp(xp, "P") == 0) { tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); if (wp->xoff >= ox) @@ -111,9 +109,7 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, *px = c->tty.sx - w; yp = args_get(args, 'y'); - if (yp == NULL) - *py = 0; - else if (strcmp(yp, "C") == 0) + if (yp == NULL || strcmp(yp, "C") == 0) *py = (c->tty.sy - 1) / 2 + h / 2; else if (strcmp(yp, "P") == 0) { tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); @@ -280,7 +276,9 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'K')) flags |= POPUP_WRITEKEYS; - if (args_has(args, 'E')) + if (args_has(args, 'E') > 1) + flags |= POPUP_CLOSEEXITZERO; + else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, cmd, cwd, c, fs) != 0) From 6d0376a679f87fe0fdecd1086bf89bfa0653d416 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 28 Mar 2020 09:55:30 +0000 Subject: [PATCH 0074/1006] Change default position for menu and popup to centre rather than top left. From 586cafff0f2ce519eff97cda65dba77b2d45f84a Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 Mar 2020 07:42:44 +0000 Subject: [PATCH 0075/1006] Do not check flags after the popup struct has been freed. --- popup.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/popup.c b/popup.c index f2b9bd11..7eff7c14 100644 --- a/popup.c +++ b/popup.c @@ -346,9 +346,8 @@ popup_job_complete_cb(struct job *job) pd->status = 0; pd->job = NULL; - if (pd->flags & POPUP_CLOSEEXIT) - server_client_clear_overlay(pd->c); - if ((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0) + if ((pd->flags & POPUP_CLOSEEXIT) || + ((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0)) server_client_clear_overlay(pd->c); } From c713b65b9e11f8d07475d7f0565044365095029f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 Mar 2020 07:42:44 +0000 Subject: [PATCH 0076/1006] Do not check flags after the popup struct has been freed. From df633c527decb594ab7c675c79cb74fbf7414d11 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 30 Mar 2020 14:17:58 +0100 Subject: [PATCH 0077/1006] Add to CHANGES. --- CHANGES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index c938e28c..80c69d11 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,12 @@ CHANGES FROM 3.1 TO 3.2 +* Change default position for display-menu -x and -y to centre rather than top + left. + +* Add support for per-client popup windows, similar to menus. These are created + with new command display-popup. Popups may either show fixed text and trigger + a tmux command when a key is pressed, or run a program (-R flag). + * Change double and triple click bindings so that only one is fired (previously double click was fired on the way to triple click). Also add default double and triple click bindings to copy the word or line under the cursor and From a46916b45250ef21d22d7a1fc1bb7a89bd0a3645 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 30 Mar 2020 14:18:29 +0100 Subject: [PATCH 0078/1006] Tweak text. --- CHANGES | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 80c69d11..a825865d 100644 --- a/CHANGES +++ b/CHANGES @@ -3,9 +3,9 @@ CHANGES FROM 3.1 TO 3.2 * Change default position for display-menu -x and -y to centre rather than top left. -* Add support for per-client popup windows, similar to menus. These are created - with new command display-popup. Popups may either show fixed text and trigger - a tmux command when a key is pressed, or run a program (-R flag). +* Add support for per-client transient popups, similar to menus. These are + created with new command display-popup. Popups may either show fixed text and + trigger a tmux command when a key is pressed, or run a program (-R flag). * Change double and triple click bindings so that only one is fired (previously double click was fired on the way to triple click). Also add default double From 34de379c7d29b22c5486f32a265ff47425e4860a Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 Mar 2020 15:49:23 +0000 Subject: [PATCH 0079/1006] Add to rather than replace flags with -c. --- client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.c b/client.c index f819e924..37da9c67 100644 --- a/client.c +++ b/client.c @@ -251,7 +251,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Set up the initial command. */ if (shell_command != NULL) { msg = MSG_SHELL; - flags = CLIENT_STARTSERVER; + flags |= CLIENT_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; flags |= CLIENT_STARTSERVER; From 1fb504d0d5c73c1a0a3585e02e1e3114a3194625 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 Mar 2020 16:16:48 +0000 Subject: [PATCH 0080/1006] Tweak key numbers to avoid some special keys crossing over with modifier bits. --- tmux.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tmux.h b/tmux.h index a9f4de8a..e40dfe34 100644 --- a/tmux.h +++ b/tmux.h @@ -102,24 +102,24 @@ struct winlink; #define VISUAL_BOTH 2 /* Special key codes. */ -#define KEYC_NONE 0xffff00000000ULL -#define KEYC_UNKNOWN 0xfffe00000000ULL -#define KEYC_BASE 0x000010000000ULL -#define KEYC_USER 0x000020000000ULL +#define KEYC_NONE 0x00ff000000000ULL +#define KEYC_UNKNOWN 0x00fe000000000ULL +#define KEYC_BASE 0x0001000000000ULL +#define KEYC_USER 0x0002000000000ULL + +/* Key modifier bits. */ +#define KEYC_ESCAPE 0x0100000000000ULL +#define KEYC_CTRL 0x0200000000000ULL +#define KEYC_SHIFT 0x0400000000000ULL +#define KEYC_XTERM 0x0800000000000ULL +#define KEYC_LITERAL 0x1000000000000ULL /* Available user keys. */ #define KEYC_NUSER 1000 -/* Key modifier bits. */ -#define KEYC_ESCAPE 0x200000000000ULL -#define KEYC_CTRL 0x400000000000ULL -#define KEYC_SHIFT 0x800000000000ULL -#define KEYC_XTERM 0x1000000000000ULL -#define KEYC_LITERAL 0x2000000000000ULL - /* Mask to obtain key w/o modifiers. */ -#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM|KEYC_LITERAL) -#define KEYC_MASK_KEY (~KEYC_MASK_MOD) +#define KEYC_MASK_MOD 0xff00000000000ULL +#define KEYC_MASK_KEY 0x00fffffffffffULL /* Is this a mouse key? */ #define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ From eedf059d00365efd763cb9d99835c91d31320956 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 06:35:38 +0000 Subject: [PATCH 0081/1006] Detach reply escape sequences from the pane so they work in popups. --- popup.c | 3 +-- spawn.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/popup.c b/popup.c index 7eff7c14..05ff1b4c 100644 --- a/popup.c +++ b/popup.c @@ -443,8 +443,6 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, popup_write_screen(c, pd); if (shellcmd != NULL) { - pd->ictx = input_init(NULL); - if (fs != NULL) s = fs->s; else @@ -455,6 +453,7 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->job = job_run(shellcmd, s, cwd, popup_job_update_cb, popup_job_complete_cb, NULL, pd, jobflags, pd->sx - 2, pd->sy - 2); + pd->ictx = input_init(NULL, job_get_event(pd->job)); } server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, diff --git a/spawn.c b/spawn.c index 660ee47d..45243517 100644 --- a/spawn.c +++ b/spawn.c @@ -256,7 +256,7 @@ spawn_pane(struct spawn_context *sc, char **cause) window_pane_reset_mode_all(sc->wp0); screen_reinit(&sc->wp0->base); input_free(sc->wp0->ictx); - sc->wp0->ictx = input_init(sc->wp0); + sc->wp0->ictx = NULL; new_wp = sc->wp0; new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN); } else if (sc->lc == NULL) { From 3bbd66c0137fe95c348ce333ea7eec9507db7659 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 07:00:34 +0000 Subject: [PATCH 0082/1006] Move alternate screen into the screen rather than the pane. --- cmd-capture-pane.c | 2 +- format.c | 8 +++-- input.c | 34 ++++++++++-------- screen.c | 84 ++++++++++++++++++++++++++++++++++++++++++++ server-client.c | 2 +- tmux.h | 18 +++++----- window.c | 86 ++++------------------------------------------ 7 files changed, 127 insertions(+), 107 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 2c8dd920..ad6755ba 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -118,7 +118,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, sx = screen_size_x(&wp->base); if (args_has(args, 'a')) { - gd = wp->saved_grid; + gd = wp->base.saved_grid; if (gd == NULL) { if (!args_has(args, 'q')) { cmdq_error(item, "no alternate screen"); diff --git a/format.c b/format.c index 630e75d6..15347e18 100644 --- a/format.c +++ b/format.c @@ -2729,9 +2729,11 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); - format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); - format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); - format_add(ft, "alternate_saved_y", "%u", wp->saved_cy); + format_add(ft, "alternate_on", "%d", wp->base.saved_grid != NULL); + if (wp->base.saved_cx != UINT_MAX) + format_add(ft, "alternate_saved_x", "%u", wp->base.saved_cx); + if (wp->base.saved_cy != UINT_MAX) + format_add(ft, "alternate_saved_y", "%u", wp->base.saved_cy); format_add(ft, "cursor_flag", "%d", !!(wp->base.mode & MODE_CURSOR)); diff --git a/input.c b/input.c index c7d67b82..4901e886 100644 --- a/input.c +++ b/input.c @@ -75,6 +75,7 @@ struct input_param { /* Input parser context. */ struct input_ctx { struct window_pane *wp; + struct bufferevent *event; struct screen_write_ctx ctx; struct input_cell cell; @@ -788,12 +789,13 @@ input_restore_state(struct input_ctx *ictx) /* Initialise input parser. */ struct input_ctx * -input_init(struct window_pane *wp) +input_init(struct window_pane *wp, struct bufferevent *bev) { struct input_ctx *ictx; ictx = xcalloc(1, sizeof *ictx); ictx->wp = wp; + ictx->event = bev; ictx->input_space = INPUT_BUF_START; ictx->input_buf = xmalloc(INPUT_BUF_START); @@ -1058,18 +1060,15 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) static void input_reply(struct input_ctx *ictx, const char *fmt, ...) { - struct window_pane *wp = ictx->wp; + struct bufferevent *bev = ictx->event; va_list ap; char *reply; - if (wp == NULL) - return; - va_start(ap, fmt); xvasprintf(&reply, fmt, ap); va_end(ap); - bufferevent_write(wp->event, reply, strlen(reply)); + bufferevent_write(bev, reply, strlen(reply)); free(reply); } @@ -1680,10 +1679,14 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) case 1047: if (wp != NULL) window_pane_alternate_off(wp, gc, 0); + else + screen_alternate_off(sctx->s, gc, 0); break; case 1049: if (wp != NULL) window_pane_alternate_off(wp, gc, 1); + else + screen_alternate_off(sctx->s, gc, 1); break; case 2004: screen_write_mode_clear(sctx, MODE_BRACKETPASTE); @@ -1781,10 +1784,14 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) case 1047: if (wp != NULL) window_pane_alternate_on(wp, gc, 0); + else + screen_alternate_on(sctx->s, gc, 0); break; case 1049: if (wp != NULL) window_pane_alternate_on(wp, gc, 1); + else + screen_alternate_on(sctx->s, gc, 1); break; case 2004: screen_write_mode_set(sctx, MODE_BRACKETPASTE); @@ -1801,7 +1808,9 @@ static void input_csi_dispatch_winops(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; + struct screen *s = sctx->s; struct window_pane *wp = ictx->wp; + u_int x = screen_size_x(s), y = screen_size_y(s); int n, m; m = 0; @@ -1858,10 +1867,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx) } break; case 18: - if (wp != NULL) { - input_reply(ictx, "\033[8;%u;%ut", wp->sy, - wp->sx); - } + input_reply(ictx, "\033[8;%u;%ut", x, y); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -2565,13 +2571,13 @@ input_osc_52(struct input_ctx *ictx, const char *p) outlen = 0; out = NULL; } - bufferevent_write(wp->event, "\033]52;;", 6); + bufferevent_write(ictx->event, "\033]52;;", 6); if (outlen != 0) - bufferevent_write(wp->event, out, outlen); + bufferevent_write(ictx->event, out, outlen); if (ictx->input_end == INPUT_END_BEL) - bufferevent_write(wp->event, "\007", 1); + bufferevent_write(ictx->event, "\007", 1); else - bufferevent_write(wp->event, "\033\\", 2); + bufferevent_write(ictx->event, "\033\\", 2); free(out); return; } diff --git a/screen.c b/screen.c index b8acf33d..c3e5da70 100644 --- a/screen.c +++ b/screen.c @@ -76,6 +76,8 @@ void screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) { s->grid = grid_create(sx, sy, hlimit); + s->saved_grid = NULL; + s->title = xstrdup(""); s->titles = NULL; @@ -99,6 +101,11 @@ screen_reinit(struct screen *s) s->mode = MODE_CURSOR | MODE_WRAP; + if (s->saved_grid != NULL) + screen_alternate_off(s, NULL, 0); + s->saved_cx = UINT_MAX; + s->saved_cy = UINT_MAX; + screen_reset_tabs(s); grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8); @@ -116,6 +123,8 @@ screen_free(struct screen *s) free(s->title); free(s->ccolour); + if (s->saved_grid != NULL) + grid_destroy(s->saved_grid); grid_destroy(s->grid); screen_free_titles(s); @@ -502,3 +511,78 @@ screen_reflow(struct screen *s, u_int new_x) log_debug("%s: reflow took %llu.%06u seconds", __func__, (unsigned long long)tv.tv_sec, (u_int)tv.tv_usec); } + +/* + * Enter alternative screen mode. A copy of the visible screen is saved and the + * history is not updated. + */ +void +screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) +{ + u_int sx, sy; + + if (s->saved_grid != NULL) + return; + sx = screen_size_x(s); + sy = screen_size_y(s); + + s->saved_grid = grid_create(sx, sy, 0); + grid_duplicate_lines(s->saved_grid, 0, s->grid, screen_hsize(s), sy); + if (cursor) { + s->saved_cx = s->cx; + s->saved_cy = s->cy; + } + memcpy(&s->saved_cell, gc, sizeof s->saved_cell); + + grid_view_clear(s->grid, 0, 0, sx, sy, 8); + + s->grid->flags &= ~GRID_HISTORY; +} + +/* Exit alternate screen mode and restore the copied grid. */ +void +screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) +{ + u_int sx, sy; + + /* + * Restore the cursor position and cell. This happens even if not + * currently in the alternate screen. + */ + if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) { + s->cx = s->saved_cx; + if (s->cx > screen_size_x(s) - 1) + s->cx = screen_size_x(s) - 1; + s->cy = s->saved_cy; + if (s->cy > screen_size_y(s) - 1) + s->cy = screen_size_y(s) - 1; + if (gc != NULL) + memcpy(gc, &s->saved_cell, sizeof *gc); + } + + if (s->saved_grid == NULL) + return; + sx = screen_size_x(s); + sy = screen_size_y(s); + + /* + * If the current size is bigger, temporarily resize to the old size + * before copying back. + */ + if (sy > s->saved_grid->sy) + screen_resize(s, sx, s->saved_grid->sy, 1); + + /* Restore the saved grid. */ + grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, sy); + + /* + * Turn history back on (so resize can use it) and then resize back to + * the current size. + */ + s->grid->flags |= GRID_HISTORY; + if (sy > s->saved_grid->sy || sx != s->saved_grid->sx) + screen_resize(s, sx, sy, 1); + + grid_destroy(s->saved_grid); + s->saved_grid = NULL; +} diff --git a/server-client.c b/server-client.c index cd33b547..75e80c81 100644 --- a/server-client.c +++ b/server-client.c @@ -1400,7 +1400,7 @@ server_client_resize_event(__unused int fd, __unused short events, void *data) log_debug("%s: %%%u timer fired (was%s resized)", __func__, wp->id, (wp->flags & PANE_RESIZED) ? "" : " not"); - if (wp->saved_grid == NULL && (wp->flags & PANE_RESIZED)) { + if (wp->base.saved_grid == NULL && (wp->flags & PANE_RESIZED)) { log_debug("%s: %%%u deferring timer", __func__, wp->id); server_client_start_resize_timer(wp); } else if (!server_client_resize_force(wp)) { diff --git a/tmux.h b/tmux.h index e40dfe34..2e6ef0c1 100644 --- a/tmux.h +++ b/tmux.h @@ -754,8 +754,12 @@ struct screen { int mode; - bitstr_t *tabs; + u_int saved_cx; + u_int saved_cy; + struct grid *saved_grid; + struct grid_cell saved_cell; + bitstr_t *tabs; struct screen_sel *sel; }; @@ -917,12 +921,6 @@ struct window_pane { struct screen status_screen; size_t status_size; - /* Saved in alternative screen mode. */ - u_int saved_cx; - u_int saved_cy; - struct grid *saved_grid; - struct grid_cell saved_cell; - TAILQ_HEAD (, window_mode_entry) modes; struct event modetimer; time_t modelast; @@ -2296,7 +2294,7 @@ void recalculate_size(struct window *); void recalculate_sizes(void); /* input.c */ -struct input_ctx *input_init(struct window_pane *); +struct input_ctx *input_init(struct window_pane *, struct bufferevent *); void input_free(struct input_ctx *); void input_reset(struct input_ctx *, int); struct evbuffer *input_pending(struct input_ctx *); @@ -2334,6 +2332,7 @@ struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); int grid_compare(struct grid *, struct grid *); void grid_collect_history(struct grid *); +void grid_remove_history(struct grid *, u_int ); void grid_scroll_history(struct grid *, u_int); void grid_scroll_history_region(struct grid *, u_int, u_int, u_int); void grid_clear_history(struct grid *); @@ -2458,6 +2457,9 @@ void screen_hide_selection(struct screen *); int screen_check_selection(struct screen *, u_int, u_int); void screen_select_cell(struct screen *, struct grid_cell *, const struct grid_cell *); +void screen_alternate_on(struct screen *, struct grid_cell *, int); +void screen_alternate_off(struct screen *, struct grid_cell *, int); + /* window.c */ extern struct windows windows; diff --git a/window.c b/window.c index 754b9e59..4ae67916 100644 --- a/window.c +++ b/window.c @@ -879,10 +879,6 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->pipe_off = 0; wp->pipe_event = NULL; - wp->saved_grid = NULL; - wp->saved_cx = UINT_MAX; - wp->saved_cy = UINT_MAX; - screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; @@ -891,8 +887,6 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) if (gethostname(host, sizeof host) == 0) screen_set_title(&wp->base, host); - wp->ictx = input_init(wp); - return (wp); } @@ -906,14 +900,12 @@ window_pane_destroy(struct window_pane *wp) bufferevent_free(wp->event); close(wp->fd); } - - input_free(wp->ictx); + if (wp->ictx != NULL) + input_free(wp->ictx); screen_free(&wp->status_screen); screen_free(&wp->base); - if (wp->saved_grid != NULL) - grid_destroy(wp->saved_grid); if (wp->pipe_fd != -1) { bufferevent_free(wp->pipe_event); @@ -974,6 +966,7 @@ window_pane_set_event(struct window_pane *wp) wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, window_pane_error_callback, wp); + wp->ictx = input_init(wp, wp->event); bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); bufferevent_enable(wp->event, EV_READ|EV_WRITE); @@ -990,7 +983,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wp->sy = sy; log_debug("%s: %%%u resize %ux%u", __func__, wp->id, sx, sy); - screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL); + screen_resize(&wp->base, sx, sy, wp->base.saved_grid == NULL); wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) @@ -999,90 +992,23 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wp->flags |= (PANE_RESIZE|PANE_RESIZED); } -/* - * Enter alternative screen mode. A copy of the visible screen is saved and the - * history is not updated - */ void window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, int cursor) { - struct screen *s = &wp->base; - u_int sx, sy; - - if (wp->saved_grid != NULL) - return; if (!options_get_number(wp->options, "alternate-screen")) return; - sx = screen_size_x(s); - sy = screen_size_y(s); - - wp->saved_grid = grid_create(sx, sy, 0); - grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy); - if (cursor) { - wp->saved_cx = s->cx; - wp->saved_cy = s->cy; - } - memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell); - - grid_view_clear(s->grid, 0, 0, sx, sy, 8); - - wp->base.grid->flags &= ~GRID_HISTORY; - + screen_alternate_on(&wp->base, gc, cursor); wp->flags |= PANE_REDRAW; } -/* Exit alternate screen mode and restore the copied grid. */ void window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, int cursor) { - struct screen *s = &wp->base; - u_int sx, sy; - if (!options_get_number(wp->options, "alternate-screen")) return; - - /* - * Restore the cursor position and cell. This happens even if not - * currently in the alternate screen. - */ - if (cursor && wp->saved_cx != UINT_MAX && wp->saved_cy != UINT_MAX) { - s->cx = wp->saved_cx; - if (s->cx > screen_size_x(s) - 1) - s->cx = screen_size_x(s) - 1; - s->cy = wp->saved_cy; - if (s->cy > screen_size_y(s) - 1) - s->cy = screen_size_y(s) - 1; - memcpy(gc, &wp->saved_cell, sizeof *gc); - } - - if (wp->saved_grid == NULL) - return; - sx = screen_size_x(s); - sy = screen_size_y(s); - - /* - * If the current size is bigger, temporarily resize to the old size - * before copying back. - */ - if (sy > wp->saved_grid->sy) - screen_resize(s, sx, wp->saved_grid->sy, 1); - - /* Restore the saved grid. */ - grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); - - /* - * Turn history back on (so resize can use it) and then resize back to - * the current size. - */ - wp->base.grid->flags |= GRID_HISTORY; - if (sy > wp->saved_grid->sy || sx != wp->saved_grid->sx) - screen_resize(s, sx, sy, 1); - - grid_destroy(wp->saved_grid); - wp->saved_grid = NULL; - + screen_alternate_off(&wp->base, gc, cursor); wp->flags |= PANE_REDRAW; } From 01b3bb8e2c695a7a32a332758e5a8f4a650fc98c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 11:38:35 +0000 Subject: [PATCH 0083/1006] Add a "second click" key type which is fired for the second click of a double click, even if the timer hasn't expired to confirm it isn't actually a triple click. Provides a way for people who don't care about triple clicks or can make their commands have no side effects to avoid the double click timer delay. --- key-string.c | 3 +++ server-client.c | 63 ++++++++++++++++++++++++++++++++++++++++++------- tmux.1 | 9 +++++++ tmux.h | 3 +++ 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/key-string.c b/key-string.c index 38e5b8a7..76ee4fbe 100644 --- a/key-string.c +++ b/key-string.c @@ -100,6 +100,9 @@ static const struct { KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3), KEYC_MOUSE_STRING(WHEELUP, WheelUp), KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), + KEYC_MOUSE_STRING(SECONDCLICK1, SecondClick1), + KEYC_MOUSE_STRING(SECONDCLICK2, SecondClick2), + KEYC_MOUSE_STRING(SECONDCLICK3, SecondClick3), KEYC_MOUSE_STRING(DOUBLECLICK1, DoubleClick1), KEYC_MOUSE_STRING(DOUBLECLICK2, DoubleClick2), KEYC_MOUSE_STRING(DOUBLECLICK3, DoubleClick3), diff --git a/server-client.c b/server-client.c index 75e80c81..aa174d4d 100644 --- a/server-client.c +++ b/server-client.c @@ -440,6 +440,7 @@ server_client_check_mouse(struct client *c, struct key_event *event) UP, DRAG, WHEEL, + SECOND, DOUBLE, TRIPLE } type = NOTYPE; enum { NOWHERE, @@ -493,9 +494,10 @@ server_client_check_mouse(struct client *c, struct key_event *event) evtimer_del(&c->click_timer); c->flags &= ~CLIENT_DOUBLECLICK; if (m->b == c->click_button) { - type = NOTYPE; + type = SECOND; + x = m->x, y = m->y, b = m->b; + log_debug("second-click at %u,%u", x, y); c->flags |= CLIENT_TRIPLECLICK; - goto add_timer; } } else if (c->flags & CLIENT_TRIPLECLICK) { evtimer_del(&c->click_timer); @@ -507,13 +509,12 @@ server_client_check_mouse(struct client *c, struct key_event *event) ignore = 1; goto have_event; } - } else + } else { + type = DOWN; + x = m->x, y = m->y, b = m->b; + log_debug("down at %u,%u", x, y); c->flags |= CLIENT_DOUBLECLICK; - - add_timer: - type = DOWN; - x = m->x, y = m->y, b = m->b; - log_debug("down at %u,%u", x, y); + } if (KEYC_CLICK_TIMEOUT != 0) { memcpy(&c->click_event, m, sizeof c->click_event); @@ -877,6 +878,52 @@ have_event: break; } break; + case SECOND: + switch (MOUSE_BUTTONS(b)) { + case 0: + if (where == PANE) + key = KEYC_SECONDCLICK1_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK1_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK1_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK1_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK1_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK1_BORDER; + break; + case 1: + if (where == PANE) + key = KEYC_SECONDCLICK2_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK2_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK2_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK2_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK2_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK2_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_SECONDCLICK3_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK3_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK3_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK3_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK3_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK3_BORDER; + break; + } + break; case DOUBLE: switch (MOUSE_BUTTONS(b)) { case 0: diff --git a/tmux.1 b/tmux.1 index 5ee1a5aa..e370b4b1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3982,10 +3982,19 @@ The following mouse events are available: .It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1" .It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2" .It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3" +.It Li "SecondClick1" Ta "SecondClick2" Ta "SecondClick3" .It Li "DoubleClick1" Ta "DoubleClick2" Ta "DoubleClick3" .It Li "TripleClick1" Ta "TripleClick2" Ta "TripleClick3" .El .Pp +The +.Ql SecondClick +events are fired for the second click of a double click, even if there may be a +third click which will fire +.Ql TripleClick +instead of +.Ql DoubleClick . +.Pp Each should be suffixed with a location, for example .Ql MouseDown1Status . .Pp diff --git a/tmux.h b/tmux.h index 2e6ef0c1..3023376c 100644 --- a/tmux.h +++ b/tmux.h @@ -182,6 +182,9 @@ enum { KEYC_MOUSE_KEY(MOUSEDRAGEND3), KEYC_MOUSE_KEY(WHEELUP), KEYC_MOUSE_KEY(WHEELDOWN), + KEYC_MOUSE_KEY(SECONDCLICK1), + KEYC_MOUSE_KEY(SECONDCLICK2), + KEYC_MOUSE_KEY(SECONDCLICK3), KEYC_MOUSE_KEY(DOUBLECLICK1), KEYC_MOUSE_KEY(DOUBLECLICK2), KEYC_MOUSE_KEY(DOUBLECLICK3), From 0dd4977d5c7a74e3d347076bf6db43e6f2281209 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 11:38:35 +0000 Subject: [PATCH 0084/1006] Add a "second click" key type which is fired for the second click of a double click, even if the timer hasn't expired to confirm it isn't actually a triple click. Provides a way for people who don't care about triple clicks or can make their commands have no side effects to avoid the double click timer delay. --- tmux.1 | 1 - 1 file changed, 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 667242db..e370b4b1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4422,7 +4422,6 @@ The following variables are available, where appropriate: .It Li "session_last_attached" Ta "" Ta "Time session last attached" .It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" .It Li "session_name" Ta "#S" Ta "Name of session" -.It Li "session_path" Ta "" Ta "Working directory of session" .It Li "session_stack" Ta "" Ta "Window indexes in most recent order" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" Ta "Server socket path" From 2a4714e76b3a85b3391b05413f36623bcb1493f9 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 11:58:05 +0000 Subject: [PATCH 0085/1006] Add session_path from Chris Ruegge in GitHub issue 2142. --- format.c | 1 + tmux.1 | 1 + 2 files changed, 2 insertions(+) diff --git a/format.c b/format.c index 15347e18..815be8da 100644 --- a/format.c +++ b/format.c @@ -2477,6 +2477,7 @@ format_defaults_session(struct format_tree *ft, struct session *s) ft->s = s; format_add(ft, "session_name", "%s", s->name); + format_add(ft, "session_path", "%s", s->cwd); format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); format_add(ft, "session_id", "$%u", s->id); diff --git a/tmux.1 b/tmux.1 index e370b4b1..667242db 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4422,6 +4422,7 @@ The following variables are available, where appropriate: .It Li "session_last_attached" Ta "" Ta "Time session last attached" .It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" .It Li "session_name" Ta "#S" Ta "Name of session" +.It Li "session_path" Ta "" Ta "Working directory of session" .It Li "session_stack" Ta "" Ta "Window indexes in most recent order" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" Ta "Server socket path" From 2ca95840d16e2292d3560ec30f4f24dc9e2eedb2 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 11:58:05 +0000 Subject: [PATCH 0086/1006] Add session_path from Chris Ruegge in GitHub issue 2142. --- tmux.1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.1 b/tmux.1 index e370b4b1..667242db 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4422,6 +4422,7 @@ The following variables are available, where appropriate: .It Li "session_last_attached" Ta "" Ta "Time session last attached" .It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" .It Li "session_name" Ta "#S" Ta "Name of session" +.It Li "session_path" Ta "" Ta "Working directory of session" .It Li "session_stack" Ta "" Ta "Window indexes in most recent order" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" Ta "Server socket path" From 2624edde46ad13995d4dd33f7d61fce74ac8f1ac Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 16:53:23 +0000 Subject: [PATCH 0087/1006] Add non-regex search variants to avoid the performance cost for people with large histories or long lines. --- tmux.1 | 24 ++++++++- tmux.h | 2 + window-copy.c | 135 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 116 insertions(+), 45 deletions(-) diff --git a/tmux.1 b/tmux.1 index 667242db..a3b2b0f8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1503,9 +1503,11 @@ The following commands are supported in copy mode: .It Li "scroll-up" Ta "C-y" Ta "C-Up" .It Li "search-again" Ta "n" Ta "n" .It Li "search-backward " Ta "?" Ta "" -.It Li "search-forward " Ta "/" Ta "" .It Li "search-backward-incremental " Ta "" Ta "C-r" +.It Li "search-backward-text " Ta "" Ta "" +.It Li "search-forward " Ta "/" Ta "" .It Li "search-forward-incremental " Ta "" Ta "C-s" +.It Li "search-forward-text " Ta "" Ta "" .It Li "search-reverse" Ta "N" Ta "N" .It Li "select-line" Ta "V" Ta "" .It Li "select-word" Ta "" Ta "" @@ -1514,6 +1516,26 @@ The following commands are supported in copy mode: .It Li "top-line" Ta "H" Ta "M-R" .El .Pp +The search commands come in several varieties: +.Ql search-forward +and +.Ql search-backward +search for a regular expression; +the +.Ql -text +variants search for a plain text string rather than a regular expression; +.Ql -incremental +perform an incremental search and expect to be used with the +.Fl i +flag to the +.Ic command-prompt +command. +.Ql search-again +repeats the last search and +.Ql search-reverse +does the same but reverses the direction (forward becomes backward and backward +becomes forward). +.Pp Copy commands may take an optional buffer prefix argument which is used to generate the buffer name (the default is .Ql buffer diff --git a/tmux.h b/tmux.h index 3023376c..20559144 100644 --- a/tmux.h +++ b/tmux.h @@ -927,7 +927,9 @@ struct window_pane { TAILQ_HEAD (, window_mode_entry) modes; struct event modetimer; time_t modelast; + char *searchstr; + int searchregex; TAILQ_ENTRY(window_pane) entry; RB_ENTRY(window_pane) tree_entry; diff --git a/window-copy.c b/window-copy.c index eeed210d..fa2d2929 100644 --- a/window-copy.c +++ b/window-copy.c @@ -256,6 +256,7 @@ struct window_copy_mode_data { u_int lastsx; /* size of last line w/ content */ int searchtype; + int searchregex; char *searchstr; bitstr_t *searchmark; u_int searchcount; @@ -310,9 +311,11 @@ window_copy_common_init(struct window_mode_entry *wme) if (wp->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = wp->searchregex; data->searchstr = xstrdup(wp->searchstr); } else { data->searchtype = WINDOW_COPY_OFF; + data->searchregex = 0; data->searchstr = NULL; } data->searchmark = NULL; @@ -700,6 +703,35 @@ window_copy_key_table(struct window_mode_entry *wme) return ("copy-mode"); } +static int +window_copy_expand_search_string(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + const char *argument; + char *expanded; + + if (cs->args->argc == 2) { + argument = cs->args->argv[1]; + if (*argument != '\0') { + if (args_has(cs->args, 'F')) { + expanded = format_single(NULL, argument, NULL, + NULL, NULL, wme->wp); + if (*expanded == '\0') { + free(expanded); + return (0); + } + free(data->searchstr); + data->searchstr = expanded; + } else { + free(data->searchstr); + data->searchstr = xstrdup(argument); + } + } + } + return (1); +} + static enum window_copy_cmd_action window_copy_cmd_append_selection(struct window_copy_cmd_state *cs) { @@ -1525,10 +1557,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, 1); + window_copy_search_up(wme, data->searchregex); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_down(wme, 1); + window_copy_search_down(wme, data->searchregex); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1542,10 +1574,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, 1); + window_copy_search_down(wme, data->searchregex); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_up(wme, 1); + window_copy_search_up(wme, data->searchregex); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1765,70 +1797,76 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument; - char *expanded; - if (cs->args->argc == 2) { - argument = cs->args->argv[1]; - if (*argument != '\0') { - if (args_has(cs->args, 'F')) { - expanded = format_single(NULL, argument, NULL, - NULL, NULL, wme->wp); - if (*expanded == '\0') { - free(expanded); - return (WINDOW_COPY_CMD_NOTHING); - } - free(data->searchstr); - data->searchstr = expanded; - } else { - free(data->searchstr); - data->searchstr = xstrdup(argument); - } - } - } + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 1; for (; np != 0; np--) window_copy_search_up(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + + if (data->searchstr != NULL) { + data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 0; + for (; np != 0; np--) + window_copy_search_up(wme, 0); + } + return (WINDOW_COPY_CMD_NOTHING); +} + static enum window_copy_cmd_action window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument; - char *expanded; - if (cs->args->argc == 2) { - argument = cs->args->argv[1]; - if (*argument != '\0') { - if (args_has(cs->args, 'F')) { - expanded = format_single(NULL, argument, NULL, - NULL, NULL, wme->wp); - if (*expanded == '\0') { - free(expanded); - return (WINDOW_COPY_CMD_NOTHING); - } - free(data->searchstr); - data->searchstr = expanded; - } else { - free(data->searchstr); - data->searchstr = xstrdup(argument); - } - } - } + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 1; for (; np != 0; np--) window_copy_search_down(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + + if (data->searchstr != NULL) { + data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 0; + for (; np != 0; np--) + window_copy_search_down(wme, 0); + } + return (WINDOW_COPY_CMD_NOTHING); +} + static enum window_copy_cmd_action window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) { @@ -1858,6 +1896,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) case '=': case '-': data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_up(wme, 0)) { @@ -1867,6 +1906,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) break; case '+': data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_down(wme, 0)) { @@ -1907,6 +1947,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) case '=': case '+': data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_down(wme, 0)) { @@ -1916,6 +1957,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) break; case '-': data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_up(wme, 0)) { @@ -2041,10 +2083,14 @@ static const struct { window_copy_cmd_search_again }, { "search-backward", 0, 1, 0, window_copy_cmd_search_backward }, + { "search-backward-text", 0, 1, 0, + window_copy_cmd_search_backward_text }, { "search-backward-incremental", 1, 1, 0, window_copy_cmd_search_backward_incremental }, { "search-forward", 0, 1, 0, window_copy_cmd_search_forward }, + { "search-forward-text", 0, 1, 0, + window_copy_cmd_search_forward_text }, { "search-forward-incremental", 1, 1, 0, window_copy_cmd_search_forward_incremental }, { "search-reverse", 0, 0, 0, @@ -2653,6 +2699,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) free(wp->searchstr); wp->searchstr = xstrdup(data->searchstr); + wp->searchregex = regex; fx = data->cx; fy = screen_hsize(data->backing) - data->oy + data->cy; From e6d1b6770c14e401fc131e9bfa5a5d2068b9211f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 16:53:23 +0000 Subject: [PATCH 0088/1006] Add non-regex search variants to avoid the performance cost for people with large histories or long lines. --- tmux.1 | 31 +++---------------------------- tmux.h | 9 +++------ 2 files changed, 6 insertions(+), 34 deletions(-) diff --git a/tmux.1 b/tmux.1 index e785cf8b..a3b2b0f8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -565,18 +565,6 @@ Environment variables may be set by using the syntax for example .Ql HOME=/home/user . Variables set during parsing are added to the global environment. -A hidden variable may be set with -.Ql %hidden , -for example: -.Bd -literal -offset indent -%hidden MYVAR=42 -.Ed -.Pp -Hidden variables are not passed to the environment of processes created -by tmux. -See the -.Sx GLOBAL AND SESSION ENVIRONMENT -section. .Pp Commands may be parsed conditionally by surrounding them with .Ql %if , @@ -2273,7 +2261,7 @@ Rename the current window, or the window at if specified, to .Ar new-name . .It Xo Ic resize-pane -.Op Fl DLMRTUZ +.Op Fl DLMRUZ .Op Fl t Ar target-pane .Op Fl x Ar width .Op Fl y Ar height @@ -2312,9 +2300,6 @@ and unzoomed (its normal position in the layout). .Fl M begins mouse resizing (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . -.Pp T -trims all lines below the current cursor position and moves lines out of the -history to replace them. .It Xo Ic resize-window .Op Fl aADLRU .Op Fl t Ar target-window @@ -4723,16 +4708,10 @@ from inside, and the variable with the correct terminal setting of .Ql screen . .Pp -Variables in both session and global environments may be marked as hidden. -Hidden variables are not passed into the environment of new processes and -instead can only be used by tmux itself (for example in formats, see the -.Sx FORMATS -section). -.Pp Commands to alter and view the environment are: .Bl -tag -width Ds .It Xo Ic set-environment -.Op Fl hgru +.Op Fl gru .Op Fl t Ar target-session .Ar name Op Ar value .Xc @@ -4749,10 +4728,8 @@ flag unsets a variable. .Fl r indicates the variable is to be removed from the environment before starting a new process. -.Fl h -marks the variable as hidden. .It Xo Ic show-environment -.Op Fl hgs +.Op Fl gs .Op Fl t Ar target-session .Op Ar variable .Xc @@ -4769,8 +4746,6 @@ Variables removed from the environment are prefixed with If .Fl s is used, the output is formatted as a set of Bourne shell commands. -.Fl h -shows hidden variables (omitted by default). .El .Sh STATUS LINE .Nm diff --git a/tmux.h b/tmux.h index 53bbc222..20559144 100644 --- a/tmux.h +++ b/tmux.h @@ -1048,9 +1048,6 @@ struct environ_entry { char *name; char *value; - int flags; -#define ENVIRON_HIDDEN 0x1 - RB_ENTRY(environ_entry) entry; }; @@ -1960,10 +1957,10 @@ struct environ_entry *environ_first(struct environ *); struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); -void printflike(4, 5) environ_set(struct environ *, const char *, int, - const char *, ...); +void printflike(3, 4) environ_set(struct environ *, const char *, const char *, + ...); void environ_clear(struct environ *, const char *); -void environ_put(struct environ *, const char *, int); +void environ_put(struct environ *, const char *); void environ_unset(struct environ *, const char *); void environ_update(struct options *, struct environ *, struct environ *); void environ_push(struct environ *); From e6cddcf752b335cb945bba4619b500b527cfee0a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 17:13:20 +0000 Subject: [PATCH 0089/1006] Add a -T flag to resize-pane to trim lines below the cursor, moving lines out of the history. GitHub issue 2134. --- cmd-resize-pane.c | 17 +++++++++++++++-- grid.c | 13 +++++++++++++ tmux.1 | 5 ++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index b0aa2756..fd303cc1 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -36,8 +36,8 @@ const struct cmd_entry cmd_resize_pane_entry = { .name = "resize-pane", .alias = "resizep", - .args = { "DLMRt:Ux:y:Z", 0, 1 }, - .usage = "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " + .args = { "DLMRTt:Ux:y:Z", 0, 1 }, + .usage = "[-DLMRTUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " "[adjustment]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -60,6 +60,19 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) char *cause; u_int adjust; int x, y; + struct grid *gd = wp->base.grid; + + if (args_has(args, 'T')) { + if (!TAILQ_EMPTY(&wp->modes)) + return (CMD_RETURN_NORMAL); + adjust = screen_size_y(&wp->base) - 1 - wp->base.cy; + if (adjust > gd->hsize) + adjust = gd->hsize; + grid_remove_history(gd, adjust); + wp->base.cy += adjust; + wp->flags |= PANE_REDRAW; + return (CMD_RETURN_NORMAL); + } if (args_has(args, 'M')) { if (cmd_mouse_window(&shared->mouse, &s) == NULL) diff --git a/grid.c b/grid.c index 29943e6b..9678bf59 100644 --- a/grid.c +++ b/grid.c @@ -351,6 +351,19 @@ grid_collect_history(struct grid *gd) gd->hscrolled = gd->hsize; } +/* Remove lines from the bottom of the history. */ +void +grid_remove_history(struct grid *gd, u_int ny) +{ + u_int yy; + + if (ny > gd->hsize) + return; + for (yy = 0; yy < ny; yy++) + grid_free_line(gd, gd->hsize + gd->sy - 1 - yy); + gd->hsize -= ny; +} + /* * Scroll the entire visible screen, moving one line into the history. Just * allocate a new line at the bottom and move the history size indicator. diff --git a/tmux.1 b/tmux.1 index a3b2b0f8..b0fc5600 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2261,7 +2261,7 @@ Rename the current window, or the window at if specified, to .Ar new-name . .It Xo Ic resize-pane -.Op Fl DLMRUZ +.Op Fl DLMRTUZ .Op Fl t Ar target-pane .Op Fl x Ar width .Op Fl y Ar height @@ -2300,6 +2300,9 @@ and unzoomed (its normal position in the layout). .Fl M begins mouse resizing (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . +.Pp T +trims all lines below the current cursor position and moves lines out of the +history to replace them. .It Xo Ic resize-window .Op Fl aADLRU .Op Fl t Ar target-window From e221ef203c8d21e6ef3f6847c076c915ba1d9788 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 17:13:20 +0000 Subject: [PATCH 0090/1006] Add a -T flag to resize-pane to trim lines below the cursor, moving lines out of the history. GitHub issue 2134. --- tmux.1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index a3b2b0f8..b0fc5600 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2261,7 +2261,7 @@ Rename the current window, or the window at if specified, to .Ar new-name . .It Xo Ic resize-pane -.Op Fl DLMRUZ +.Op Fl DLMRTUZ .Op Fl t Ar target-pane .Op Fl x Ar width .Op Fl y Ar height @@ -2300,6 +2300,9 @@ and unzoomed (its normal position in the layout). .Fl M begins mouse resizing (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . +.Pp T +trims all lines below the current cursor position and moves lines out of the +history to replace them. .It Xo Ic resize-window .Op Fl aADLRU .Op Fl t Ar target-window From cc8b41f294974cdfb1ddfe3b907da58374ff130f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 17:14:40 +0000 Subject: [PATCH 0091/1006] Add a way to mark environment variables as "hidden" so they can be used by tmux but are not passed into the environment of new panes. --- cmd-new-window.c | 2 +- cmd-parse.y | 23 ++++++++++++++++++++++- cmd-respawn-pane.c | 2 +- cmd-respawn-window.c | 2 +- cmd-set-environment.c | 9 ++++++--- cmd-show-environment.c | 12 +++++++++--- cmd-split-window.c | 2 +- environ.c | 29 +++++++++++++++++++---------- server-client.c | 3 ++- spawn.c | 8 ++++---- tmux.1 | 26 ++++++++++++++++++++++++-- tmux.c | 4 ++-- tmux.h | 9 ++++++--- 13 files changed, 98 insertions(+), 33 deletions(-) diff --git a/cmd-new-window.c b/cmd-new-window.c index 9a1025e9..a4d6c014 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -81,7 +81,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) add = args_first_value(args, 'e', &value); while (add != NULL) { - environ_put(sc.environ, add); + environ_put(sc.environ, add, 0); add = args_next_value(&value); } diff --git a/cmd-parse.y b/cmd-parse.y index 2375370b..aabfe2d3 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -99,6 +99,7 @@ static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, } %token ERROR +%token HIDDEN %token IF %token ELSE %token ELIF @@ -134,6 +135,11 @@ statements : statement '\n' } statement : /* empty */ + { + $$ = xmalloc (sizeof *$$); + TAILQ_INIT($$); + } + | hidden_assignment { $$ = xmalloc (sizeof *$$); TAILQ_INIT($$); @@ -204,10 +210,21 @@ assignment : EQUALS if ((~flags & CMD_PARSE_PARSEONLY) && (ps->scope == NULL || ps->scope->flag)) - environ_put(global_environ, $1); + environ_put(global_environ, $1, 0); free($1); } +hidden_assignment : HIDDEN EQUALS + { + struct cmd_parse_state *ps = &parse_state; + int flags = ps->input->flags; + + if ((~flags & CMD_PARSE_PARSEONLY) && + (ps->scope == NULL || ps->scope->flag)) + environ_put(global_environ, $2, ENVIRON_HIDDEN); + free($2); + } + if_open : IF expanded { struct cmd_parse_state *ps = &parse_state; @@ -1079,6 +1096,10 @@ yylex(void) if (*cp == '\0') return (TOKEN); ps->condition = 1; + if (strcmp(yylval.token, "%hidden") == 0) { + free(yylval.token); + return (HIDDEN); + } if (strcmp(yylval.token, "%if") == 0) { free(yylval.token); return (IF); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 5e23fa15..55544f93 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -71,7 +71,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) add = args_first_value(args, 'e', &value); while (add != NULL) { - environ_put(sc.environ, add); + environ_put(sc.environ, add, 0); add = args_next_value(&value); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 497e401e..5f44db12 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -68,7 +68,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) add = args_first_value(args, 'e', &value); while (add != NULL) { - environ_put(sc.environ, add); + environ_put(sc.environ, add, 0); add = args_next_value(&value); } diff --git a/cmd-set-environment.c b/cmd-set-environment.c index a80acd01..248f734a 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 = { "grt:u", 1, 2 }, - .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", + .args = { "hgrt:u", 1, 2 }, + .usage = "[-hgru] " CMD_TARGET_SESSION_USAGE " name [value]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -96,7 +96,10 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "no value specified"); return (CMD_RETURN_ERROR); } - environ_set(env, name, "%s", value); + 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); diff --git a/cmd-show-environment.c b/cmd-show-environment.c index eb19cf20..0d2f7dd9 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -38,8 +38,8 @@ const struct cmd_entry cmd_show_environment_entry = { .name = "show-environment", .alias = "showenv", - .args = { "gst:", 0, 1 }, - .usage = "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", + .args = { "hgst:", 0, 1 }, + .usage = "[-hgs] " CMD_TARGET_SESSION_USAGE " [name]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -69,7 +69,13 @@ static void cmd_show_environment_print(struct cmd *self, struct cmdq_item *item, struct environ_entry *envent) { - char *escaped; + struct args *args = self->args; + char *escaped; + + if (!args_has(args, 'h') && (envent->flags & ENVIRON_HIDDEN)) + return; + if (args_has(args, 'h') && (~envent->flags & ENVIRON_HIDDEN)) + return; if (!args_has(self->args, 's')) { if (envent->value != NULL) diff --git a/cmd-split-window.c b/cmd-split-window.c index e8c2050e..c1c8be25 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -142,7 +142,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) add = args_first_value(args, 'e', &value); while (add != NULL) { - environ_put(sc.environ, add); + environ_put(sc.environ, add, 0); add = args_next_value(&value); } diff --git a/environ.c b/environ.c index 25968f7b..0ce717df 100644 --- a/environ.c +++ b/environ.c @@ -86,8 +86,10 @@ environ_copy(struct environ *srcenv, struct environ *dstenv) RB_FOREACH(envent, environ, srcenv) { if (envent->value == NULL) environ_clear(dstenv, envent->name); - else - environ_set(dstenv, envent->name, "%s", envent->value); + else { + environ_set(dstenv, envent->name, envent->flags, + "%s", envent->value); + } } } @@ -103,18 +105,21 @@ environ_find(struct environ *env, const char *name) /* Set an environment variable. */ void -environ_set(struct environ *env, const char *name, const char *fmt, ...) +environ_set(struct environ *env, const char *name, int flags, const char *fmt, + ...) { struct environ_entry *envent; va_list ap; va_start(ap, fmt); if ((envent = environ_find(env, name)) != NULL) { + envent->flags = flags; free(envent->value); xvasprintf(&envent->value, fmt, ap); } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); + envent->flags = flags; xvasprintf(&envent->value, fmt, ap); RB_INSERT(environ, env, envent); } @@ -133,6 +138,7 @@ environ_clear(struct environ *env, const char *name) } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); + envent->flags = 0; envent->value = NULL; RB_INSERT(environ, env, envent); } @@ -140,7 +146,7 @@ environ_clear(struct environ *env, const char *name) /* Set an environment variable from a NAME=VALUE string. */ void -environ_put(struct environ *env, const char *var) +environ_put(struct environ *env, const char *var, int flags) { char *name, *value; @@ -152,7 +158,7 @@ environ_put(struct environ *env, const char *var) name = xstrdup(var); name[strcspn(name, "=")] = '\0'; - environ_set(env, name, "%s", value); + environ_set(env, name, flags, "%s", value); free(name); } @@ -170,7 +176,7 @@ environ_unset(struct environ *env, const char *name) free(envent); } -/* Copy variables from a destination into a source * environment. */ +/* Copy variables from a destination into a source environment. */ void environ_update(struct options *oo, struct environ *src, struct environ *dst) { @@ -188,7 +194,7 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst) if ((envent = environ_find(src, ov->string)) == NULL) environ_clear(dst, ov->string); else - environ_set(dst, envent->name, "%s", envent->value); + environ_set(dst, envent->name, 0, "%s", envent->value); a = options_array_next(a); } } @@ -201,7 +207,9 @@ environ_push(struct environ *env) environ = xcalloc(1, sizeof *environ); RB_FOREACH(envent, environ, env) { - if (envent->value != NULL && *envent->name != '\0') + if (envent->value != NULL && + *envent->name != '\0' && + (~envent->flags & ENVIRON_HIDDEN)) setenv(envent->name, envent->value, 1); } } @@ -243,14 +251,15 @@ environ_for_session(struct session *s, int no_TERM) if (!no_TERM) { value = options_get_string(global_options, "default-terminal"); - environ_set(env, "TERM", "%s", value); + environ_set(env, "TERM", 0, "%s", value); } if (s != NULL) idx = s->id; else idx = -1; - environ_set(env, "TMUX", "%s,%ld,%d", socket_path, (long)getpid(), idx); + environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(), + idx); return (env); } diff --git a/server-client.c b/server-client.c index aa174d4d..054e1161 100644 --- a/server-client.c +++ b/server-client.c @@ -520,6 +520,7 @@ server_client_check_mouse(struct client *c, struct key_event *event) memcpy(&c->click_event, m, sizeof c->click_event); c->click_button = m->b; + log_debug("click timer started"); tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000; tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L; evtimer_del(&c->click_timer); @@ -2020,7 +2021,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); if (strchr(data, '=') != NULL) - environ_put(c->environ, data); + environ_put(c->environ, data, 0); log_debug("client %p IDENTIFY_ENVIRON %s", c, data); break; case MSG_IDENTIFY_CLIENTPID: diff --git a/spawn.c b/spawn.c index 45243517..865a77e2 100644 --- a/spawn.c +++ b/spawn.c @@ -303,7 +303,7 @@ spawn_pane(struct spawn_context *sc, char **cause) child = environ_for_session(s, 0); if (sc->environ != NULL) environ_copy(sc->environ, child); - environ_set(child, "TMUX_PANE", "%%%u", new_wp->id); + environ_set(child, "TMUX_PANE", 0, "%%%u", new_wp->id); /* * Then the PATH environment variable. The session one is replaced from @@ -313,10 +313,10 @@ spawn_pane(struct spawn_context *sc, char **cause) if (c != NULL && c->session == NULL) { /* only unattached clients */ ee = environ_find(c->environ, "PATH"); if (ee != NULL) - environ_set(child, "PATH", "%s", ee->value); + environ_set(child, "PATH", 0, "%s", ee->value); } if (environ_find(child, "PATH") == NULL) - environ_set(child, "%s", _PATH_DEFPATH); + environ_set(child, "PATH", 0, "%s", _PATH_DEFPATH); /* Then the shell. If respawning, use the old one. */ if (~sc->flags & SPAWN_RESPAWN) { @@ -326,7 +326,7 @@ spawn_pane(struct spawn_context *sc, char **cause) free(new_wp->shell); new_wp->shell = xstrdup(tmp); } - environ_set(child, "SHELL", "%s", new_wp->shell); + environ_set(child, "SHELL", 0, "%s", new_wp->shell); /* Log the arguments we are going to use. */ log_debug("%s: shell=%s", __func__, new_wp->shell); diff --git a/tmux.1 b/tmux.1 index b0fc5600..e785cf8b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -565,6 +565,18 @@ Environment variables may be set by using the syntax for example .Ql HOME=/home/user . Variables set during parsing are added to the global environment. +A hidden variable may be set with +.Ql %hidden , +for example: +.Bd -literal -offset indent +%hidden MYVAR=42 +.Ed +.Pp +Hidden variables are not passed to the environment of processes created +by tmux. +See the +.Sx GLOBAL AND SESSION ENVIRONMENT +section. .Pp Commands may be parsed conditionally by surrounding them with .Ql %if , @@ -4711,10 +4723,16 @@ from inside, and the variable with the correct terminal setting of .Ql screen . .Pp +Variables in both session and global environments may be marked as hidden. +Hidden variables are not passed into the environment of new processes and +instead can only be used by tmux itself (for example in formats, see the +.Sx FORMATS +section). +.Pp Commands to alter and view the environment are: .Bl -tag -width Ds .It Xo Ic set-environment -.Op Fl gru +.Op Fl hgru .Op Fl t Ar target-session .Ar name Op Ar value .Xc @@ -4731,8 +4749,10 @@ flag unsets a variable. .Fl r indicates the variable is to be removed from the environment before starting a new process. +.Fl h +marks the variable as hidden. .It Xo Ic show-environment -.Op Fl gs +.Op Fl hgs .Op Fl t Ar target-session .Op Ar variable .Xc @@ -4749,6 +4769,8 @@ Variables removed from the environment are prefixed with If .Fl s is used, the output is formatted as a set of Bourne shell commands. +.Fl h +shows hidden variables (omitted by default). .El .Sh STATUS LINE .Nm diff --git a/tmux.c b/tmux.c index 60b35e5d..2055a894 100644 --- a/tmux.c +++ b/tmux.c @@ -332,9 +332,9 @@ main(int argc, char **argv) global_environ = environ_create(); for (var = environ; *var != NULL; var++) - environ_put(global_environ, *var); + environ_put(global_environ, *var, 0); if ((cwd = find_cwd()) != NULL) - environ_set(global_environ, "PWD", "%s", cwd); + environ_set(global_environ, "PWD", 0, "%s", cwd); global_options = options_create(NULL); global_s_options = options_create(NULL); diff --git a/tmux.h b/tmux.h index 20559144..53bbc222 100644 --- a/tmux.h +++ b/tmux.h @@ -1048,6 +1048,9 @@ struct environ_entry { char *name; char *value; + int flags; +#define ENVIRON_HIDDEN 0x1 + RB_ENTRY(environ_entry) entry; }; @@ -1957,10 +1960,10 @@ struct environ_entry *environ_first(struct environ *); struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); -void printflike(3, 4) environ_set(struct environ *, const char *, const char *, - ...); +void printflike(4, 5) environ_set(struct environ *, const char *, int, + const char *, ...); void environ_clear(struct environ *, const char *); -void environ_put(struct environ *, const char *); +void environ_put(struct environ *, const char *, int); void environ_unset(struct environ *, const char *); void environ_update(struct options *, struct environ *, struct environ *); void environ_push(struct environ *); From 38f1546a667c014a77778f6dd91898dbf41b077f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 17:14:40 +0000 Subject: [PATCH 0092/1006] Add a way to mark environment variables as "hidden" so they can be used by tmux but are not passed into the environment of new panes. --- tmux.1 | 26 ++++++++++++++++++++++++-- tmux.h | 9 ++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/tmux.1 b/tmux.1 index b0fc5600..e785cf8b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -565,6 +565,18 @@ Environment variables may be set by using the syntax for example .Ql HOME=/home/user . Variables set during parsing are added to the global environment. +A hidden variable may be set with +.Ql %hidden , +for example: +.Bd -literal -offset indent +%hidden MYVAR=42 +.Ed +.Pp +Hidden variables are not passed to the environment of processes created +by tmux. +See the +.Sx GLOBAL AND SESSION ENVIRONMENT +section. .Pp Commands may be parsed conditionally by surrounding them with .Ql %if , @@ -4711,10 +4723,16 @@ from inside, and the variable with the correct terminal setting of .Ql screen . .Pp +Variables in both session and global environments may be marked as hidden. +Hidden variables are not passed into the environment of new processes and +instead can only be used by tmux itself (for example in formats, see the +.Sx FORMATS +section). +.Pp Commands to alter and view the environment are: .Bl -tag -width Ds .It Xo Ic set-environment -.Op Fl gru +.Op Fl hgru .Op Fl t Ar target-session .Ar name Op Ar value .Xc @@ -4731,8 +4749,10 @@ flag unsets a variable. .Fl r indicates the variable is to be removed from the environment before starting a new process. +.Fl h +marks the variable as hidden. .It Xo Ic show-environment -.Op Fl gs +.Op Fl hgs .Op Fl t Ar target-session .Op Ar variable .Xc @@ -4749,6 +4769,8 @@ Variables removed from the environment are prefixed with If .Fl s is used, the output is formatted as a set of Bourne shell commands. +.Fl h +shows hidden variables (omitted by default). .El .Sh STATUS LINE .Nm diff --git a/tmux.h b/tmux.h index 20559144..53bbc222 100644 --- a/tmux.h +++ b/tmux.h @@ -1048,6 +1048,9 @@ struct environ_entry { char *name; char *value; + int flags; +#define ENVIRON_HIDDEN 0x1 + RB_ENTRY(environ_entry) entry; }; @@ -1957,10 +1960,10 @@ struct environ_entry *environ_first(struct environ *); struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); -void printflike(3, 4) environ_set(struct environ *, const char *, const char *, - ...); +void printflike(4, 5) environ_set(struct environ *, const char *, int, + const char *, ...); void environ_clear(struct environ *, const char *); -void environ_put(struct environ *, const char *); +void environ_put(struct environ *, const char *, int); void environ_unset(struct environ *, const char *); void environ_update(struct options *, struct environ *, struct environ *); void environ_push(struct environ *); From 46ed81fc45c26e383e6bb97b0bf624a3087b217f Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 07:35:10 +0000 Subject: [PATCH 0093/1006] Performance improvements for regex searching, most notably: - Use the grid data directly instead of copying it. - Special case the most typical one byte character cells and use memcmp for multiple bytes instead of a handrolled loop. - Hoist regcomp out of the loop into the calling functions. GitHub issue 2143. Also a man page from from jmc@. --- tmux.1 | 3 +- window-copy.c | 242 +++++++++++++++++++++++++++++--------------------- 2 files changed, 142 insertions(+), 103 deletions(-) diff --git a/tmux.1 b/tmux.1 index e785cf8b..ab214fe8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2312,7 +2312,8 @@ and unzoomed (its normal position in the layout). .Fl M begins mouse resizing (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . -.Pp T +.Pp +.Fl T trims all lines below the current cursor position and moves lines out of the history to replace them. .It Xo Ic resize-window diff --git a/window-copy.c b/window-copy.c index fa2d2929..57228273 100644 --- a/window-copy.c +++ b/window-copy.c @@ -58,10 +58,6 @@ static int window_copy_search_lr(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); static int window_copy_search_rl(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); -static int window_copy_search_lr_regex(struct grid *, struct grid *, - u_int *, u_int *, u_int, u_int, u_int, int); -static int window_copy_search_rl_regex(struct grid *, struct grid *, - u_int *, u_int *, u_int, u_int, u_int, int); static int window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg, int eflags); @@ -2292,14 +2288,12 @@ window_copy_search_rl(struct grid *gd, } static int -window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, - u_int *ppx, u_int *psx, u_int py, u_int first, u_int last, int cis) +window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, + u_int first, u_int last, regex_t *reg) { - int cflags = REG_EXTENDED, eflags = 0; + int eflags = 0; u_int endline, foundx, foundy, len, pywrap, size = 1; - u_int ssize = 1; - char *buf, *sbuf; - regex_t reg; + char *buf; regmatch_t regmatch; struct grid_line *gl; @@ -2310,19 +2304,7 @@ window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, if (first >= last) return (0); - sbuf = xmalloc(ssize); - sbuf[0] = '\0'; - sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); - if (sbuf == NULL) - return (0); - /* Set flags for regex search. */ - if (cis) - cflags |= REG_ICASE; - if (regcomp(®, sbuf, cflags) != 0) { - free(sbuf); - return (0); - } if (first != 0) eflags |= REG_NOTBOL; @@ -2342,7 +2324,7 @@ window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, len += gd->sx; } - if (regexec(®, buf, 1, ®match, eflags) == 0) { + if (regexec(reg, buf, 1, ®match, eflags) == 0) { foundx = first; foundy = py; window_copy_cstrtocellpos(gd, len, &foundx, &foundy, @@ -2358,15 +2340,11 @@ window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, foundy--; } *psx -= *ppx; - regfree(®); - free(sbuf); free(buf); return (1); } } - regfree(®); - free(sbuf); free(buf); *ppx = 0; *psx = 0; @@ -2374,28 +2352,15 @@ window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, } static int -window_copy_search_rl_regex(struct grid *gd, struct grid *sgd, - u_int *ppx, u_int *psx, u_int py, u_int first, u_int last, int cis) +window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, + u_int first, u_int last, regex_t *reg) { - int cflags = REG_EXTENDED, eflags = 0; - u_int endline, len, pywrap, size = 1, ssize = 1; - char *buf, *sbuf; - regex_t reg; + int eflags = 0; + u_int endline, len, pywrap, size = 1; + char *buf; struct grid_line *gl; - sbuf = xmalloc(ssize); - sbuf[0] = '\0'; - sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); - if (sbuf == NULL) - return (0); - /* Set flags for regex search. */ - if (cis) - cflags |= REG_ICASE; - if (regcomp(®, sbuf, cflags) != 0) { - free(sbuf); - return (0); - } if (first != 0) eflags |= REG_NOTBOL; @@ -2416,22 +2381,38 @@ window_copy_search_rl_regex(struct grid *gd, struct grid *sgd, } if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf, - ®, eflags)) + reg, eflags)) { - regfree(®); - free(sbuf); free(buf); return (1); } - regfree(®); - free(sbuf); free(buf); *ppx = 0; *psx = 0; return (0); } +static const char * +window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size) +{ + struct grid_cell_entry *gce; + + if (px >= gl->cellsize) { + *size = 1; + return (" "); + } + + gce = &gl->celldata[px]; + if (~gce->flags & GRID_FLAG_EXTENDED) { + *size = 1; + return (&gce->data.data); + } + + *size = gl->extddata[gce->offset].data.size; + return (gl->extddata[gce->offset].data.data); +} + /* Find last match in given range. */ static int window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, @@ -2486,20 +2467,33 @@ static char * window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, char *buf, u_int *size) { - u_int ax, bx, newsize; - struct grid_cell gc; + u_int ax, bx, newsize = *size; + const struct grid_line *gl; + const char *d; + size_t bufsize = 1024, dlen; + while (bufsize < newsize) + bufsize *= 2; + buf = xrealloc(buf, bufsize); + + gl = grid_peek_line(gd, py); bx = *size - 1; - newsize = *size; for (ax = first; ax < last; ax++) { - grid_get_cell(gd, ax, py, &gc); - newsize += gc.data.size; - buf = xrealloc(buf, newsize); - memcpy(buf + bx, gc.data.data, gc.data.size); - bx += gc.data.size; + d = window_copy_cellstring(gl, ax, &dlen); + newsize += dlen; + while (bufsize < newsize) { + bufsize *= 2; + buf = xrealloc(buf, bufsize); + } + if (dlen == 1) + buf[bx++] = *d; + else { + memcpy(buf + bx, d, dlen); + bx += dlen; + } } - buf[newsize - 1] = '\0'; + *size = newsize; return (buf); } @@ -2509,57 +2503,64 @@ static void window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, const char *str) { - u_int cell, ccell, px, pywrap; - int match; - const char *cstr; - char *celldata, **cells; - struct grid_cell gc; - - /* Set up staggered array of cell contents. This speeds up search. */ - cells = xreallocarray(NULL, ncells, sizeof cells[0]); + u_int cell, ccell, px, pywrap, pos, len; + int match; + const struct grid_line *gl; + const char *d; + size_t dlen; + struct { + const char *d; + size_t dlen; + } *cells; /* Populate the array of cell data. */ + cells = xreallocarray(NULL, ncells, sizeof cells[0]); cell = 0; px = *ppx; pywrap = *ppy; + gl = grid_peek_line(gd, pywrap); while (cell < ncells) { - grid_get_cell(gd, px, pywrap, &gc); - celldata = xmalloc(gc.data.size + 1); - memcpy(celldata, gc.data.data, gc.data.size); - celldata[gc.data.size] = '\0'; - cells[cell] = celldata; + cells[cell].d = window_copy_cellstring(gl, px, + &cells[cell].dlen); cell++; px = (px + 1) % gd->sx; - if (px == 0) + if (px == 0) { pywrap++; + gl = grid_peek_line(gd, pywrap); + } } /* Locate starting cell. */ cell = 0; + len = strlen(str); while (cell < ncells) { ccell = cell; - cstr = str; + pos = 0; match = 1; while (ccell < ncells) { - /* Anchor found to the end. */ - if (*cstr == '\0') { + if (str[pos] == '\0') { match = 0; break; } - - celldata = cells[ccell]; - while (*celldata != '\0' && *cstr != '\0') { - if (*celldata++ != *cstr++) { + d = cells[ccell].d; + dlen = cells[ccell].dlen; + if (dlen == 1) { + if (str[pos] != *d) { match = 0; break; } + pos++; + } else { + if (dlen > len - pos) + dlen = len - pos; + if (memcmp(str + pos, d, dlen) != 0) { + match = 0; + break; + } + pos += dlen; } - - if (!match) - break; ccell++; } - if (match) break; cell++; @@ -2577,8 +2578,6 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, *ppy = pywrap; /* Free cell data. */ - for (cell = 0; cell < ncells; cell++) - free(cells[cell]); free(cells); } @@ -2638,29 +2637,45 @@ 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) { - u_int i, px, sx; - int found = 0; + u_int i, px, sx, ssize = 1; + int found = 0, cflags = REG_EXTENDED; + char *sbuf; + regex_t reg; + + if (regex) { + sbuf = xmalloc(ssize); + sbuf[0] = '\0'; + sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); + if (cis) + cflags |= REG_ICASE; + if (regcomp(®, sbuf, cflags) != 0) { + free(sbuf); + return (0); + } + } if (direction) { for (i = fy; i <= endline; i++) { - if (regex) - found = window_copy_search_lr_regex(gd, sgd, - &px, &sx, i, fx, gd->sx, cis); - else + if (regex) { + found = window_copy_search_lr_regex(gd, + &px, &sx, i, fx, gd->sx, ®); + } else { found = window_copy_search_lr(gd, sgd, &px, i, fx, gd->sx, cis); + } if (found) break; fx = 0; } } else { for (i = fy + 1; endline < i; i--) { - if (regex) - found = window_copy_search_rl_regex(gd, sgd, - &px, &sx, i - 1, 0, fx + 1, cis); - else + if (regex) { + found = window_copy_search_rl_regex(gd, + &px, &sx, i - 1, 0, fx + 1, ®); + } else { found = window_copy_search_rl(gd, sgd, &px, i - 1, 0, fx + 1, cis); + } if (found) { i--; break; @@ -2668,6 +2683,10 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, fx = gd->sx - 1; } } + if (regex) { + free(sbuf); + regfree(®); + } if (found) { window_copy_scroll_to(wme, px, i); @@ -2739,7 +2758,11 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, struct screen_write_ctx ctx; struct grid *gd = s->grid; int found, cis, which = -1; + int cflags = REG_EXTENDED; u_int px, py, b, nfound = 0, width; + u_int ssize = 1; + char *sbuf; + regex_t reg; if (ssp == NULL) { width = screen_write_strlen("%s", data->searchstr); @@ -2757,25 +2780,36 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, free(data->searchmark); data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx); + if (regex) { + sbuf = xmalloc(ssize); + sbuf[0] = '\0'; + sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx, + sbuf, &ssize); + if (cis) + cflags |= REG_ICASE; + if (regcomp(®, sbuf, cflags) != 0) { + free(sbuf); + return (0); + } + } for (py = 0; py < gd->hsize + gd->sy; py++) { px = 0; for (;;) { if (regex) { found = window_copy_search_lr_regex(gd, - ssp->grid, &px, &width, py, px, - gd->sx, cis); + &px, &width, py, px, gd->sx, ®); if (!found) break; - } - else { + } else { found = window_copy_search_lr(gd, ssp->grid, - &px, py, px, gd->sx, cis); + &px, py, px, gd->sx, cis); if (!found) break; } nfound++; - if (px == data->cx && py == gd->hsize + data->cy - data->oy) + if (px == data->cx && + py == gd->hsize + data->cy - data->oy) which = nfound; b = (py * gd->sx) + px; @@ -2784,6 +2818,10 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, px++; } } + if (regex) { + free(sbuf); + regfree(®); + } if (which != -1) data->searchthis = 1 + nfound - which; From 89d2a20e561878f3fee11005369c1d56b9a08c38 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 07:35:10 +0000 Subject: [PATCH 0094/1006] Performance improvements for regex searching, most notably: - Use the grid data directly instead of copying it. - Special case the most typical one byte character cells and use memcmp for multiple bytes instead of a handrolled loop. - Hoist regcomp out of the loop into the calling functions. GitHub issue 2143. Also a man page from from jmc@. --- window-copy.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/window-copy.c b/window-copy.c index 8c226a92..57228273 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2523,9 +2523,8 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, cells[cell].d = window_copy_cellstring(gl, px, &cells[cell].dlen); cell++; - px++; - if (px == gd->sx) { - px = 0; + px = (px + 1) % gd->sx; + if (px == 0) { pywrap++; gl = grid_peek_line(gd, pywrap); } @@ -2714,27 +2713,23 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; - const char *str = data->searchstr; u_int fx, fy, endline; int wrapflag, cis, found; - if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') - regex = 0; - free(wp->searchstr); - wp->searchstr = xstrdup(str); + wp->searchstr = xstrdup(data->searchstr); wp->searchregex = regex; fx = data->cx; fy = screen_hsize(data->backing) - data->oy + data->cy; - screen_init(&ss, screen_write_strlen("%s", str), 1, 0); + screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0); screen_write_start(&ctx, NULL, &ss); - screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); + screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); screen_write_stop(&ctx); wrapflag = options_get_number(wp->window->options, "wrap-search"); - cis = window_copy_is_lowercase(str); + cis = window_copy_is_lowercase(data->searchstr); if (direction) { window_copy_move_right(s, &fx, &fy, wrapflag); From 46092f27605de183056f39fd0859af11ae284041 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 07:52:07 +0000 Subject: [PATCH 0095/1006] Use a comparison to check for wrap and avoid an expensive modulus. --- window-copy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/window-copy.c b/window-copy.c index 57228273..4a3ee9ea 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2523,8 +2523,9 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, cells[cell].d = window_copy_cellstring(gl, px, &cells[cell].dlen); cell++; - px = (px + 1) % gd->sx; - if (px == 0) { + px++; + if (px == gd->sx) { + px = 0; pywrap++; gl = grid_peek_line(gd, pywrap); } From c129ed3233d8303f3ae1fc14a9728e8d231c3911 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 07:52:07 +0000 Subject: [PATCH 0096/1006] Use a comparison to check for wrap and avoid an expensive modulus. --- window-copy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/window-copy.c b/window-copy.c index 57228273..4a3ee9ea 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2523,8 +2523,9 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, cells[cell].d = window_copy_cellstring(gl, px, &cells[cell].dlen); cell++; - px = (px + 1) % gd->sx; - if (px == 0) { + px++; + if (px == gd->sx) { + px = 0; pywrap++; gl = grid_peek_line(gd, pywrap); } From b66d62d2d05fcb889680c5d844d68ffcef6e9689 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 08:07:05 +0000 Subject: [PATCH 0097/1006] Do not go down the regex search path (which is expensive because we need to convert the grid data into a string for regexec and reverse it to find the grid position) if the search string does not contain any regex special characters. --- window-copy.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/window-copy.c b/window-copy.c index 4a3ee9ea..8c226a92 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2714,23 +2714,27 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; + const char *str = data->searchstr; u_int fx, fy, endline; int wrapflag, cis, found; + if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') + regex = 0; + free(wp->searchstr); - wp->searchstr = xstrdup(data->searchstr); + wp->searchstr = xstrdup(str); wp->searchregex = regex; fx = data->cx; fy = screen_hsize(data->backing) - data->oy + data->cy; - screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0); + screen_init(&ss, screen_write_strlen("%s", str), 1, 0); screen_write_start(&ctx, NULL, &ss); - screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); + screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); screen_write_stop(&ctx); wrapflag = options_get_number(wp->window->options, "wrap-search"); - cis = window_copy_is_lowercase(data->searchstr); + cis = window_copy_is_lowercase(str); if (direction) { window_copy_move_right(s, &fx, &fy, wrapflag); From cd30633d1092366dc5cc44adca1cd3675de9cf39 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 08:07:05 +0000 Subject: [PATCH 0098/1006] Do not go down the regex search path (which is expensive because we need to convert the grid data into a string for regexec and reverse it to find the grid position) if the search string does not contain any regex special characters. --- window-copy.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/window-copy.c b/window-copy.c index 4a3ee9ea..8c226a92 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2714,23 +2714,27 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; + const char *str = data->searchstr; u_int fx, fy, endline; int wrapflag, cis, found; + if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') + regex = 0; + free(wp->searchstr); - wp->searchstr = xstrdup(data->searchstr); + wp->searchstr = xstrdup(str); wp->searchregex = regex; fx = data->cx; fy = screen_hsize(data->backing) - data->oy + data->cy; - screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0); + screen_init(&ss, screen_write_strlen("%s", str), 1, 0); screen_write_start(&ctx, NULL, &ss); - screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); + screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); screen_write_stop(&ctx); wrapflag = options_get_number(wp->window->options, "wrap-search"); - cis = window_copy_is_lowercase(data->searchstr); + cis = window_copy_is_lowercase(str); if (direction) { window_copy_move_right(s, &fx, &fy, wrapflag); From b8356c650a0d9ca455ed381777828308756b32d0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 1 Apr 2020 09:29:02 +0100 Subject: [PATCH 0099/1006] Update CHANGES. --- CHANGES | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGES b/CHANGES index a825865d..709e69b6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,16 @@ CHANGES FROM 3.1 TO 3.2 +* Make the mouse_word and mouse_line formats work in copy mode and enable the + default pane menu in copy mode. + +* Add a -T flag to resize-pane to trim lines below the cursor, moving lines out + of the history. + +* Add a way to mark environment variables as "hidden" so they can be used by + tmux (for example in formats) but are not set in the environment for new + panes. set-environment and show-environment have a new -h flag and there is a + new %hidden statement for the configuration file. + * Change default position for display-menu -x and -y to centre rather than top left. @@ -30,6 +41,10 @@ CHANGES FROM 3.1 TO 3.2 CHANGES FROM 3.0a TO 3.1 +* Search using regular expressions in copy mode. search-forward and + search-backward use regular expressions by default; the incremental versions + do not. + * Add selection_active format for when the selection is present but not moving with the cursor. From e5fd85415d89ebe8b2f69c80ce6cf80fb1442fde Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 1 Apr 2020 09:29:44 +0100 Subject: [PATCH 0100/1006] Update CHANGES. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index d70167d4..62b697bc 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,9 @@ CHANGES FROM 3.0a TO 3.1 +* Search using regular expressions in copy mode. search-forward and + search-backward use regular expressions by default; the incremental versions + do not. + * Turn off mouse mode 1003 as well as the rest when exiting. * Add selection_active format for when the selection is present but not moving From 0ced25ce5053dcc66650c87c12a61800b884d78b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 1 Apr 2020 09:30:29 +0100 Subject: [PATCH 0101/1006] Fix configure.ac. --- configure.ac | 4 ---- 1 file changed, 4 deletions(-) diff --git a/configure.ac b/configure.ac index a835e104..e29ebdc7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,6 @@ # configure.ac -<<<<<<< HEAD AC_INIT([tmux], next-3.2) -======= -AC_INIT([tmux], 3.1-rc3) ->>>>>>> 3.1-rc AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From dd2fdcda791127607a57767f0ebc69060fa6af42 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 09:05:27 +0000 Subject: [PATCH 0102/1006] Support mouse in popups. --- input-keys.c | 54 ++++++++++++++++++++++++++++++++++------------------ popup.c | 13 ++++++++++--- tmux.h | 2 ++ 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/input-keys.c b/input-keys.c index 9d4043ef..5e54d121 100644 --- a/input-keys.c +++ b/input-keys.c @@ -257,26 +257,20 @@ input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, return (0); } -/* Translate mouse and output. */ -static void -input_key_mouse(struct window_pane *wp, struct mouse_event *m) +/* Get mouse event string. */ +int +input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y, + const char **rbuf, size_t *rlen) { - struct screen *s = wp->screen; - char buf[40]; + static char buf[40]; size_t len; - u_int x, y; - /* Ignore events if no mouse mode or the pane is not visible. */ - if (m->ignore || (s->mode & ALL_MOUSE_MODES) == 0) - return; - if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) - return; - if (!window_pane_visible(wp)) - return; + *rbuf = NULL; + *rlen = 0; /* If this pane is not in button or all mode, discard motion events. */ if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0) - return; + return (0); /* * If this event is a release event and not in all mode, discard it. @@ -288,13 +282,13 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) if (MOUSE_DRAG(m->sgr_b) && MOUSE_BUTTONS(m->sgr_b) == 3 && (~s->mode & MODE_MOUSE_ALL)) - return; + return (0); } else { if (MOUSE_DRAG(m->b) && MOUSE_BUTTONS(m->b) == 3 && MOUSE_BUTTONS(m->lb) == 3 && (~s->mode & MODE_MOUSE_ALL)) - return; + return (0); } /* @@ -311,19 +305,43 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) m->sgr_b, x + 1, y + 1, m->sgr_type); } else if (s->mode & MODE_MOUSE_UTF8) { if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) - return; + return (0); len = xsnprintf(buf, sizeof buf, "\033[M"); len += input_split2(m->b + 32, &buf[len]); len += input_split2(x + 33, &buf[len]); len += input_split2(y + 33, &buf[len]); } else { if (m->b > 223) - return; + return (0); len = xsnprintf(buf, sizeof buf, "\033[M"); buf[len++] = m->b + 32; buf[len++] = x + 33; buf[len++] = y + 33; } + + *rbuf = buf; + *rlen = len; + return (1); +} + +/* Translate mouse and output. */ +static void +input_key_mouse(struct window_pane *wp, struct mouse_event *m) +{ + struct screen *s = wp->screen; + u_int x, y; + const char *buf; + size_t len; + + /* Ignore events if no mouse mode or the pane is not visible. */ + if (m->ignore || (s->mode & ALL_MOUSE_MODES) == 0) + return; + if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) + return; + if (!window_pane_visible(wp)) + return; + if (!input_key_get_mouse(s, m, x, y, &buf, &len)) + return; log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id); bufferevent_write(wp->event, buf, len); } diff --git a/popup.c b/popup.c index 05ff1b4c..299d4e76 100644 --- a/popup.c +++ b/popup.c @@ -225,7 +225,8 @@ popup_key_cb(struct client *c, struct key_event *event) struct cmdq_item *new_item; struct cmd_parse_result *pr; struct format_tree *ft; - const char *cmd; + const char *cmd, *buf; + size_t len; if (KEYC_IS_MOUSE(event->key)) { if (pd->dragging != OFF) { @@ -258,14 +259,20 @@ popup_key_cb(struct client *c, struct key_event *event) } if (pd->ictx != NULL && (pd->flags & POPUP_WRITEKEYS)) { - if (KEYC_IS_MOUSE(event->key)) - return (0); if (((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0 || pd->job == NULL) && (event->key == '\033' || event->key == '\003')) return (1); if (pd->job == NULL) return (0); + if (KEYC_IS_MOUSE(event->key)) { + /* Must be inside, checked already. */ + if (!input_key_get_mouse(&pd->s, m, m->x - pd->px, + m->y - pd->py, &buf, &len)) + return (0); + bufferevent_write(job_get_event(pd->job), buf, len); + return (0); + } input_key(NULL, &pd->s, job_get_event(pd->job), event->key); return (0); } diff --git a/tmux.h b/tmux.h index 53bbc222..326f77dd 100644 --- a/tmux.h +++ b/tmux.h @@ -2315,6 +2315,8 @@ void input_parse_screen(struct input_ctx *, struct screen *, u_char *, int input_key_pane(struct window_pane *, key_code, struct mouse_event *); int input_key(struct window_pane *, struct screen *, struct bufferevent *, key_code); +int input_key_get_mouse(struct screen *, struct mouse_event *, u_int, + u_int, const char **, size_t *); /* xterm-keys.c */ char *xterm_keys_lookup(key_code); From 8dedccaa205a91a0dd57012150567403c2ac827d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2020 16:53:23 +0000 Subject: [PATCH 0103/1006] Add non-regex search variants to avoid the performance cost for people with large histories or long lines. --- tmux.1 | 24 ++++++++- tmux.h | 2 + window-copy.c | 135 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 116 insertions(+), 45 deletions(-) diff --git a/tmux.1 b/tmux.1 index dc78abdc..03950c60 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1503,9 +1503,11 @@ The following commands are supported in copy mode: .It Li "scroll-up" Ta "C-y" Ta "C-Up" .It Li "search-again" Ta "n" Ta "n" .It Li "search-backward " Ta "?" Ta "" -.It Li "search-forward " Ta "/" Ta "" .It Li "search-backward-incremental " Ta "" Ta "C-r" +.It Li "search-backward-text " Ta "" Ta "" +.It Li "search-forward " Ta "/" Ta "" .It Li "search-forward-incremental " Ta "" Ta "C-s" +.It Li "search-forward-text " Ta "" Ta "" .It Li "search-reverse" Ta "N" Ta "N" .It Li "select-line" Ta "V" Ta "" .It Li "select-word" Ta "" Ta "" @@ -1514,6 +1516,26 @@ The following commands are supported in copy mode: .It Li "top-line" Ta "H" Ta "M-R" .El .Pp +The search commands come in several varieties: +.Ql search-forward +and +.Ql search-backward +search for a regular expression; +the +.Ql -text +variants search for a plain text string rather than a regular expression; +.Ql -incremental +perform an incremental search and expect to be used with the +.Fl i +flag to the +.Ic command-prompt +command. +.Ql search-again +repeats the last search and +.Ql search-reverse +does the same but reverses the direction (forward becomes backward and backward +becomes forward). +.Pp Copy commands may take an optional buffer prefix argument which is used to generate the buffer name (the default is .Ql buffer diff --git a/tmux.h b/tmux.h index 222f7d43..3720beee 100644 --- a/tmux.h +++ b/tmux.h @@ -927,7 +927,9 @@ struct window_pane { TAILQ_HEAD (, window_mode_entry) modes; struct event modetimer; time_t modelast; + char *searchstr; + int searchregex; TAILQ_ENTRY(window_pane) entry; RB_ENTRY(window_pane) tree_entry; diff --git a/window-copy.c b/window-copy.c index 8e3f63d1..bb38e60e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -254,6 +254,7 @@ struct window_copy_mode_data { u_int lastsx; /* size of last line w/ content */ int searchtype; + int searchregex; char *searchstr; bitstr_t *searchmark; u_int searchcount; @@ -307,9 +308,11 @@ window_copy_common_init(struct window_mode_entry *wme) if (wp->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = wp->searchregex; data->searchstr = xstrdup(wp->searchstr); } else { data->searchtype = WINDOW_COPY_OFF; + data->searchregex = 0; data->searchstr = NULL; } data->searchmark = NULL; @@ -675,6 +678,35 @@ window_copy_key_table(struct window_mode_entry *wme) return ("copy-mode"); } +static int +window_copy_expand_search_string(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + const char *argument; + char *expanded; + + if (cs->args->argc == 2) { + argument = cs->args->argv[1]; + if (*argument != '\0') { + if (args_has(cs->args, 'F')) { + expanded = format_single(NULL, argument, NULL, + NULL, NULL, wme->wp); + if (*expanded == '\0') { + free(expanded); + return (0); + } + free(data->searchstr); + data->searchstr = expanded; + } else { + free(data->searchstr); + data->searchstr = xstrdup(argument); + } + } + } + return (1); +} + static enum window_copy_cmd_action window_copy_cmd_append_selection(struct window_copy_cmd_state *cs) { @@ -1496,10 +1528,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, 1); + window_copy_search_up(wme, data->searchregex); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_down(wme, 1); + window_copy_search_down(wme, data->searchregex); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1513,10 +1545,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, 1); + window_copy_search_down(wme, data->searchregex); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_up(wme, 1); + window_copy_search_up(wme, data->searchregex); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1736,70 +1768,76 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument; - char *expanded; - if (cs->args->argc == 2) { - argument = cs->args->argv[1]; - if (*argument != '\0') { - if (args_has(cs->args, 'F')) { - expanded = format_single(NULL, argument, NULL, - NULL, NULL, wme->wp); - if (*expanded == '\0') { - free(expanded); - return (WINDOW_COPY_CMD_NOTHING); - } - free(data->searchstr); - data->searchstr = expanded; - } else { - free(data->searchstr); - data->searchstr = xstrdup(argument); - } - } - } + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 1; for (; np != 0; np--) window_copy_search_up(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + + if (data->searchstr != NULL) { + data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 0; + for (; np != 0; np--) + window_copy_search_up(wme, 0); + } + return (WINDOW_COPY_CMD_NOTHING); +} + static enum window_copy_cmd_action window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument; - char *expanded; - if (cs->args->argc == 2) { - argument = cs->args->argv[1]; - if (*argument != '\0') { - if (args_has(cs->args, 'F')) { - expanded = format_single(NULL, argument, NULL, - NULL, NULL, wme->wp); - if (*expanded == '\0') { - free(expanded); - return (WINDOW_COPY_CMD_NOTHING); - } - free(data->searchstr); - data->searchstr = expanded; - } else { - free(data->searchstr); - data->searchstr = xstrdup(argument); - } - } - } + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 1; for (; np != 0; np--) window_copy_search_down(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + if (!window_copy_expand_search_string(cs)) + return (WINDOW_COPY_CMD_NOTHING); + + if (data->searchstr != NULL) { + data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 0; + for (; np != 0; np--) + window_copy_search_down(wme, 0); + } + return (WINDOW_COPY_CMD_NOTHING); +} + static enum window_copy_cmd_action window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) { @@ -1829,6 +1867,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) case '=': case '-': data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_up(wme, 0)) { @@ -1838,6 +1877,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) break; case '+': data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_down(wme, 0)) { @@ -1878,6 +1918,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) case '=': case '+': data->searchtype = WINDOW_COPY_SEARCHDOWN; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_down(wme, 0)) { @@ -1887,6 +1928,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) break; case '-': data->searchtype = WINDOW_COPY_SEARCHUP; + data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_up(wme, 0)) { @@ -2012,10 +2054,14 @@ static const struct { window_copy_cmd_search_again }, { "search-backward", 0, 1, 0, window_copy_cmd_search_backward }, + { "search-backward-text", 0, 1, 0, + window_copy_cmd_search_backward_text }, { "search-backward-incremental", 1, 1, 0, window_copy_cmd_search_backward_incremental }, { "search-forward", 0, 1, 0, window_copy_cmd_search_forward }, + { "search-forward-text", 0, 1, 0, + window_copy_cmd_search_forward_text }, { "search-forward-incremental", 1, 1, 0, window_copy_cmd_search_forward_incremental }, { "search-reverse", 0, 0, 0, @@ -2624,6 +2670,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) free(wp->searchstr); wp->searchstr = xstrdup(data->searchstr); + wp->searchregex = regex; fx = data->cx; fy = screen_hsize(data->backing) - data->oy + data->cy; From 0dbf4145788cda92b983037e3a7dcdd9a8997e23 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 07:35:10 +0000 Subject: [PATCH 0104/1006] Performance improvements for regex searching, most notably: - Use the grid data directly instead of copying it. - Special case the most typical one byte character cells and use memcmp for multiple bytes instead of a handrolled loop. - Hoist regcomp out of the loop into the calling functions. GitHub issue 2143. Also a man page from from jmc@. --- window-copy.c | 242 +++++++++++++++++++++++++++++--------------------- 1 file changed, 140 insertions(+), 102 deletions(-) diff --git a/window-copy.c b/window-copy.c index bb38e60e..b2b61dc7 100644 --- a/window-copy.c +++ b/window-copy.c @@ -58,10 +58,6 @@ static int window_copy_search_lr(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); static int window_copy_search_rl(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); -static int window_copy_search_lr_regex(struct grid *, struct grid *, - u_int *, u_int *, u_int, u_int, u_int, int); -static int window_copy_search_rl_regex(struct grid *, struct grid *, - u_int *, u_int *, u_int, u_int, u_int, int); static int window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg, int eflags); @@ -2263,14 +2259,12 @@ window_copy_search_rl(struct grid *gd, } static int -window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, - u_int *ppx, u_int *psx, u_int py, u_int first, u_int last, int cis) +window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, + u_int first, u_int last, regex_t *reg) { - int cflags = REG_EXTENDED, eflags = 0; + int eflags = 0; u_int endline, foundx, foundy, len, pywrap, size = 1; - u_int ssize = 1; - char *buf, *sbuf; - regex_t reg; + char *buf; regmatch_t regmatch; struct grid_line *gl; @@ -2281,19 +2275,7 @@ window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, if (first >= last) return (0); - sbuf = xmalloc(ssize); - sbuf[0] = '\0'; - sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); - if (sbuf == NULL) - return (0); - /* Set flags for regex search. */ - if (cis) - cflags |= REG_ICASE; - if (regcomp(®, sbuf, cflags) != 0) { - free(sbuf); - return (0); - } if (first != 0) eflags |= REG_NOTBOL; @@ -2313,7 +2295,7 @@ window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, len += gd->sx; } - if (regexec(®, buf, 1, ®match, eflags) == 0) { + if (regexec(reg, buf, 1, ®match, eflags) == 0) { foundx = first; foundy = py; window_copy_cstrtocellpos(gd, len, &foundx, &foundy, @@ -2329,15 +2311,11 @@ window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, foundy--; } *psx -= *ppx; - regfree(®); - free(sbuf); free(buf); return (1); } } - regfree(®); - free(sbuf); free(buf); *ppx = 0; *psx = 0; @@ -2345,28 +2323,15 @@ window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, } static int -window_copy_search_rl_regex(struct grid *gd, struct grid *sgd, - u_int *ppx, u_int *psx, u_int py, u_int first, u_int last, int cis) +window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, + u_int first, u_int last, regex_t *reg) { - int cflags = REG_EXTENDED, eflags = 0; - u_int endline, len, pywrap, size = 1, ssize = 1; - char *buf, *sbuf; - regex_t reg; + int eflags = 0; + u_int endline, len, pywrap, size = 1; + char *buf; struct grid_line *gl; - sbuf = xmalloc(ssize); - sbuf[0] = '\0'; - sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); - if (sbuf == NULL) - return (0); - /* Set flags for regex search. */ - if (cis) - cflags |= REG_ICASE; - if (regcomp(®, sbuf, cflags) != 0) { - free(sbuf); - return (0); - } if (first != 0) eflags |= REG_NOTBOL; @@ -2387,22 +2352,38 @@ window_copy_search_rl_regex(struct grid *gd, struct grid *sgd, } if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf, - ®, eflags)) + reg, eflags)) { - regfree(®); - free(sbuf); free(buf); return (1); } - regfree(®); - free(sbuf); free(buf); *ppx = 0; *psx = 0; return (0); } +static const char * +window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size) +{ + struct grid_cell_entry *gce; + + if (px >= gl->cellsize) { + *size = 1; + return (" "); + } + + gce = &gl->celldata[px]; + if (~gce->flags & GRID_FLAG_EXTENDED) { + *size = 1; + return (&gce->data.data); + } + + *size = gl->extddata[gce->offset].data.size; + return (gl->extddata[gce->offset].data.data); +} + /* Find last match in given range. */ static int window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, @@ -2457,20 +2438,33 @@ static char * window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, char *buf, u_int *size) { - u_int ax, bx, newsize; - struct grid_cell gc; + u_int ax, bx, newsize = *size; + const struct grid_line *gl; + const char *d; + size_t bufsize = 1024, dlen; + while (bufsize < newsize) + bufsize *= 2; + buf = xrealloc(buf, bufsize); + + gl = grid_peek_line(gd, py); bx = *size - 1; - newsize = *size; for (ax = first; ax < last; ax++) { - grid_get_cell(gd, ax, py, &gc); - newsize += gc.data.size; - buf = xrealloc(buf, newsize); - memcpy(buf + bx, gc.data.data, gc.data.size); - bx += gc.data.size; + d = window_copy_cellstring(gl, ax, &dlen); + newsize += dlen; + while (bufsize < newsize) { + bufsize *= 2; + buf = xrealloc(buf, bufsize); + } + if (dlen == 1) + buf[bx++] = *d; + else { + memcpy(buf + bx, d, dlen); + bx += dlen; + } } - buf[newsize - 1] = '\0'; + *size = newsize; return (buf); } @@ -2480,57 +2474,64 @@ static void window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, const char *str) { - u_int cell, ccell, px, pywrap; - int match; - const char *cstr; - char *celldata, **cells; - struct grid_cell gc; - - /* Set up staggered array of cell contents. This speeds up search. */ - cells = xreallocarray(NULL, ncells, sizeof cells[0]); + u_int cell, ccell, px, pywrap, pos, len; + int match; + const struct grid_line *gl; + const char *d; + size_t dlen; + struct { + const char *d; + size_t dlen; + } *cells; /* Populate the array of cell data. */ + cells = xreallocarray(NULL, ncells, sizeof cells[0]); cell = 0; px = *ppx; pywrap = *ppy; + gl = grid_peek_line(gd, pywrap); while (cell < ncells) { - grid_get_cell(gd, px, pywrap, &gc); - celldata = xmalloc(gc.data.size + 1); - memcpy(celldata, gc.data.data, gc.data.size); - celldata[gc.data.size] = '\0'; - cells[cell] = celldata; + cells[cell].d = window_copy_cellstring(gl, px, + &cells[cell].dlen); cell++; px = (px + 1) % gd->sx; - if (px == 0) + if (px == 0) { pywrap++; + gl = grid_peek_line(gd, pywrap); + } } /* Locate starting cell. */ cell = 0; + len = strlen(str); while (cell < ncells) { ccell = cell; - cstr = str; + pos = 0; match = 1; while (ccell < ncells) { - /* Anchor found to the end. */ - if (*cstr == '\0') { + if (str[pos] == '\0') { match = 0; break; } - - celldata = cells[ccell]; - while (*celldata != '\0' && *cstr != '\0') { - if (*celldata++ != *cstr++) { + d = cells[ccell].d; + dlen = cells[ccell].dlen; + if (dlen == 1) { + if (str[pos] != *d) { match = 0; break; } + pos++; + } else { + if (dlen > len - pos) + dlen = len - pos; + if (memcmp(str + pos, d, dlen) != 0) { + match = 0; + break; + } + pos += dlen; } - - if (!match) - break; ccell++; } - if (match) break; cell++; @@ -2548,8 +2549,6 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, *ppy = pywrap; /* Free cell data. */ - for (cell = 0; cell < ncells; cell++) - free(cells[cell]); free(cells); } @@ -2609,29 +2608,45 @@ 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) { - u_int i, px, sx; - int found = 0; + u_int i, px, sx, ssize = 1; + int found = 0, cflags = REG_EXTENDED; + char *sbuf; + regex_t reg; + + if (regex) { + sbuf = xmalloc(ssize); + sbuf[0] = '\0'; + sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); + if (cis) + cflags |= REG_ICASE; + if (regcomp(®, sbuf, cflags) != 0) { + free(sbuf); + return (0); + } + } if (direction) { for (i = fy; i <= endline; i++) { - if (regex) - found = window_copy_search_lr_regex(gd, sgd, - &px, &sx, i, fx, gd->sx, cis); - else + if (regex) { + found = window_copy_search_lr_regex(gd, + &px, &sx, i, fx, gd->sx, ®); + } else { found = window_copy_search_lr(gd, sgd, &px, i, fx, gd->sx, cis); + } if (found) break; fx = 0; } } else { for (i = fy + 1; endline < i; i--) { - if (regex) - found = window_copy_search_rl_regex(gd, sgd, - &px, &sx, i - 1, 0, fx + 1, cis); - else + if (regex) { + found = window_copy_search_rl_regex(gd, + &px, &sx, i - 1, 0, fx + 1, ®); + } else { found = window_copy_search_rl(gd, sgd, &px, i - 1, 0, fx + 1, cis); + } if (found) { i--; break; @@ -2639,6 +2654,10 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, fx = gd->sx - 1; } } + if (regex) { + free(sbuf); + regfree(®); + } if (found) { window_copy_scroll_to(wme, px, i); @@ -2710,7 +2729,11 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, struct screen_write_ctx ctx; struct grid *gd = s->grid; int found, cis, which = -1; + int cflags = REG_EXTENDED; u_int px, py, b, nfound = 0, width; + u_int ssize = 1; + char *sbuf; + regex_t reg; if (ssp == NULL) { width = screen_write_strlen("%s", data->searchstr); @@ -2728,25 +2751,36 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, free(data->searchmark); data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx); + if (regex) { + sbuf = xmalloc(ssize); + sbuf[0] = '\0'; + sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx, + sbuf, &ssize); + if (cis) + cflags |= REG_ICASE; + if (regcomp(®, sbuf, cflags) != 0) { + free(sbuf); + return (0); + } + } for (py = 0; py < gd->hsize + gd->sy; py++) { px = 0; for (;;) { if (regex) { found = window_copy_search_lr_regex(gd, - ssp->grid, &px, &width, py, px, - gd->sx, cis); + &px, &width, py, px, gd->sx, ®); if (!found) break; - } - else { + } else { found = window_copy_search_lr(gd, ssp->grid, - &px, py, px, gd->sx, cis); + &px, py, px, gd->sx, cis); if (!found) break; } nfound++; - if (px == data->cx && py == gd->hsize + data->cy - data->oy) + if (px == data->cx && + py == gd->hsize + data->cy - data->oy) which = nfound; b = (py * gd->sx) + px; @@ -2755,6 +2789,10 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, px++; } } + if (regex) { + free(sbuf); + regfree(®); + } if (which != -1) data->searchthis = 1 + nfound - which; From 3476eccf48001865ee43f98454d76895158063dc Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 07:52:07 +0000 Subject: [PATCH 0105/1006] Use a comparison to check for wrap and avoid an expensive modulus. --- window-copy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/window-copy.c b/window-copy.c index b2b61dc7..c00017b7 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2494,8 +2494,9 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, cells[cell].d = window_copy_cellstring(gl, px, &cells[cell].dlen); cell++; - px = (px + 1) % gd->sx; - if (px == 0) { + px++; + if (px == gd->sx) { + px = 0; pywrap++; gl = grid_peek_line(gd, pywrap); } From a5922546ac1e596fc37dc883cff12a9026f68d27 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 08:07:05 +0000 Subject: [PATCH 0106/1006] Do not go down the regex search path (which is expensive because we need to convert the grid data into a string for regexec and reverse it to find the grid position) if the search string does not contain any regex special characters. --- window-copy.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/window-copy.c b/window-copy.c index c00017b7..8cf499ca 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2685,23 +2685,27 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; + const char *str = data->searchstr; u_int fx, fy, endline; int wrapflag, cis, found; + if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') + regex = 0; + free(wp->searchstr); - wp->searchstr = xstrdup(data->searchstr); + wp->searchstr = xstrdup(str); wp->searchregex = regex; fx = data->cx; fy = screen_hsize(data->backing) - data->oy + data->cy; - screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0); + screen_init(&ss, screen_write_strlen("%s", str), 1, 0); screen_write_start(&ctx, NULL, &ss); - screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); + screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); screen_write_stop(&ctx); wrapflag = options_get_number(wp->window->options, "wrap-search"); - cis = window_copy_is_lowercase(data->searchstr); + cis = window_copy_is_lowercase(str); if (direction) { window_copy_move_right(s, &fx, &fy, wrapflag); From 9f378a163ff9457eb5b0398ea66263a32e58bba2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 1 Apr 2020 10:09:49 +0100 Subject: [PATCH 0107/1006] 3.1-rc4. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 78fbc8c9..9675660c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.1-rc3) +AC_INIT([tmux], 3.1-rc4) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 567b27e10a07e9ab8266e629edd348a1d432a873 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 09:36:37 +0000 Subject: [PATCH 0108/1006] Add a 10 second timeout to prevent searches taking too much time, from Anindya Mukherjee. --- window-copy.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/window-copy.c b/window-copy.c index 8c226a92..bc0cd849 100644 --- a/window-copy.c +++ b/window-copy.c @@ -261,6 +261,9 @@ struct window_copy_mode_data { int searchy; int searcho; + int timeout; /* search has timed out */ +#define WINDOW_COPY_SEARCH_TIMEOUT 10 + int jumptype; char jumpchar; @@ -316,6 +319,7 @@ window_copy_common_init(struct window_mode_entry *wme) } data->searchmark = NULL; data->searchx = data->searchy = data->searcho = -1; + data->timeout = 0; data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; @@ -680,8 +684,8 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1); screen_write_stop(&ctx); - if (search) - window_copy_search_marks(wme, NULL, 1); + if (search && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; @@ -1800,6 +1804,7 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; data->searchregex = 1; + data->timeout = 0; for (; np != 0; np--) window_copy_search_up(wme, 1); } @@ -1819,6 +1824,7 @@ window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; data->searchregex = 0; + data->timeout = 0; for (; np != 0; np--) window_copy_search_up(wme, 0); } @@ -1838,6 +1844,7 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHDOWN; data->searchregex = 1; + data->timeout = 0; for (; np != 0; np--) window_copy_search_down(wme, 1); } @@ -1857,6 +1864,7 @@ window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHDOWN; data->searchregex = 0; + data->timeout = 0; for (; np != 0; np--) window_copy_search_down(wme, 0); } @@ -1873,6 +1881,8 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) char prefix; enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; + data->timeout = 0; + prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; @@ -1924,6 +1934,8 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) char prefix; enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; + data->timeout = 0; + prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; @@ -2721,6 +2733,9 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') regex = 0; + if (data->timeout) + return (0); + free(wp->searchstr); wp->searchstr = xstrdup(str); wp->searchregex = regex; @@ -2768,6 +2783,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, u_int ssize = 1; char *sbuf; regex_t reg; + time_t tstart, t; if (ssp == NULL) { width = screen_write_strlen("%s", data->searchstr); @@ -2797,6 +2813,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, return (0); } } + time(&tstart); for (py = 0; py < gd->hsize + gd->sy; py++) { px = 0; for (;;) { @@ -2822,11 +2839,21 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, px++; } + + time(&t); + if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { + data->timeout = 1; + break; + } } if (regex) { free(sbuf); regfree(®); } + if (data->timeout) { + window_copy_clear_marks(wme); + return (1); + } if (which != -1) data->searchthis = 1 + nfound - which; @@ -2836,7 +2863,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, if (ssp == &ss) screen_free(&ss); - return (nfound); + return (1); } static void @@ -2895,8 +2922,15 @@ window_copy_write_line(struct window_mode_entry *wme, if (py == 0 && s->rupper < s->rlower && !data->hide_position) { if (data->searchmark == NULL) { - size = xsnprintf(hdr, sizeof hdr, - "[%u/%u]", data->oy, screen_hsize(data->backing)); + if (data->timeout) { + size = xsnprintf(hdr, sizeof hdr, + "(timed out) [%u/%u]", data->oy, + screen_hsize(data->backing)); + } else { + size = xsnprintf(hdr, sizeof hdr, + "[%u/%u]", data->oy, + screen_hsize(data->backing)); + } } else { if (data->searchthis == -1) { size = xsnprintf(hdr, sizeof hdr, From 05a15215c5d7b0f2062c7f91c7f9a3ec62d729f9 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 11:47:44 +0000 Subject: [PATCH 0109/1006] Do not ignore triple-click and send to pane. --- server-client.c | 1 - 1 file changed, 1 deletion(-) diff --git a/server-client.c b/server-client.c index 054e1161..70244620 100644 --- a/server-client.c +++ b/server-client.c @@ -506,7 +506,6 @@ server_client_check_mouse(struct client *c, struct key_event *event) type = TRIPLE; x = m->x, y = m->y, b = m->b; log_debug("triple-click at %u,%u", x, y); - ignore = 1; goto have_event; } } else { From 90f4e149c1b8c2211727d5e9c3eecf8f4dd9b151 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Apr 2020 05:35:15 +0000 Subject: [PATCH 0110/1006] Add a W position to display-menu -y to use the line above (or below) the status line containing the window list. Leave S meaning above (or below) all status lines. GitHub issue 2145. --- cmd-display-menu.c | 52 +++++++++++++++++++++++++++++++++++++--------- key-bindings.c | 6 +++--- tmux.1 | 2 +- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 3e756116..852c540e 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -65,12 +65,26 @@ static void cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, struct args *args, u_int *px, u_int *py, u_int w, u_int h) { + struct session *s = c->session; struct winlink *wl = item->target.wl; struct window_pane *wp = item->target.wp; + struct style_ranges *ranges; struct style_range *sr; const char *xp, *yp; - int at = status_at_line(c); - u_int ox, oy, sx, sy; + u_int line, ox, oy, sx, sy, lines; + + lines = status_line_size(c); + for (line = 0; line < lines; line++) { + ranges = &c->status.entries[line].ranges; + TAILQ_FOREACH(sr, ranges, entry) { + if (sr->type == STYLE_RANGE_WINDOW) + break; + } + if (sr != NULL) + break; + } + if (line == lines) + ranges = &c->status.entries[0].ranges; xp = args_get(args, 'x'); if (xp == NULL || strcmp(xp, "C") == 0) @@ -89,10 +103,10 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, else *px = 0; } else if (strcmp(xp, "W") == 0) { - if (at == -1) + if (status_at_line(c) == -1) *px = 0; else { - TAILQ_FOREACH(sr, &c->status.entries[0].ranges, entry) { + TAILQ_FOREACH(sr, ranges, entry) { if (sr->type != STYLE_RANGE_WINDOW) continue; if (sr->argument == (u_int)wl->idx) @@ -120,12 +134,30 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, } else if (strcmp(yp, "M") == 0 && item->shared->mouse.valid) *py = item->shared->mouse.y + h; else if (strcmp(yp, "S") == 0) { - if (at == -1) - *py = c->tty.sy; - else if (at == 0) - *py = status_line_size(c) + h; - else - *py = at; + if (options_get_number(s->options, "status-position") == 0) { + if (lines != 0) + *py = lines + h; + else + *py = 0; + } else { + if (lines != 0) + *py = c->tty.sy - lines; + else + *py = c->tty.sy; + } + } + else if (strcmp(yp, "W") == 0) { + if (options_get_number(s->options, "status-position") == 0) { + if (lines != 0) + *py = line + 1 + h; + else + *py = 0; + } else { + if (lines != 0) + *py = c->tty.sy - lines + line; + else + *py = c->tty.sy; + } } else *py = strtoul(yp, NULL, 10); if (*py < h) diff --git a/key-bindings.c b/key-bindings.c index a528a6c0..e9f175d0 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -317,7 +317,7 @@ key_bindings_init(void) "bind -N 'Resize the pane right' -r C-Right resize-pane -R", /* Menu keys */ - "bind < display-menu -xW -yS -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, + "bind < display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, "bind > display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU, /* Mouse button 1 down on pane. */ @@ -351,10 +351,10 @@ key_bindings_init(void) "bind -n WheelUpStatus previous-window", /* Mouse button 3 down on status left. */ - "bind -n MouseDown3StatusLeft display-menu -t= -xM -yS -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU, + "bind -n MouseDown3StatusLeft display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU, /* Mouse button 3 down on status line. */ - "bind -n MouseDown3Status display-menu -t= -xW -yS -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, + "bind -n MouseDown3Status display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, /* Mouse button 3 down on pane. */ "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", diff --git a/tmux.1 b/tmux.1 index ab214fe8..9346c443 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4985,7 +4985,7 @@ Both may be a row or column number, or one of the following special values: .It Li "R" Ta Fl x Ta "The right side of the terminal" .It Li "P" Ta "Both" Ta "The bottom left of the pane" .It Li "M" Ta "Both" Ta "The mouse position" -.It Li "W" Ta Fl x Ta "The window position on the status line" +.It Li "W" Ta "Both" Ta "The window position on the status line" .It Li "S" Ta Fl y Ta "The line above or below the status line" .El .Pp From a20d96000e7b04962105242883c91655833809da Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Apr 2020 17:03:10 +0000 Subject: [PATCH 0111/1006] Only search the visible part of the history when marking (highlighting) search terms, much faster than searching the whole history. --- window-copy.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index bc0cd849..1a558915 100644 --- a/window-copy.c +++ b/window-copy.c @@ -509,6 +509,8 @@ window_copy_pageup1(struct window_mode_entry *wme, int half_page) window_copy_cursor_end_of_line(wme); } + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -557,6 +559,8 @@ window_copy_pagedown(struct window_mode_entry *wme, int half_page, if (scroll_exit && data->oy == 0) return (1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); return (0); @@ -1049,6 +1053,8 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) data->cx = window_copy_find_length(wme, data->cy); data->oy = 0; + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1068,6 +1074,8 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs) data->cx = 0; data->oy = screen_hsize(data->backing); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -2200,6 +2208,8 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py) data->oy = gd->hsize - offset; } + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -2777,6 +2787,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; + const struct grid_line *gl; int found, cis, which = -1; int cflags = REG_EXTENDED; u_int px, py, b, nfound = 0, width; @@ -2814,7 +2825,15 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, } } time(&tstart); - for (py = 0; py < gd->hsize + gd->sy; py++) { + py = gd->hsize - data->oy; + if (py > 0) + py--; + for (; py > 0; py--) { + gl = grid_peek_line(gd, py); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + } + for (; py < gd->hsize - data->oy + gd->sy; py++) { px = 0; for (;;) { if (regex) { @@ -4190,6 +4209,8 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) return; data->oy -= ny; + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 0, 0); screen_write_start(&ctx, wp, NULL); @@ -4224,6 +4245,8 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) return; data->oy += ny; + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 0, 0); screen_write_start(&ctx, wp, NULL); From b65eab55057d68fee88795043bd407014829ced5 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Apr 2020 05:18:02 +0000 Subject: [PATCH 0112/1006] Check previous line rather than an extra line, from Anindya Mukherjee. --- window-copy.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/window-copy.c b/window-copy.c index 1a558915..149287b5 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2825,11 +2825,8 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, } } time(&tstart); - py = gd->hsize - data->oy; - if (py > 0) - py--; - for (; py > 0; py--) { - gl = grid_peek_line(gd, py); + for (py = gd->hsize - data->oy; py > 0; py--) { + gl = grid_peek_line(gd, py - 1); if (~gl->flags & GRID_LINE_WRAPPED) break; } From c9b9b0c7c3b071056c1e72bc03fde0f5771f809c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Apr 2020 12:59:22 +0000 Subject: [PATCH 0113/1006] Stop logging the entire command queue every time we add something, spotted by tb & sthen. --- cmd-queue.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 59c7a35c..a9e1dd3a 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -59,10 +59,6 @@ cmdq_append(struct client *c, struct cmdq_item *item) struct cmdq_list *queue = cmdq_get(c); struct cmdq_item *next; - TAILQ_FOREACH(next, queue, entry) { - log_debug("%s %s: queue %s (%u)", __func__, cmdq_name(c), - next->name, next->group); - } do { next = item->next; item->next = NULL; @@ -88,10 +84,6 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) struct cmdq_list *queue = after->queue; struct cmdq_item *next; - TAILQ_FOREACH(next, queue, entry) { - log_debug("%s %s: queue %s (%u)", __func__, cmdq_name(c), - next->name, next->group); - } do { next = item->next; item->next = after->next; From 832b8a8cf51ff0ea5137ae38210e067ffae169d8 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Apr 2020 13:54:31 +0000 Subject: [PATCH 0114/1006] Use new window and new pane as well for -P to new-session or new-window. --- cmd-new-session.c | 2 +- cmd-new-window.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index c76b564e..a75fc972 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -334,7 +334,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_SESSION_TEMPLATE; - cp = format_single(item, template, c, s, NULL, NULL); + cp = format_single(item, template, c, s, s->curw, NULL); cmdq_print(item, "%s", cp); free(cp); } diff --git a/cmd-new-window.c b/cmd-new-window.c index a4d6c014..033d208a 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -108,7 +108,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - cp = format_single(item, template, c, s, new_wl, NULL); + cp = format_single(item, template, c, s, new_wl, + new_wl->window->active); cmdq_print(item, "%s", cp); free(cp); } From fccce69cf08b220ecad690e22451afd487e4b639 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 5 Apr 2020 08:40:31 +0000 Subject: [PATCH 0115/1006] Add an argument to list-commands to show only a single command. --- cmd-list-keys.c | 14 +++++++++++--- tmux.1 | 7 +++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 34ed43bf..7e340516 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -47,8 +47,8 @@ const struct cmd_entry cmd_list_commands_entry = { .name = "list-commands", .alias = "lscm", - .args = { "F:", 0, 0 }, - .usage = "[-F format]", + .args = { "F:", 0, 1 }, + .usage = "[-F format] [command]", .flags = CMD_STARTSERVER|CMD_AFTERHOOK, .exec = cmd_list_keys_exec @@ -317,9 +317,12 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) const struct cmd_entry **entryp; const struct cmd_entry *entry; struct format_tree *ft; - const char *template, *s; + const char *template, *s, *command = NULL; char *line; + if (args->argc != 0) + command = args->argv[0]; + if ((template = args_get(args, 'F')) == NULL) { template = "#{command_list_name}" "#{?command_list_alias, (#{command_list_alias}),} " @@ -331,6 +334,11 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) for (entryp = cmd_table; *entryp != NULL; entryp++) { entry = *entryp; + if (command != NULL && + (strcmp(entry->name, command) != 0 && + (entry->alias == NULL || + strcmp(entry->alias, command) != 0))) + continue; format_add(ft, "command_list_name", "%s", entry->name); if (entry->alias != NULL) diff --git a/tmux.1 b/tmux.1 index 9346c443..ca50f524 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1048,9 +1048,12 @@ If is specified, list only clients connected to that session. .It Xo Ic list-commands .Op Fl F Ar format +.Op Ar command .Xc .D1 (alias: Ic lscm ) -List the syntax of all commands supported by +List the syntax of +.Ar command +or - if omitted - of all commands supported by .Nm . .It Ic list-sessions Op Fl F Ar format .D1 (alias: Ic ls ) @@ -2731,7 +2734,7 @@ command. .It Xo Ic list-keys .Op Fl 1aN .Op Fl P Ar prefix-string Fl T Ar key-table -.Op key +.Op Ar key .Xc .D1 (alias: Ic lsk ) List key bindings. From 938ad5a98c2a37938ea726a910e640a2a8aa8d26 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Apr 2020 13:54:31 +0000 Subject: [PATCH 0116/1006] Use new window and new pane as well for -P to new-session or new-window. --- cmd-new-session.c | 2 +- cmd-new-window.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index c76b564e..a75fc972 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -334,7 +334,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_SESSION_TEMPLATE; - cp = format_single(item, template, c, s, NULL, NULL); + cp = format_single(item, template, c, s, s->curw, NULL); cmdq_print(item, "%s", cp); free(cp); } diff --git a/cmd-new-window.c b/cmd-new-window.c index 9a1025e9..2fb92830 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -108,7 +108,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - cp = format_single(item, template, c, s, new_wl, NULL); + cp = format_single(item, template, c, s, new_wl, + new_wl->window->active); cmdq_print(item, "%s", cp); free(cp); } From 32340172603f03e55fe21071ede93652e560b29a Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 5 Apr 2020 08:40:31 +0000 Subject: [PATCH 0117/1006] Add an argument to list-commands to show only a single command. --- cmd-list-keys.c | 14 +++++++++++--- tmux.1 | 7 +++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 34ed43bf..7e340516 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -47,8 +47,8 @@ const struct cmd_entry cmd_list_commands_entry = { .name = "list-commands", .alias = "lscm", - .args = { "F:", 0, 0 }, - .usage = "[-F format]", + .args = { "F:", 0, 1 }, + .usage = "[-F format] [command]", .flags = CMD_STARTSERVER|CMD_AFTERHOOK, .exec = cmd_list_keys_exec @@ -317,9 +317,12 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) const struct cmd_entry **entryp; const struct cmd_entry *entry; struct format_tree *ft; - const char *template, *s; + const char *template, *s, *command = NULL; char *line; + if (args->argc != 0) + command = args->argv[0]; + if ((template = args_get(args, 'F')) == NULL) { template = "#{command_list_name}" "#{?command_list_alias, (#{command_list_alias}),} " @@ -331,6 +334,11 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) for (entryp = cmd_table; *entryp != NULL; entryp++) { entry = *entryp; + if (command != NULL && + (strcmp(entry->name, command) != 0 && + (entry->alias == NULL || + strcmp(entry->alias, command) != 0))) + continue; format_add(ft, "command_list_name", "%s", entry->name); if (entry->alias != NULL) diff --git a/tmux.1 b/tmux.1 index 03950c60..c7b40c16 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1036,9 +1036,12 @@ If is specified, list only clients connected to that session. .It Xo Ic list-commands .Op Fl F Ar format +.Op Ar command .Xc .D1 (alias: Ic lscm ) -List the syntax of all commands supported by +List the syntax of +.Ar command +or - if omitted - of all commands supported by .Nm . .It Ic list-sessions Op Fl F Ar format .D1 (alias: Ic ls ) @@ -2710,7 +2713,7 @@ command. .It Xo Ic list-keys .Op Fl 1aN .Op Fl P Ar prefix-string Fl T Ar key-table -.Op key +.Op Ar key .Xc .D1 (alias: Ic lsk ) List key bindings. From ac050b2583685d105f3dfe8ae7161925f30e889e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Apr 2020 12:59:22 +0000 Subject: [PATCH 0118/1006] Stop logging the entire command queue every time we add something, spotted by tb & sthen. --- cmd-queue.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 59c7a35c..a9e1dd3a 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -59,10 +59,6 @@ cmdq_append(struct client *c, struct cmdq_item *item) struct cmdq_list *queue = cmdq_get(c); struct cmdq_item *next; - TAILQ_FOREACH(next, queue, entry) { - log_debug("%s %s: queue %s (%u)", __func__, cmdq_name(c), - next->name, next->group); - } do { next = item->next; item->next = NULL; @@ -88,10 +84,6 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) struct cmdq_list *queue = after->queue; struct cmdq_item *next; - TAILQ_FOREACH(next, queue, entry) { - log_debug("%s %s: queue %s (%u)", __func__, cmdq_name(c), - next->name, next->group); - } do { next = item->next; item->next = after->next; From 8d2af4fb547edf55ffb34c8cf1c6ab0d179fd074 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Apr 2020 09:36:37 +0000 Subject: [PATCH 0119/1006] Add a 10 second timeout to prevent searches taking too much time, from Anindya Mukherjee. --- window-copy.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/window-copy.c b/window-copy.c index 8cf499ca..8f33914a 100644 --- a/window-copy.c +++ b/window-copy.c @@ -259,6 +259,9 @@ struct window_copy_mode_data { int searchy; int searcho; + int timeout; /* search has timed out */ +#define WINDOW_COPY_SEARCH_TIMEOUT 10 + int jumptype; char jumpchar; @@ -313,6 +316,7 @@ window_copy_common_init(struct window_mode_entry *wme) } data->searchmark = NULL; data->searchx = data->searchy = data->searcho = -1; + data->timeout = 0; data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; @@ -655,8 +659,8 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1); screen_write_stop(&ctx); - if (search) - window_copy_search_marks(wme, NULL, 1); + if (search && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; @@ -1771,6 +1775,7 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; data->searchregex = 1; + data->timeout = 0; for (; np != 0; np--) window_copy_search_up(wme, 1); } @@ -1790,6 +1795,7 @@ window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; data->searchregex = 0; + data->timeout = 0; for (; np != 0; np--) window_copy_search_up(wme, 0); } @@ -1809,6 +1815,7 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHDOWN; data->searchregex = 1; + data->timeout = 0; for (; np != 0; np--) window_copy_search_down(wme, 1); } @@ -1828,6 +1835,7 @@ window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHDOWN; data->searchregex = 0; + data->timeout = 0; for (; np != 0; np--) window_copy_search_down(wme, 0); } @@ -1844,6 +1852,8 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) char prefix; enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; + data->timeout = 0; + prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; @@ -1895,6 +1905,8 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) char prefix; enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; + data->timeout = 0; + prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; @@ -2692,6 +2704,9 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') regex = 0; + if (data->timeout) + return (0); + free(wp->searchstr); wp->searchstr = xstrdup(str); wp->searchregex = regex; @@ -2739,6 +2754,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, u_int ssize = 1; char *sbuf; regex_t reg; + time_t tstart, t; if (ssp == NULL) { width = screen_write_strlen("%s", data->searchstr); @@ -2768,6 +2784,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, return (0); } } + time(&tstart); for (py = 0; py < gd->hsize + gd->sy; py++) { px = 0; for (;;) { @@ -2793,11 +2810,21 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, px++; } + + time(&t); + if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { + data->timeout = 1; + break; + } } if (regex) { free(sbuf); regfree(®); } + if (data->timeout) { + window_copy_clear_marks(wme); + return (1); + } if (which != -1) data->searchthis = 1 + nfound - which; @@ -2807,7 +2834,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, if (ssp == &ss) screen_free(&ss); - return (nfound); + return (1); } static void @@ -2866,8 +2893,15 @@ window_copy_write_line(struct window_mode_entry *wme, if (py == 0 && s->rupper < s->rlower) { if (data->searchmark == NULL) { - size = xsnprintf(hdr, sizeof hdr, - "[%u/%u]", data->oy, screen_hsize(data->backing)); + if (data->timeout) { + size = xsnprintf(hdr, sizeof hdr, + "(timed out) [%u/%u]", data->oy, + screen_hsize(data->backing)); + } else { + size = xsnprintf(hdr, sizeof hdr, + "[%u/%u]", data->oy, + screen_hsize(data->backing)); + } } else { if (data->searchthis == -1) { size = xsnprintf(hdr, sizeof hdr, From 10975961de95ef3ef7066d6d6cc97ba207b88e6f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Apr 2020 17:03:10 +0000 Subject: [PATCH 0120/1006] Only search the visible part of the history when marking (highlighting) search terms, much faster than searching the whole history. --- window-copy.c | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/window-copy.c b/window-copy.c index 8f33914a..cbf63cc7 100644 --- a/window-copy.c +++ b/window-copy.c @@ -505,7 +505,9 @@ window_copy_pageup1(struct window_mode_entry *wme, int half_page) window_copy_cursor_end_of_line(wme); } - window_copy_update_selection(wme, 1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -553,7 +555,9 @@ window_copy_pagedown(struct window_mode_entry *wme, int half_page, if (scroll_exit && data->oy == 0) return (1); - window_copy_update_selection(wme, 1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); return (0); } @@ -1023,7 +1027,9 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) data->cx = window_copy_find_length(wme, data->cy); data->oy = 0; - window_copy_update_selection(wme, 1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1042,7 +1048,9 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs) data->cx = 0; data->oy = screen_hsize(data->backing); - window_copy_update_selection(wme, 1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -2171,7 +2179,9 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py) data->oy = gd->hsize - offset; } - window_copy_update_selection(wme, 1); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -2748,6 +2758,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; + const struct grid_line *gl; int found, cis, which = -1; int cflags = REG_EXTENDED; u_int px, py, b, nfound = 0, width; @@ -2785,7 +2796,15 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, } } time(&tstart); - for (py = 0; py < gd->hsize + gd->sy; py++) { + py = gd->hsize - data->oy; + if (py > 0) + py--; + for (; py > 0; py--) { + gl = grid_peek_line(gd, py); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + } + for (; py < gd->hsize - data->oy + gd->sy; py++) { px = 0; for (;;) { if (regex) { @@ -4153,7 +4172,9 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) return; data->oy -= ny; - window_copy_update_selection(wme, 0); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 0, 0); screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); @@ -4187,7 +4208,9 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) return; data->oy += ny; - window_copy_update_selection(wme, 0); + if (data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_update_selection(wme, 0, 0); screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); From bc36b473f1d33f92bb70e5bd68d31924b59be70c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Apr 2020 05:18:02 +0000 Subject: [PATCH 0121/1006] Check previous line rather than an extra line, from Anindya Mukherjee. --- window-copy.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/window-copy.c b/window-copy.c index cbf63cc7..7f3c0277 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2796,11 +2796,8 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, } } time(&tstart); - py = gd->hsize - data->oy; - if (py > 0) - py--; - for (; py > 0; py--) { - gl = grid_peek_line(gd, py); + for (py = gd->hsize - data->oy; py > 0; py--) { + gl = grid_peek_line(gd, py - 1); if (~gl->flags & GRID_LINE_WRAPPED) break; } From a4e19bcd8020f655f613604361d0f30384edbdc0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 6 Apr 2020 16:09:49 +0100 Subject: [PATCH 0122/1006] Various fixes for copy mode from master. --- cmd-copy-mode.c | 9 ++- tmux.1 | 7 ++- window-copy.c | 162 ++++++++++++++++++++++++++++++------------------ 3 files changed, 114 insertions(+), 64 deletions(-) diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index b35d0af1..bdb8245e 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = { .name = "copy-mode", .alias = NULL, - .args = { "Met:u", 0, 0 }, - .usage = "[-Mu] " CMD_TARGET_PANE_USAGE, + .args = { "eHMt:uq", 0, 0 }, + .usage = "[-eHMuq] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -61,6 +61,11 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) struct session *s; struct window_pane *wp = item->target.wp; + if (args_has(args, 'q')) { + window_pane_reset_mode_all(wp); + return (CMD_RETURN_NORMAL); + } + if (args_has(args, 'M')) { if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); diff --git a/tmux.1 b/tmux.1 index c7b40c16..615274bf 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1590,7 +1590,7 @@ The synopsis for the command is: .Bl -tag -width Ds .It Xo Ic copy-mode -.Op Fl Meu +.Op Fl eHMqu .Op Fl t Ar target-pane .Xc Enter copy mode. @@ -1600,6 +1600,11 @@ option scrolls one page up. .Fl M begins a mouse drag (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . +.Fl H +hides the position indicator in the top right. +.Fl q +cancels copy mode and any other modes. +.Pp .Fl e specifies that scrolling to the bottom of the history (to the visible screen) should exit copy mode. diff --git a/window-copy.c b/window-copy.c index 7f3c0277..149287b5 100644 --- a/window-copy.c +++ b/window-copy.c @@ -83,9 +83,10 @@ static void window_copy_update_cursor(struct window_mode_entry *, u_int, static void window_copy_start_selection(struct window_mode_entry *); static int window_copy_adjust_selection(struct window_mode_entry *, u_int *, u_int *); -static int window_copy_set_selection(struct window_mode_entry *, int); -static int window_copy_update_selection(struct window_mode_entry *, int); -static void window_copy_synchronize_cursor(struct window_mode_entry *); +static int window_copy_set_selection(struct window_mode_entry *, int, int); +static int window_copy_update_selection(struct window_mode_entry *, int, + int); +static void window_copy_synchronize_cursor(struct window_mode_entry *, int); static void *window_copy_get_selection(struct window_mode_entry *, size_t *); static void window_copy_copy_buffer(struct window_mode_entry *, const char *, void *, size_t); @@ -118,7 +119,7 @@ static void window_copy_cursor_next_word(struct window_mode_entry *, static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *, const char *, u_int *, u_int *); static void window_copy_cursor_next_word_end(struct window_mode_entry *, - const char *); + const char *, int); static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, const char *, int, u_int *, u_int *); static void window_copy_cursor_previous_word(struct window_mode_entry *, @@ -226,6 +227,7 @@ struct window_copy_mode_data { } lineflag; /* line selection mode */ int rectflag; /* in rectangle copy mode? */ int scroll_exit; /* exit on scroll to end? */ + int hide_position; /* hide position marker */ enum { SEL_CHAR, /* select one char at a time */ @@ -304,6 +306,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; if (wp->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; @@ -348,6 +351,7 @@ window_copy_init(struct window_mode_entry *wme, data->cy = data->backing->cy; data->scroll_exit = args_has(args, 'e'); + data->hide_position = args_has(args, 'H'); data->screen.cx = data->cx; data->screen.cy = data->cy; @@ -599,10 +603,31 @@ window_copy_next_paragraph(struct window_mode_entry *wme) window_copy_scroll_to(wme, ox, oy); } +char * +window_copy_get_word(struct window_pane *wp, u_int x, u_int y) +{ + struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->screen.grid; + + return (format_grid_word(gd, x, gd->hsize + y)); +} + +char * +window_copy_get_line(struct window_pane *wp, u_int y) +{ + struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->screen.grid; + + return (format_grid_line(gd, gd->hsize + y)); +} + static void window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) { struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->screen.grid; char *s; format_add(ft, "scroll_position", "%d", data->oy); @@ -622,13 +647,13 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) } else format_add(ft, "selection_active", "%d", 0); - s = format_grid_word(data->screen.grid, data->cx, data->cy); + s = format_grid_word(gd, data->cx, gd->hsize + data->cy); if (s != NULL) { format_add(ft, "copy_cursor_word", "%s", s); free(s); } - s = format_grid_line(data->screen.grid, data->cy); + s = format_grid_line(gd, gd->hsize + data->cy); if (s != NULL) { format_add(ft, "copy_cursor_line", "%s", s); free(s); @@ -751,9 +776,6 @@ window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) struct client *c = cs->c; struct mouse_event *m = cs->m; struct window_copy_mode_data *data = wme->data; - struct options *oo = cs->s->options; - - data->ws = options_get_string(oo, "word-separators"); if (m != NULL) { window_copy_start_drag(c, m); @@ -761,6 +783,7 @@ window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) } data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; window_copy_start_selection(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -773,6 +796,7 @@ window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs) data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; return (WINDOW_COPY_CMD_NOTHING); } @@ -785,7 +809,7 @@ window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs) data->cx = 0; data->cy = screen_size_y(&data->screen) - 1; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -842,12 +866,14 @@ window_copy_cmd_copy_line(struct window_copy_cmd_state *cs) struct session *s = cs->s; struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; char *prefix = NULL; if (cs->args->argc == 2) prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + data->selflag = SEL_CHAR; window_copy_cursor_start_of_line(wme); window_copy_start_selection(wme); for (; np > 1; np--) @@ -1119,7 +1145,7 @@ window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) data->cx = 0; data->cy = (screen_size_y(&data->screen) - 1) / 2; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1276,7 +1302,8 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_next_word_end(wme, "{[( "); + window_copy_cursor_next_word_end(wme, "{[( ", + 0); continue; } /* For vi, continue searching for bracket until EOL. */ @@ -1359,7 +1386,7 @@ window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, " "); + window_copy_cursor_next_word_end(wme, " ", 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1387,7 +1414,7 @@ window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, ws); + window_copy_cursor_next_word_end(wme, ws, 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1396,7 +1423,9 @@ window_copy_cmd_other_end(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; + struct window_copy_mode_data *data = wme->data; + data->selflag = SEL_CHAR; if ((np % 2) != 0) window_copy_other_end(wme); return (WINDOW_COPY_CMD_NOTHING); @@ -1577,12 +1606,12 @@ window_copy_cmd_select_line(struct window_copy_cmd_state *cs) window_copy_cursor_start_of_line(wme); data->selrx = data->cx; data->selry = screen_hsize(data->backing) + data->cy - data->oy; + data->endselrx = window_copy_find_length(wme, data->selry); + data->endselry = data->selry; window_copy_start_selection(wme); for (; np > 1; np--) window_copy_cursor_down(wme, 0); window_copy_cursor_end_of_line(wme); - data->endselrx = data->cx; - data->endselry = screen_hsize(data->backing) + data->cy - data->oy; return (WINDOW_COPY_CMD_REDRAW); } @@ -1593,7 +1622,6 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct session *s = cs->s; struct window_copy_mode_data *data = wme->data; - const char *ws; u_int px, py; data->lineflag = LINE_SEL_LEFT_RIGHT; @@ -1602,25 +1630,26 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) data->dx = data->cx; data->dy = screen_hsize(data->backing) + data->cy - data->oy; + data->ws = options_get_string(s->options, "word-separators"); + window_copy_cursor_previous_word(wme, data->ws, 0); px = data->cx; py = screen_hsize(data->backing) + data->cy - data->oy; - - ws = options_get_string(s->options, "word-separators"); - window_copy_cursor_previous_word(wme, ws, 0); - data->selrx = data->cx; - data->selry = screen_hsize(data->backing) + data->cy - data->oy; + data->selrx = px; + data->selry = py; window_copy_start_selection(wme); if (px >= window_copy_find_length(wme, py) || - !window_copy_in_set(wme, px + 1, py, ws)) - window_copy_cursor_next_word_end(wme, ws); + !window_copy_in_set(wme, px + 1, py, data->ws)) + window_copy_cursor_next_word_end(wme, data->ws, 1); else { window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 1)) window_copy_redraw_lines(wme, data->cy, 1); } data->endselrx = data->cx; data->endselry = screen_hsize(data->backing) + data->cy - data->oy; + if (data->dx > data->endselrx) + data->dx = data->endselrx; return (WINDOW_COPY_CMD_REDRAW); } @@ -1643,7 +1672,7 @@ window_copy_cmd_top_line(struct window_copy_cmd_state *cs) data->cx = 0; data->cy = 0; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -2888,7 +2917,7 @@ window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) lineno = screen_hsize(data->backing); data->oy = lineno; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -2907,7 +2936,7 @@ window_copy_write_line(struct window_mode_entry *wme, style_apply(&gc, oo, "mode-style"); gc.flags |= GRID_FLAG_NOPALETTE; - if (py == 0 && s->rupper < s->rlower) { + if (py == 0 && s->rupper < s->rlower && !data->hide_position) { if (data->searchmark == NULL) { if (data->timeout) { size = xsnprintf(hdr, sizeof hdr, @@ -3013,18 +3042,19 @@ window_copy_redraw_screen(struct window_mode_entry *wme) } static void -window_copy_synchronize_cursor_end(struct window_mode_entry *wme) +window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, + int no_reset) { struct window_copy_mode_data *data = wme->data; u_int xx, yy; - int begin = 0; yy = screen_hsize(data->backing) + data->cy - data->oy; switch (data->selflag) { case SEL_WORD: xx = data->cx; - if (data->ws == NULL) + if (no_reset) break; + begin = 0; if (data->dy > yy || (data->dy == yy && data->dx > xx)) { /* Right to left selection. */ window_copy_cursor_previous_word_pos(wme, data->ws, 0, @@ -3047,6 +3077,11 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme) } break; case SEL_LINE: + if (no_reset) { + xx = data->cx; + break; + } + begin = 0; if (data->dy > yy) { /* Right to left selection. */ xx = 0; @@ -3078,17 +3113,16 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme) } static void -window_copy_synchronize_cursor(struct window_mode_entry *wme) +window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset) { struct window_copy_mode_data *data = wme->data; switch (data->cursordrag) { case CURSORDRAG_ENDSEL: - window_copy_synchronize_cursor_end(wme); + window_copy_synchronize_cursor_end(wme, 0, no_reset); break; case CURSORDRAG_SEL: - data->selx = data->cx; - data->sely = screen_hsize(data->backing) + data->cy - data->oy; + window_copy_synchronize_cursor_end(wme, 1, no_reset); break; case CURSORDRAG_NONE: break; @@ -3130,7 +3164,7 @@ window_copy_start_selection(struct window_mode_entry *wme) data->cursordrag = CURSORDRAG_ENDSEL; - window_copy_set_selection(wme, 1); + window_copy_set_selection(wme, 1, 0); } static int @@ -3167,18 +3201,20 @@ window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx, } static int -window_copy_update_selection(struct window_mode_entry *wme, int may_redraw) +window_copy_update_selection(struct window_mode_entry *wme, int may_redraw, + int no_reset) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) return (0); - return (window_copy_set_selection(wme, may_redraw)); + return (window_copy_set_selection(wme, may_redraw, no_reset)); } static int -window_copy_set_selection(struct window_mode_entry *wme, int may_redraw) +window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, + int no_reset) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -3188,7 +3224,7 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw) u_int sx, sy, cy, endsx, endsy; int startrelpos, endrelpos; - window_copy_synchronize_cursor(wme); + window_copy_synchronize_cursor(wme, no_reset); /* Adjust the selection. */ sx = data->selx; @@ -3369,7 +3405,7 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, static void window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, - const char *prefix, const char *command) + const char *prefix, const char *cmd) { void *buf; size_t len; @@ -3379,7 +3415,7 @@ window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, if (buf == NULL) return; - job = job_run(command, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); + job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT, -1, -1); bufferevent_write(job_get_event(job), buf, len); window_copy_copy_buffer(wme, prefix, buf, len); } @@ -3498,6 +3534,7 @@ window_copy_clear_selection(struct window_mode_entry *wme) data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; + data->selflag = SEL_CHAR; py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); @@ -3543,7 +3580,7 @@ window_copy_cursor_start_of_line(struct window_mode_entry *wme) } } window_copy_update_cursor(wme, 0, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -3566,7 +3603,7 @@ window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) } window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -3599,7 +3636,7 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme) } window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -3650,7 +3687,7 @@ window_copy_other_end(struct window_mode_entry *wme) } else data->cy = cy + sely - yy; - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 1); window_copy_redraw_screen(wme); } @@ -3674,7 +3711,7 @@ window_copy_cursor_left(struct window_mode_entry *wme) window_copy_cursor_end_of_line(wme); } else if (cx > 0) { window_copy_update_cursor(wme, cx - 1, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } } @@ -3706,7 +3743,7 @@ window_copy_cursor_right(struct window_mode_entry *wme) cx++; } window_copy_update_cursor(wme, cx, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } } @@ -3739,7 +3776,7 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) } } else { window_copy_update_cursor(wme, data->lastcx, data->cy - 1); - if (window_copy_update_selection(wme, 1)) { + if (window_copy_update_selection(wme, 1, 0)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wme, data->cy, 1); else @@ -3785,7 +3822,7 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) window_copy_redraw_lines(wme, data->cy - 1, 2); } else { window_copy_update_cursor(wme, data->lastcx, data->cy + 1); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy - 1, 2); } @@ -3820,7 +3857,7 @@ window_copy_cursor_jump(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3847,7 +3884,7 @@ window_copy_cursor_jump_back(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3874,7 +3911,7 @@ window_copy_cursor_jump_to(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px - 1, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3904,7 +3941,7 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme) if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px + 1, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); return; } @@ -3953,7 +3990,7 @@ window_copy_cursor_next_word(struct window_mode_entry *wme, } while (expected == 1); window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -4008,7 +4045,7 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, static void window_copy_cursor_next_word_end(struct window_mode_entry *wme, - const char *separators) + const char *separators, int no_reset) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -4054,7 +4091,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, px--; window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, no_reset)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -4151,7 +4188,7 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, out: window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -4234,7 +4271,7 @@ window_copy_rectangle_toggle(struct window_mode_entry *wme) if (data->cx > px) window_copy_update_cursor(wme, px, data->cy); - window_copy_update_selection(wme, 1); + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -4266,7 +4303,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) struct window_pane *wp; struct window_mode_entry *wme; struct window_copy_mode_data *data; - u_int x, y; + u_int x, y, yg; if (c == NULL) return; @@ -4287,6 +4324,9 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) c->tty.mouse_drag_release = window_copy_drag_release; data = wme->data; + yg = screen_hsize(data->backing) + y - data->oy; + if (x < data->selrx || x > data->endselrx || yg != data->selry) + data->selflag = SEL_CHAR; switch (data->selflag) { case SEL_WORD: if (data->ws) { @@ -4342,7 +4382,7 @@ window_copy_drag_update(struct client *c, struct mouse_event *m) old_cy = data->cy; window_copy_update_cursor(wme, x, y); - if (window_copy_update_selection(wme, 1)) + if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_selection(wme, old_cy); if (old_cy != data->cy || old_cx == data->cx) { if (y == 0) { From 9077b212c3a210cf1764cf0662e98e3af5fcd3ef Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 6 Apr 2020 16:14:09 +0100 Subject: [PATCH 0123/1006] job_run needs fewer arguments. --- window-copy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index 149287b5..2a47e9b0 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3415,7 +3415,7 @@ window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, if (buf == NULL) return; - job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT, -1, -1); + job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); bufferevent_write(job_get_event(job), buf, len); window_copy_copy_buffer(wme, prefix, buf, len); } From 77b827f879e4dc296996b3f4345c38a7a57caf48 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 6 Apr 2020 17:51:34 +0000 Subject: [PATCH 0124/1006] Change copy mode to make copy of the pane history so it does not need to freeze updates (which does not play nicely with some applications, a longstanding problem) and will allow some other changes later. From Anindya Mukherjee. --- key-bindings.c | 2 ++ tmux.1 | 1 + tmux.h | 3 --- window-copy.c | 69 ++++++++++++++++++++++++++++++++++++-------------- window.c | 27 -------------------- 5 files changed, 53 insertions(+), 49 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index e9f175d0..3c6f8ff6 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -387,6 +387,7 @@ key_bindings_init(void) "bind -Tcopy-mode g command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode n send -X search-again", "bind -Tcopy-mode q send -X cancel", + "bind -Tcopy-mode r send -X refresh-from-pane", "bind -Tcopy-mode t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'", "bind -Tcopy-mode Home send -X start-of-line", "bind -Tcopy-mode End send -X end-of-line", @@ -489,6 +490,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi n send -X search-again", "bind -Tcopy-mode-vi o send -X other-end", "bind -Tcopy-mode-vi q send -X cancel", + "bind -Tcopy-mode-vi r send -X refresh-from-pane", "bind -Tcopy-mode-vi t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'", "bind -Tcopy-mode-vi v send -X rectangle-toggle", "bind -Tcopy-mode-vi w send -X next-word", diff --git a/tmux.1 b/tmux.1 index ca50f524..00231c08 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1513,6 +1513,7 @@ The following commands are supported in copy mode: .It Li "previous-space" Ta "B" Ta "" .It Li "previous-word" Ta "b" Ta "M-b" .It Li "rectangle-toggle" Ta "v" Ta "R" +.It Li "refresh-from-pane" Ta "r" Ta "r" .It Li "scroll-down" Ta "C-e" Ta "C-Down" .It Li "scroll-down-and-cancel" Ta "" Ta "" .It Li "scroll-up" Ta "C-y" Ta "C-Up" diff --git a/tmux.h b/tmux.h index 326f77dd..3cc46df1 100644 --- a/tmux.h +++ b/tmux.h @@ -904,7 +904,6 @@ struct window_pane { int fd; struct bufferevent *event; - u_int disabled; struct event resize_timer; @@ -925,8 +924,6 @@ struct window_pane { size_t status_size; TAILQ_HEAD (, window_mode_entry) modes; - struct event modetimer; - time_t modelast; char *searchstr; int searchregex; diff --git a/window-copy.c b/window-copy.c index 149287b5..5a1a03ec 100644 --- a/window-copy.c +++ b/window-copy.c @@ -130,6 +130,7 @@ static void window_copy_rectangle_toggle(struct window_mode_entry *); static void window_copy_move_mouse(struct mouse_event *); static void window_copy_drag_update(struct client *, struct mouse_event *); static void window_copy_drag_release(struct client *, struct mouse_event *); +static struct screen* window_copy_clone_screen(struct screen *src); const struct window_mode window_copy_mode = { .name = "copy-mode", @@ -205,6 +206,8 @@ struct window_copy_mode_data { struct screen *backing; int backing_written; /* backing display started */ + int viewmode; /* view mode entered */ + u_int oy; /* number of lines scrolled up */ u_int selx; /* beginning of selection */ @@ -295,6 +298,27 @@ window_copy_scroll_timer(__unused int fd, __unused short events, void *arg) } } +static struct screen * +window_copy_clone_screen(struct screen *src) +{ + struct screen *dst; + struct screen_write_ctx ctx; + + dst = xcalloc(1, sizeof *dst); + screen_init(dst, screen_size_x(src), + screen_hsize(src) + screen_size_y(src), src->grid->hlimit); + grid_duplicate_lines(dst->grid, 0, src->grid, 0, + screen_hsize(src) + screen_size_y(src)); + dst->grid->sy = screen_size_y(src); + dst->grid->hsize = screen_hsize(src); + + screen_write_start(&ctx, NULL, dst); + screen_write_cursormove(&ctx, src->cx, src->cy, 0); + screen_write_stop(&ctx); + + return (dst); +} + static struct window_copy_mode_data * window_copy_common_init(struct window_mode_entry *wme) { @@ -320,6 +344,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->searchmark = NULL; data->searchx = data->searchy = data->searcho = -1; data->timeout = 0; + data->viewmode = 0; data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; @@ -343,10 +368,7 @@ window_copy_init(struct window_mode_entry *wme, data = window_copy_common_init(wme); - if (wp->fd != -1 && wp->disabled++ == 0) - bufferevent_disable(wp->event, EV_READ|EV_WRITE); - - data->backing = &wp->base; + data->backing = window_copy_clone_screen(&wp->base); data->cx = data->backing->cx; data->cy = data->backing->cy; @@ -375,6 +397,7 @@ window_copy_view_init(struct window_mode_entry *wme, struct screen *s; data = window_copy_common_init(wme); + data->viewmode = 1; data->backing = s = xmalloc(sizeof *data->backing); screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX); @@ -385,23 +408,17 @@ window_copy_view_init(struct window_mode_entry *wme, static void window_copy_free(struct window_mode_entry *wme) { - struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; evtimer_del(&data->dragtimer); - if (wp->fd != -1 && --wp->disabled == 0) - bufferevent_enable(wp->event, EV_READ|EV_WRITE); - free(data->searchmark); free(data->searchstr); - if (data->backing != &wp->base) { - screen_free(data->backing); - free(data->backing); - } - screen_free(&data->screen); + screen_free(data->backing); + free(data->backing); + screen_free(&data->screen); free(data); } @@ -425,9 +442,6 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) struct grid_cell gc; u_int old_hsize, old_cy; - if (backing == &wp->base) - return; - memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); @@ -663,15 +677,13 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) static void window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) { - struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct screen_write_ctx ctx; int search; screen_resize(s, sx, sy, 1); - if (data->backing != &wp->base) - screen_resize(data->backing, sx, sy, 1); + screen_resize(data->backing, sx, sy, 1); if (data->cy > sy - 1) data->cy = sy - 1; @@ -1984,6 +1996,23 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) return (action); } +static enum window_copy_cmd_action +window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; + + if (data->viewmode) + return (WINDOW_COPY_CMD_NOTHING); + + screen_free(data->backing); + free(data->backing); + data->backing = window_copy_clone_screen(&wp->base); + + return (WINDOW_COPY_CMD_REDRAW); +} + static const struct { const char *command; int minargs; @@ -2089,6 +2118,8 @@ static const struct { window_copy_cmd_previous_word }, { "rectangle-toggle", 0, 0, 0, window_copy_cmd_rectangle_toggle }, + { "refresh-from-pane", 0, 0, 0, + window_copy_cmd_refresh_from_pane }, { "scroll-down", 0, 0, 1, window_copy_cmd_scroll_down }, { "scroll-down-and-cancel", 0, 0, 0, diff --git a/window.c b/window.c index 4ae67916..4c53d91c 100644 --- a/window.c +++ b/window.c @@ -1066,40 +1066,15 @@ window_pane_get_palette(struct window_pane *wp, int c) return (new); } -static void -window_pane_mode_timer(__unused int fd, __unused short events, void *arg) -{ - struct window_pane *wp = arg; - struct timeval tv = { .tv_sec = 10 }; - int n = 0; - - evtimer_del(&wp->modetimer); - evtimer_add(&wp->modetimer, &tv); - - log_debug("%%%u in mode: last=%ld", wp->id, (long)wp->modelast); - - if (wp->modelast < time(NULL) - WINDOW_MODE_TIMEOUT) { - if (ioctl(wp->fd, FIONREAD, &n) == -1 || n > 0) - window_pane_reset_mode_all(wp); - } -} - int window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode, struct cmd_find_state *fs, struct args *args) { - struct timeval tv = { .tv_sec = 10 }; struct window_mode_entry *wme; if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode) return (1); - wp->modelast = time(NULL); - if (TAILQ_EMPTY(&wp->modes)) { - evtimer_set(&wp->modetimer, window_pane_mode_timer, wp); - evtimer_add(&wp->modetimer, &tv); - } - TAILQ_FOREACH(wme, &wp->modes, entry) { if (wme->mode == mode) break; @@ -1141,7 +1116,6 @@ window_pane_reset_mode(struct window_pane *wp) next = TAILQ_FIRST(&wp->modes); if (next == NULL) { log_debug("%s: no next mode", __func__); - evtimer_del(&wp->modetimer); wp->screen = &wp->base; } else { log_debug("%s: next mode is %s", __func__, next->mode->name); @@ -1174,7 +1148,6 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { - wp->modelast = time(NULL); if (wme->mode->key != NULL) wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); return (0); From a2efdb21a84947fc8a84ea886d664bf428a0230a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 7 Apr 2020 13:33:00 +0000 Subject: [PATCH 0125/1006] Limit size to 1x1 (total size 3x3). --- popup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/popup.c b/popup.c index 299d4e76..d23c2001 100644 --- a/popup.c +++ b/popup.c @@ -200,9 +200,9 @@ popup_handle_drag(struct client *c, struct popup_data *pd, pd->dy = m->y - pd->py; server_redraw_client(c); } else if (pd->dragging == SIZE) { - if (m->x < pd->px + 2) + if (m->x < pd->px + 3) return; - if (m->y < pd->py + 2) + if (m->y < pd->py + 3) return; pd->sx = m->x - pd->px; pd->sy = m->y - pd->py; From eff881b15aba52841c7b41ff6b5dca3cf6984077 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 7 Apr 2020 13:38:30 +0000 Subject: [PATCH 0126/1006] Do not send mouse events if the program has not requested them. --- input-keys.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/input-keys.c b/input-keys.c index 5e54d121..e9c595b4 100644 --- a/input-keys.c +++ b/input-keys.c @@ -271,6 +271,8 @@ input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y, /* If this pane is not in button or all mode, discard motion events. */ if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0) return (0); + if ((s->mode & ALL_MOUSE_MODES) == 0) + return (0); /* * If this event is a release event and not in all mode, discard it. From 1c8f7c1f7afcc7d2a9fcef8d38e6c0e4451da659 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 7 Apr 2020 13:55:24 +0000 Subject: [PATCH 0127/1006] Do not restore history flag if it was never set. --- screen.c | 4 +++- tmux.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/screen.c b/screen.c index c3e5da70..898cc860 100644 --- a/screen.c +++ b/screen.c @@ -536,6 +536,7 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) grid_view_clear(s->grid, 0, 0, sx, sy, 8); + s->saved_flags = s->grid->flags; s->grid->flags &= ~GRID_HISTORY; } @@ -579,7 +580,8 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) * Turn history back on (so resize can use it) and then resize back to * the current size. */ - s->grid->flags |= GRID_HISTORY; + if (s->saved_flags & GRID_HISTORY) + s->grid->flags |= GRID_HISTORY; if (sy > s->saved_grid->sy || sx != s->saved_grid->sx) screen_resize(s, sx, sy, 1); diff --git a/tmux.h b/tmux.h index 3cc46df1..fbf10cf4 100644 --- a/tmux.h +++ b/tmux.h @@ -761,6 +761,7 @@ struct screen { u_int saved_cy; struct grid *saved_grid; struct grid_cell saved_cell; + int saved_flags; bitstr_t *tabs; struct screen_sel *sel; From d388dbdea9ceacacadc27a36ccc968fa7d6070ec Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 8 Apr 2020 10:58:09 +0000 Subject: [PATCH 0128/1006] Pass the cmd item to format expansion so that mouse formats work. --- cmd-display-menu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 852c540e..b4db7331 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -190,7 +190,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); if (args_has(args, 'T')) - title = format_single(NULL, args_get(args, 'T'), c, s, wl, wp); + title = format_single(item, args_get(args, 'T'), c, s, wl, wp); else title = xstrdup(""); @@ -298,13 +298,13 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) value = args_get(args, 'd'); if (value != NULL) - cwd = format_single(NULL, value, c, fs->s, fs->wl, fs->wp); + cwd = format_single(item, value, c, fs->s, fs->wl, fs->wp); else cwd = xstrdup(server_client_get_cwd(c, fs->s)); value = args_get(args, 'R'); if (value != NULL) - shellcmd = format_single(NULL, value, c, fs->s, fs->wl, fs->wp); + shellcmd = format_single(item, value, c, fs->s, fs->wl, fs->wp); if (args_has(args, 'K')) flags |= POPUP_WRITEKEYS; From 5d0eb619f18b1ed98d0ecf492dddf66ab49bed03 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 8 Apr 2020 11:26:07 +0000 Subject: [PATCH 0129/1006] Restore pane_current_path format from portable tmux, it is no longer used by default and is very useful. --- format.c | 16 ++++++++++++++++ procname.c | 15 +++++++++++++++ tmux.1 | 1 + tmux.h | 1 + 4 files changed, 33 insertions(+) diff --git a/format.c b/format.c index 815be8da..6300b332 100644 --- a/format.c +++ b/format.c @@ -741,6 +741,21 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe) free(cmd); } +/* Callback for pane_current_path. */ +static void +format_cb_current_path(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + char *cwd; + + if (wp == NULL) + return; + + cwd = get_proc_cwd(wp->fd); + if (cwd != NULL) + fe->value = xstrdup(cwd); +} + /* Callback for history_bytes. */ static void format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) @@ -2722,6 +2737,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_pid", "%ld", (long) wp->pid); format_add_cb(ft, "pane_start_command", format_cb_start_command); format_add_cb(ft, "pane_current_command", format_cb_current_command); + format_add_cb(ft, "pane_current_path", format_cb_current_path); format_add(ft, "cursor_x", "%u", wp->base.cx); format_add(ft, "cursor_y", "%u", wp->base.cy); diff --git a/procname.c b/procname.c index 07a8a56c..45e508ef 100644 --- a/procname.c +++ b/procname.c @@ -38,6 +38,7 @@ static struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); char *get_proc_name(int, char *); +char *get_proc_cwd(int); static struct kinfo_proc * cmp_procs(struct kinfo_proc *p1, struct kinfo_proc *p2) @@ -132,3 +133,17 @@ error: free(buf); return (NULL); } + +char * +get_proc_cwd(int fd) +{ + int name[] = { CTL_KERN, KERN_PROC_CWD, 0 }; + static char path[MAXPATHLEN]; + size_t pathlen = sizeof path; + + if ((name[2] = tcgetpgrp(fd)) == -1) + return (NULL); + if (sysctl(name, 3, path, &pathlen, NULL, 0) != 0) + return (NULL); + return (path); +} diff --git a/tmux.1 b/tmux.1 index 00231c08..c62d543b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4409,6 +4409,7 @@ The following variables are available, where appropriate: .It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window" .It Li "pane_bottom" Ta "" Ta "Bottom of pane" .It Li "pane_current_command" Ta "" Ta "Current command if available" +.It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" .It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" .It Li "pane_format" Ta "" Ta "1 if format is for a pane" diff --git a/tmux.h b/tmux.h index fbf10cf4..fd739974 100644 --- a/tmux.h +++ b/tmux.h @@ -2743,6 +2743,7 @@ int utf8_cstrhas(const char *, const struct utf8_data *); /* procname.c */ char *get_proc_name(int, char *); +char *get_proc_cwd(int); /* log.c */ void log_add_level(void); From ff135b34a41b388b9ac98e2f655cf487ace6aa43 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 06:28:55 +0000 Subject: [PATCH 0130/1006] Mention paste at same place as copy, suggested by John Boyle. --- tmux.1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tmux.1 b/tmux.1 index c62d543b..d17c19e9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1425,6 +1425,10 @@ This mode is entered with the command, bound to .Ql \&[ by default. +Copied text can be pasted with the +.Ic paste-buffer +command, bound to +.Ql \&] . .It View mode, which is like copy mode but is entered when a command that produces output, such as From 0e8710f507acda408e3daf1ad71b33997e126505 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 12:16:16 +0000 Subject: [PATCH 0131/1006] Wait until the initial command sequence is done before sending a device attributes request and other bits that prompt a reply from the terminal. This means that stray relies are not left on the terminal if the command has attached and then immediately detached and tmux will not be around to receive them. Prompted by a problem report from espie@. --- server-client.c | 2 ++ tmux.h | 1 + tty.c | 22 ++++++++++++++++------ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/server-client.c b/server-client.c index 70244620..5d39e492 100644 --- a/server-client.c +++ b/server-client.c @@ -1898,6 +1898,8 @@ server_client_command_done(struct cmdq_item *item, __unused void *data) if (~c->flags & CLIENT_ATTACHED) c->flags |= CLIENT_EXIT; + else if (~c->flags & CLIENT_DETACHING) + tty_send_requests(&c->tty); return (CMD_RETURN_NORMAL); } diff --git a/tmux.h b/tmux.h index fd739974..9344dfe3 100644 --- a/tmux.h +++ b/tmux.h @@ -1995,6 +1995,7 @@ int tty_init(struct tty *, struct client *, int, char *); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); void tty_start_tty(struct tty *); +void tty_send_requests(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); diff --git a/tty.c b/tty.c index 46ab1283..a7607cba 100644 --- a/tty.c +++ b/tty.c @@ -340,12 +340,7 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } - if (~tty->flags & TTY_HAVEDA) - tty_puts(tty, "\033[c"); - if (~tty->flags & TTY_HAVEDSR) - tty_puts(tty, "\033[1337n"); - } else - tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); + } evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); @@ -361,6 +356,21 @@ tty_start_tty(struct tty *tty) tty->mouse_drag_release = NULL; } +void +tty_send_requests(struct tty *tty) +{ + if (~tty->flags & TTY_STARTED) + return; + + if (tty_term_flag(tty->term, TTYC_XT)) { + if (~tty->flags & TTY_HAVEDA) + tty_puts(tty, "\033[c"); + if (~tty->flags & TTY_HAVEDSR) + tty_puts(tty, "\033[1337n"); + } else + tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); +} + void tty_stop_tty(struct tty *tty) { From c4d0089edb802763619724e405ac2715542969d5 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 13:49:21 +0000 Subject: [PATCH 0132/1006] Pass correct flags to fnmatch. --- window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window.c b/window.c index 4c53d91c..2169ede2 100644 --- a/window.c +++ b/window.c @@ -1213,7 +1213,7 @@ window_pane_search(struct window_pane *wp, const char *term, int regex, } log_debug("%s: %s", __func__, line); if (!regex) - found = (fnmatch(new, line, 0) == 0); + found = (fnmatch(new, line, flags) == 0); else found = (regexec(&r, line, 0, NULL, 0) == 0); free(line); From b96ac809018c461b55aed66459a68a71dd08047f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 13:52:31 +0000 Subject: [PATCH 0133/1006] Some unnecessary assignments and unused variables. --- client.c | 2 +- cmd-send-keys.c | 2 +- key-string.c | 4 +--- server-client.c | 7 +++---- window-buffer.c | 3 +-- window-copy.c | 1 - 6 files changed, 7 insertions(+), 12 deletions(-) diff --git a/client.c b/client.c index 37da9c67..5aae8d0d 100644 --- a/client.c +++ b/client.c @@ -644,7 +644,7 @@ client_read_open(void *data, size_t datalen) struct msg_read_done reply; struct client_file find, *cf; const int flags = O_NONBLOCK|O_RDONLY; - int error = 0; + int error; if (datalen < sizeof *msg) fatalx("bad MSG_READ_OPEN size"); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 5770572a..15967b0c 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -152,7 +152,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } if (wme != NULL && (args_has(args, 'X') || args->argc == 0)) { - if (wme == NULL || wme->mode->command == NULL) { + if (wme->mode->command == NULL) { cmdq_error(item, "not in a mode"); return (CMD_RETURN_ERROR); } diff --git a/key-string.c b/key-string.c index 76ee4fbe..2a0602b2 100644 --- a/key-string.c +++ b/key-string.c @@ -229,10 +229,8 @@ key_string_lookup_string(const char *string) key -= 64; else if (key == 32) key = 0; - else if (key == '?') - key = 127; else if (key == 63) - key = KEYC_BSPACE; + key = 127; else return (KEYC_UNKNOWN); modifiers &= ~KEYC_CTRL; diff --git a/server-client.c b/server-client.c index 5d39e492..b6ce94a9 100644 --- a/server-client.c +++ b/server-client.c @@ -606,10 +606,9 @@ have_event: wp = window_get_active_at(s->curw->window, px, py); if (wp != NULL) where = PANE; + else + return (KEYC_UNKNOWN); } - - if (where == NOWHERE) - return (KEYC_UNKNOWN); if (where == PANE) log_debug("mouse %u,%u on pane %%%u", x, y, wp->id); else if (where == BORDER) @@ -1543,7 +1542,7 @@ server_client_reset_state(struct client *c) struct window_pane *wp = w->active, *loop; struct screen *s; struct options *oo = c->session->options; - int mode, cursor = 0; + int mode, cursor; u_int cx = 0, cy = 0, ox, oy, sx, sy; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) diff --git a/window-buffer.c b/window-buffer.c index bbc5c744..37707233 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -348,9 +348,8 @@ window_buffer_do_paste(void *modedata, void *itemdata, struct client *c, { struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item = itemdata; - struct paste_buffer *pb; - if ((pb = paste_get_name(item->name)) != NULL) + if (paste_get_name(item->name) != NULL) mode_tree_run_command(c, NULL, data->command, item->name); } diff --git a/window-copy.c b/window-copy.c index 5a1a03ec..ca938fdd 100644 --- a/window-copy.c +++ b/window-copy.c @@ -4151,7 +4151,6 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, data->oy >= screen_hsize(data->backing) - 1)) goto out; - py--; py = screen_hsize(data->backing) + data->cy - data->oy; From 315961faeceefae30d3fcce28c3171fdec7e5c5d Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 13:53:50 +0000 Subject: [PATCH 0134/1006] Some more, and use of wp->window before wp NULL check in format.c. --- cmd-switch-client.c | 2 +- format.c | 6 ++++-- grid.c | 8 ++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 61677761..cf84c319 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -73,7 +73,6 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); s = item->target.s; wl = item->target.wl; - w = wl->window; wp = item->target.wp; if (args_has(args, 'r')) @@ -115,6 +114,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) if (item->client == NULL) return (CMD_RETURN_NORMAL); if (wl != NULL && wp != NULL) { + w = wl->window; if (window_push_zoom(w, args_has(self->args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); diff --git a/format.c b/format.c index 6300b332..24710f15 100644 --- a/format.c +++ b/format.c @@ -900,11 +900,12 @@ static void format_cb_pane_at_top(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; - struct window *w = wp->window; + struct window *w; int status, flag; if (wp == NULL) return; + w = wp->window; status = options_get_number(w->options, "pane-border-status"); if (status == PANE_STATUS_TOP) @@ -919,11 +920,12 @@ static void format_cb_pane_at_bottom(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; - struct window *w = wp->window; + struct window *w; int status, flag; if (wp == NULL) return; + w = wp->window; status = options_get_number(w->options, "pane-border-status"); if (status == PANE_STATUS_BOTTOM) diff --git a/grid.c b/grid.c index 9678bf59..f33bc98d 100644 --- a/grid.c +++ b/grid.c @@ -1343,17 +1343,13 @@ grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy) void grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy) { - u_int yy, ax = 0, ay = 0; + u_int yy, ay = 0; for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) { if (ay == wy) break; - if (gd->linedata[yy].flags & GRID_LINE_WRAPPED) - ax += gd->linedata[yy].cellused; - else { - ax = 0; + if (~gd->linedata[yy].flags & GRID_LINE_WRAPPED) ay++; - } } /* From 5288801d3e2e06b97bda336835491c11eb8f9f76 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 13:54:38 +0000 Subject: [PATCH 0135/1006] Do not try to use the client if the item containing it is NULL. --- cmd-find.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index e3e14f55..81afb423 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -1231,29 +1231,31 @@ no_pane: static struct client * cmd_find_current_client(struct cmdq_item *item, int quiet) { - struct client *c; + struct client *c = NULL, *found; struct session *s; struct window_pane *wp; struct cmd_find_state fs; - if (item->client != NULL && item->client->session != NULL) - return (item->client); + if (item != NULL) + c = item->client; + if (c != NULL && c->session != NULL) + return (c); - c = NULL; - if ((wp = cmd_find_inside_pane(item->client)) != NULL) { + found = NULL; + if (c != NULL && (wp = cmd_find_inside_pane(c)) != NULL) { cmd_find_clear_state(&fs, CMD_FIND_QUIET); fs.w = wp->window; if (cmd_find_best_session_with_window(&fs) == 0) - c = cmd_find_best_client(fs.s); + found = cmd_find_best_client(fs.s); } else { s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET); if (s != NULL) - c = cmd_find_best_client(s); + found = cmd_find_best_client(s); } - if (c == NULL && !quiet) + if (found == NULL && item != NULL && !quiet) cmdq_error(item, "no current client"); - log_debug("%s: no target, return %p", __func__, c); - return (c); + log_debug("%s: no target, return %p", __func__, found); + return (found); } /* Find the target client or report an error and return NULL. */ From 886fdb1f7e185d47d8ddff0eb3bd1b1835f0d0c9 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 13:56:46 +0000 Subject: [PATCH 0136/1006] A couple of other redundant checks/assignments. --- cmd-list-keys.c | 4 ++-- cmd-show-options.c | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 7e340516..e1ec4d3c 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -269,7 +269,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) tmpsize *= 2; tmp = xrealloc(tmp, tmpsize); } - tmpused = strlcat(tmp, cp, tmpsize); + strlcat(tmp, cp, tmpsize); tmpused = strlcat(tmp, " ", tmpsize); free(cp); @@ -279,7 +279,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) tmpsize *= 2; tmp = xrealloc(tmp, tmpsize); } - tmpused = strlcat(tmp, cp, tmpsize); + strlcat(tmp, cp, tmpsize); tmpused = strlcat(tmp, " ", tmpsize); free(cp); diff --git a/cmd-show-options.c b/cmd-show-options.c index 0b9eb096..b5c5f59d 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -212,11 +212,9 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, if ((self->entry != &cmd_show_hooks_entry && !args_has(self->args, 'H') && - oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK)) || (self->entry == &cmd_show_hooks_entry && - (oe == NULL || - (~oe->flags & OPTIONS_TABLE_IS_HOOK)))) + (~oe->flags & OPTIONS_TABLE_IS_HOOK))) continue; o = options_get_only(oo, oe->name); From b0b07fb585310175b22d1e1f14db42138f08cd6d Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 13:57:18 +0000 Subject: [PATCH 0137/1006] Tweak how the default size is worked out so it is more obvious. --- cmd-new-session.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index a75fc972..725448fd 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -207,7 +207,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) goto fail; } } - } + } else + dsx = 80; if (args_has(args, 'y')) { tmp = args_get(args, 'y'); if (strcmp(tmp, "-") == 0) { @@ -222,7 +223,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) goto fail; } } - } + } else + dsy = 24; /* Find new session size. */ if (!detached && !is_control) { @@ -233,13 +235,14 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) } else { tmp = options_get_string(global_s_options, "default-size"); if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) { - sx = 80; - sy = 24; - } - if (args_has(args, 'x')) sx = dsx; - if (args_has(args, 'y')) sy = dsy; + } else { + if (args_has(args, 'x')) + sx = dsx; + if (args_has(args, 'y')) + sy = dsy; + } } if (sx == 0) sx = 1; From e9e5facb0e0d0cb374b17f6818b035156886bcac Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 14:23:34 +0000 Subject: [PATCH 0138/1006] Some minor style nits. --- regsub.c | 3 +-- tmux.h | 1 - window-copy.c | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/regsub.c b/regsub.c index 22e236dc..4039b9be 100644 --- a/regsub.c +++ b/regsub.c @@ -24,8 +24,7 @@ #include "tmux.h" static void -regsub_copy(char **buf, size_t *len, const char *text, size_t start, - size_t end) +regsub_copy(char **buf, size_t *len, const char *text, size_t start, size_t end) { size_t add = end - start; diff --git a/tmux.h b/tmux.h index 9344dfe3..e66cbb39 100644 --- a/tmux.h +++ b/tmux.h @@ -2469,7 +2469,6 @@ void screen_select_cell(struct screen *, struct grid_cell *, void screen_alternate_on(struct screen *, struct grid_cell *, int); void screen_alternate_off(struct screen *, struct grid_cell *, int); - /* window.c */ extern struct windows windows; extern struct window_pane_tree all_window_panes; diff --git a/window-copy.c b/window-copy.c index ca938fdd..668a1b50 100644 --- a/window-copy.c +++ b/window-copy.c @@ -4359,7 +4359,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) data->selflag = SEL_CHAR; switch (data->selflag) { case SEL_WORD: - if (data->ws) { + if (data->ws != NULL) { window_copy_update_cursor(wme, x, y); window_copy_cursor_previous_word_pos(wme, data->ws, 0, &x, &y); From 26f5dfbe46c75f8b907ee75daaadde9a310d8dfb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 14:30:28 +0000 Subject: [PATCH 0139/1006] Fix history-bottom to use the right line when working out the length. --- window-copy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/window-copy.c b/window-copy.c index 668a1b50..14f7a284 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1055,14 +1055,15 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; + struct screen *s = data->backing; u_int oy; - oy = screen_hsize(data->backing) + data->cy - data->oy; + oy = screen_hsize(s) + data->cy - data->oy; if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) window_copy_other_end(wme); data->cy = screen_size_y(&data->screen) - 1; - data->cx = window_copy_find_length(wme, data->cy); + data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy); data->oy = 0; if (data->searchmark != NULL && !data->timeout) From a1fc8f8b23bf6371877ff93e123b735d2c7d596d Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Apr 2020 15:35:27 +0000 Subject: [PATCH 0140/1006] More style nits. --- format-draw.c | 2 +- format.c | 4 ++-- tmux.c | 2 +- window-tree.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/format-draw.c b/format-draw.c index 85248aa6..3ac33ce4 100644 --- a/format-draw.c +++ b/format-draw.c @@ -804,7 +804,7 @@ format_width(const char *expanded) if (cp[0] == '#' && cp[1] == '[') { end = format_skip(cp + 2, "]"); if (end == NULL) - return 0; + return (0); cp = end + 1; } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) diff --git a/format.c b/format.c index 24710f15..efbf5c62 100644 --- a/format.c +++ b/format.c @@ -1617,7 +1617,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) return (NULL); } *s = cp + 1; - return list; + return (list); } /* Match against an fnmatch(3) pattern or regular expression. */ @@ -1909,7 +1909,7 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, free(right); free(left); - return value; + return (value); fail: free(right); diff --git a/tmux.c b/tmux.c index 2055a894..eac8c12f 100644 --- a/tmux.c +++ b/tmux.c @@ -224,7 +224,7 @@ getversion(void) fatalx("uname failed"); xasprintf(&version, "openbsd-%s", u.release); } - return version; + return (version); } int diff --git a/window-tree.c b/window-tree.c index 4d5b4a1e..d32358a5 100644 --- a/window-tree.c +++ b/window-tree.c @@ -833,7 +833,7 @@ window_tree_search(__unused void *modedata, void *itemdata, const char *ss) return (0); retval = (strstr(cmd, ss) != NULL); free(cmd); - return retval; + return (retval); } return (0); } From c0602f357d7b48ab830f93b446cb1830c5df7a0f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Apr 2020 07:44:26 +0000 Subject: [PATCH 0141/1006] Now that copy mode copies the pane content rather than keeping a reference to it, it isn't necessary that the pane in copy mode is the same as the one copying from. Add a -s flag to copy-mode to specify a different pane for the source content. This means it is possible to view two places in a pane's history at the same time in different panes, or copy from a pane's history into an editor or shell in the same pane. From Anindya Mukherjee. --- cfg.c | 2 +- cmd-choose-tree.c | 2 +- cmd-copy-mode.c | 15 ++++++++++----- cmd-find-window.c | 3 ++- cmd-queue.c | 6 ++++-- cmd-run-shell.c | 2 +- tmux.1 | 6 ++++++ tmux.h | 5 +++-- window-copy.c | 49 ++++++++++++++++++++++++++--------------------- window.c | 6 ++++-- 10 files changed, 59 insertions(+), 37 deletions(-) diff --git a/cfg.c b/cfg.c index 92c87225..6740bce4 100644 --- a/cfg.c +++ b/cfg.c @@ -284,7 +284,7 @@ cfg_show_causes(struct session *s) wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) - window_pane_set_mode(wp, &window_view_mode, NULL, NULL); + window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); for (i = 0; i < cfg_ncauses; i++) { window_copy_add(wp, "%s", cfg_causes[i]); free(cfg_causes[i]); diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 8178ec9f..e4cc754e 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -86,6 +86,6 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) } else mode = &window_tree_mode; - window_pane_set_mode(wp, mode, &item->target, args); + window_pane_set_mode(wp, NULL, mode, &item->target, args); return (CMD_RETURN_NORMAL); } diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index bdb8245e..5ce7a2c4 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -30,9 +30,10 @@ const struct cmd_entry cmd_copy_mode_entry = { .name = "copy-mode", .alias = NULL, - .args = { "eHMt:uq", 0, 0 }, - .usage = "[-eHMuq] " CMD_TARGET_PANE_USAGE, + .args = { "eHMs:t:uq", 0, 0 }, + .usage = "[-eHMuq] [-s src-pane] " CMD_TARGET_PANE_USAGE, + .source = { 's', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, @@ -59,7 +60,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) struct cmdq_shared *shared = item->shared; struct client *c = item->client; struct session *s; - struct window_pane *wp = item->target.wp; + struct window_pane *wp = item->target.wp, *swp; if (args_has(args, 'q')) { window_pane_reset_mode_all(wp); @@ -74,11 +75,15 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) } if (self->entry == &cmd_clock_mode_entry) { - window_pane_set_mode(wp, &window_clock_mode, NULL, NULL); + window_pane_set_mode(wp, NULL, &window_clock_mode, NULL, NULL); return (CMD_RETURN_NORMAL); } - if (!window_pane_set_mode(wp, &window_copy_mode, NULL, args)) { + if (args_has(args, 's')) + swp = item->source.wp; + else + swp = wp; + if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) { if (args_has(args, 'M')) window_copy_start_drag(c, &shared->mouse); } diff --git a/cmd-find-window.c b/cmd-find-window.c index c29878b5..774c0bb9 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -116,7 +116,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) args_set(new_args, 'Z', NULL); args_set(new_args, 'f', filter); - window_pane_set_mode(wp, &window_tree_mode, &item->target, new_args); + window_pane_set_mode(wp, NULL, &window_tree_mode, &item->target, + new_args); args_free(new_args); free(filter); diff --git a/cmd-queue.c b/cmd-queue.c index a9e1dd3a..42e7d31b 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -511,8 +511,10 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) } else { wp = c->session->curw->window->active; wme = TAILQ_FIRST(&wp->modes); - if (wme == NULL || wme->mode != &window_view_mode) - window_pane_set_mode(wp, &window_view_mode, NULL, NULL); + if (wme == NULL || wme->mode != &window_view_mode) { + window_pane_set_mode(wp, NULL, &window_view_mode, NULL, + NULL); + } window_copy_add(wp, "%s", msg); } diff --git a/cmd-run-shell.c b/cmd-run-shell.c index a57beb83..7ce6d55a 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -82,7 +82,7 @@ cmd_run_shell_print(struct job *job, const char *msg) wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) - window_pane_set_mode(wp, &window_view_mode, NULL, NULL); + window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); window_copy_add(wp, "%s", msg); } diff --git a/tmux.1 b/tmux.1 index d17c19e9..5098895c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1608,6 +1608,7 @@ command is: .Bl -tag -width Ds .It Xo Ic copy-mode .Op Fl eHMqu +.Op Fl s Ar src-pane .Op Fl t Ar target-pane .Xc Enter copy mode. @@ -1621,6 +1622,11 @@ begins a mouse drag (only valid if bound to a mouse key binding, see hides the position indicator in the top right. .Fl q cancels copy mode and any other modes. +.Fl s +copies from +.Ar src-pane +instead of +.Ar target-pane. .Pp .Fl e specifies that scrolling to the bottom of the history (to the visible screen) diff --git a/tmux.h b/tmux.h index e66cbb39..5cc192fe 100644 --- a/tmux.h +++ b/tmux.h @@ -848,6 +848,7 @@ struct window_mode { /* Active window mode. */ struct window_mode_entry { struct window_pane *wp; + struct window_pane *swp; const struct window_mode *mode; void *data; @@ -2537,8 +2538,8 @@ void window_pane_unset_palette(struct window_pane *, u_int); void window_pane_reset_palette(struct window_pane *); int window_pane_get_palette(struct window_pane *, int); int window_pane_set_mode(struct window_pane *, - const struct window_mode *, struct cmd_find_state *, - struct args *); + struct window_pane *, const struct window_mode *, + struct cmd_find_state *, struct args *); void window_pane_reset_mode(struct window_pane *); void window_pane_reset_mode_all(struct window_pane *); int window_pane_key(struct window_pane *, struct client *, diff --git a/window-copy.c b/window-copy.c index 14f7a284..995dc35c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -130,7 +130,7 @@ static void window_copy_rectangle_toggle(struct window_mode_entry *); static void window_copy_move_mouse(struct mouse_event *); static void window_copy_drag_update(struct client *, struct mouse_event *); static void window_copy_drag_release(struct client *, struct mouse_event *); -static struct screen* window_copy_clone_screen(struct screen *src); +static struct screen* window_copy_clone_screen(struct screen *, struct screen*); const struct window_mode window_copy_mode = { .name = "copy-mode", @@ -299,21 +299,29 @@ window_copy_scroll_timer(__unused int fd, __unused short events, void *arg) } static struct screen * -window_copy_clone_screen(struct screen *src) +window_copy_clone_screen(struct screen *src, struct screen *hint) { struct screen *dst; struct screen_write_ctx ctx; + u_int dy, sy; dst = xcalloc(1, sizeof *dst); - screen_init(dst, screen_size_x(src), - screen_hsize(src) + screen_size_y(src), src->grid->hlimit); - grid_duplicate_lines(dst->grid, 0, src->grid, 0, - screen_hsize(src) + screen_size_y(src)); - dst->grid->sy = screen_size_y(src); - dst->grid->hsize = screen_hsize(src); + + sy = screen_hsize(src) + screen_size_y(src); + if (screen_size_y(hint) > sy) + dy = screen_size_y(hint); + else + dy = sy; + screen_init(dst, screen_size_x(src), dy, src->grid->hlimit); + + grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy); + if (screen_size_y(hint) < sy) { + dst->grid->sy = screen_size_y(hint); + dst->grid->hsize = sy - screen_size_y(hint); + } screen_write_start(&ctx, NULL, dst); - screen_write_cursormove(&ctx, src->cx, src->cy, 0); + screen_write_cursormove(&ctx, 0, dst->grid->sy - 1, 0); screen_write_stop(&ctx); return (dst); @@ -361,14 +369,14 @@ static struct screen * window_copy_init(struct window_mode_entry *wme, __unused struct cmd_find_state *fs, struct args *args) { - struct window_pane *wp = wme->wp; + struct window_pane *wp = wme->swp; struct window_copy_mode_data *data; struct screen_write_ctx ctx; u_int i; data = window_copy_common_init(wme); - data->backing = window_copy_clone_screen(&wp->base); + data->backing = window_copy_clone_screen(&wp->base, &data->screen); data->cx = data->backing->cx; data->cy = data->backing->cy; @@ -2001,7 +2009,7 @@ static enum window_copy_cmd_action window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct window_pane *wp = wme->wp; + struct window_pane *wp = wme->swp; struct window_copy_mode_data *data = wme->data; if (data->viewmode) @@ -2009,7 +2017,7 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) screen_free(data->backing); free(data->backing); - data->backing = window_copy_clone_screen(&wp->base); + data->backing = window_copy_clone_screen(&wp->base, &data->screen); return (WINDOW_COPY_CMD_REDRAW); } @@ -2964,6 +2972,7 @@ window_copy_write_line(struct window_mode_entry *wme, struct grid_cell gc; char hdr[512]; size_t size = 0; + u_int hsize = screen_hsize(data->backing); style_apply(&gc, oo, "mode-style"); gc.flags |= GRID_FLAG_NOPALETTE; @@ -2972,23 +2981,20 @@ window_copy_write_line(struct window_mode_entry *wme, if (data->searchmark == NULL) { if (data->timeout) { size = xsnprintf(hdr, sizeof hdr, - "(timed out) [%u/%u]", data->oy, - screen_hsize(data->backing)); + "(timed out) [%u/%u]", data->oy, hsize); } else { size = xsnprintf(hdr, sizeof hdr, - "[%u/%u]", data->oy, - screen_hsize(data->backing)); + "[%u/%u]", data->oy, hsize); } } else { if (data->searchthis == -1) { size = xsnprintf(hdr, sizeof hdr, "(%u results) [%d/%u]", data->searchcount, - data->oy, screen_hsize(data->backing)); + data->oy, hsize); } else { size = xsnprintf(hdr, sizeof hdr, "(%u/%u results) [%d/%u]", data->searchthis, - data->searchcount, data->oy, - screen_hsize(data->backing)); + data->searchcount, data->oy, hsize); } } if (size > screen_size_x(s)) @@ -3000,8 +3006,7 @@ window_copy_write_line(struct window_mode_entry *wme, if (size < screen_size_x(s)) { screen_write_cursormove(ctx, 0, py, 0); - screen_write_copy(ctx, data->backing, 0, - (screen_hsize(data->backing) - data->oy) + py, + screen_write_copy(ctx, data->backing, 0, hsize - data->oy + py, screen_size_x(s) - size, 1, data->searchmark, &gc); } diff --git a/window.c b/window.c index 2169ede2..38e31810 100644 --- a/window.c +++ b/window.c @@ -1067,8 +1067,9 @@ window_pane_get_palette(struct window_pane *wp, int c) } int -window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode, - struct cmd_find_state *fs, struct args *args) +window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, + const struct window_mode *mode, struct cmd_find_state *fs, + struct args *args) { struct window_mode_entry *wme; @@ -1085,6 +1086,7 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode, } else { wme = xcalloc(1, sizeof *wme); wme->wp = wp; + wme->swp = swp; wme->mode = mode; wme->prefix = 1; TAILQ_INSERT_HEAD(&wp->modes, wme, entry); From 1c433f135436664e3a369edbc43cb5cd057a8ae4 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Apr 2020 20:53:54 +0000 Subject: [PATCH 0142/1006] Remove unused define, also a man fix from jmc. --- tmux.1 | 2 +- tmux.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 5098895c..64164f09 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1626,7 +1626,7 @@ cancels copy mode and any other modes. copies from .Ar src-pane instead of -.Ar target-pane. +.Ar target-pane . .Pp .Fl e specifies that scrolling to the bottom of the history (to the visible screen) diff --git a/tmux.h b/tmux.h index 5cc192fe..8f8640bf 100644 --- a/tmux.h +++ b/tmux.h @@ -843,7 +843,6 @@ struct window_mode { void (*formats)(struct window_mode_entry *, struct format_tree *); }; -#define WINDOW_MODE_TIMEOUT 180 /* Active window mode. */ struct window_mode_entry { From 70534cfde68d7f9002a26e8e23d767b05967f31e Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 12 Apr 2020 08:13:41 +0000 Subject: [PATCH 0143/1006] Clarify a couple of style options. --- tmux.1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tmux.1 b/tmux.1 index 64164f09..0f3204b6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3318,6 +3318,9 @@ with .Fl np . .It Ic message-command-style Ar style Set status line message command style. +This is used for the command prompt with +.Xr vi 1 +keys when in command mode. For how to specify .Ar style , see the @@ -3325,6 +3328,7 @@ see the section. .It Ic message-style Ar style Set status line message style. +This is used for messages and for the command prompt. For how to specify .Ar style , see the From 756591b4ca28089879ef785bde3a6fb30e9f5943 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 12 Apr 2020 08:36:18 +0000 Subject: [PATCH 0144/1006] Add a -f filter argument to the list commands like choose-tree. --- cmd-list-buffers.c | 24 +++++++++++++++++------- cmd-list-panes.c | 24 +++++++++++++++++------- cmd-list-sessions.c | 24 +++++++++++++++++------- cmd-list-windows.c | 24 +++++++++++++++++------- tmux.1 | 36 +++++++++++++++++++++++++++--------- 5 files changed, 95 insertions(+), 37 deletions(-) diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 0457a62d..2da8a518 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_buffers_entry = { .name = "list-buffers", .alias = "lsb", - .args = { "F:", 0, 0 }, - .usage = "[-F format]", + .args = { "F:f:", 0, 0 }, + .usage = "[-F format] [-f filter]", .flags = CMD_AFTERHOOK, .exec = cmd_list_buffers_exec @@ -49,20 +49,30 @@ cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item) struct args *args = self->args; struct paste_buffer *pb; struct format_tree *ft; - char *line; - const char *template; + const char *template, *filter; + char *line, *expanded; + int flag; if ((template = args_get(args, 'F')) == NULL) template = LIST_BUFFERS_TEMPLATE; + filter = args_get(args, 'f'); pb = NULL; while ((pb = paste_walk(pb)) != NULL) { ft = format_create(item->client, item, FORMAT_NONE, 0); format_defaults_paste_buffer(ft, pb); - line = format_expand(ft, template); - cmdq_print(item, "%s", line); - free(line); + if (filter != NULL) { + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + } else + flag = 1; + if (flag) { + line = format_expand(ft, template); + cmdq_print(item, "%s", line); + free(line); + } format_free(ft); } diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 7f6994bd..10789460 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -38,8 +38,8 @@ const struct cmd_entry cmd_list_panes_entry = { .name = "list-panes", .alias = "lsp", - .args = { "asF:t:", 0, 0 }, - .usage = "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, + .args = { "asF:f:t:", 0, 0 }, + .usage = "[-as] [-F format] [-f filter] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -91,8 +91,9 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, struct window_pane *wp; u_int n; struct format_tree *ft; - const char *template; - char *line; + const char *template, *filter; + char *line, *expanded; + int flag; template = args_get(args, 'F'); if (template == NULL) { @@ -120,6 +121,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, break; } } + filter = args_get(args, 'f'); n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { @@ -127,9 +129,17 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); - line = format_expand(ft, template); - cmdq_print(item, "%s", line); - free(line); + if (filter != NULL) { + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + } else + flag = 1; + if (flag) { + line = format_expand(ft, template); + cmdq_print(item, "%s", line); + free(line); + } format_free(ft); n++; diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 72ff47e8..36d6dd78 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -42,8 +42,8 @@ const struct cmd_entry cmd_list_sessions_entry = { .name = "list-sessions", .alias = "ls", - .args = { "F:", 0, 0 }, - .usage = "[-F format]", + .args = { "F:f:", 0, 0 }, + .usage = "[-F format] [-f filter]", .flags = CMD_AFTERHOOK, .exec = cmd_list_sessions_exec @@ -56,11 +56,13 @@ cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item) struct session *s; u_int n; struct format_tree *ft; - const char *template; - char *line; + const char *template, *filter; + char *line, *expanded; + int flag; if ((template = args_get(args, 'F')) == NULL) template = LIST_SESSIONS_TEMPLATE; + filter = args_get(args, 'f'); n = 0; RB_FOREACH(s, sessions, &sessions) { @@ -68,9 +70,17 @@ cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item) format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, NULL, NULL); - line = format_expand(ft, template); - cmdq_print(item, "%s", line); - free(line); + if (filter != NULL) { + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + } else + flag = 1; + if (flag) { + line = format_expand(ft, template); + cmdq_print(item, "%s", line); + free(line); + } format_free(ft); n++; diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 46ee6f0c..32b7b8f5 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -49,8 +49,8 @@ const struct cmd_entry cmd_list_windows_entry = { .name = "list-windows", .alias = "lsw", - .args = { "F:at:", 0, 0 }, - .usage = "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, + .args = { "F:f:at:", 0, 0 }, + .usage = "[-a] [-F format] [-f filter] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, @@ -88,8 +88,9 @@ cmd_list_windows_session(struct cmd *self, struct session *s, struct winlink *wl; u_int n; struct format_tree *ft; - const char *template; - char *line; + const char *template, *filter; + char *line, *expanded; + int flag; template = args_get(args, 'F'); if (template == NULL) { @@ -102,6 +103,7 @@ cmd_list_windows_session(struct cmd *self, struct session *s, break; } } + filter = args_get(args, 'f'); n = 0; RB_FOREACH(wl, winlinks, &s->windows) { @@ -109,9 +111,17 @@ cmd_list_windows_session(struct cmd *self, struct session *s, format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, NULL); - line = format_expand(ft, template); - cmdq_print(item, "%s", line); - free(line); + if (filter != NULL) { + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + } else + flag = 1; + if (flag) { + line = format_expand(ft, template); + cmdq_print(item, "%s", line); + free(line); + } format_free(ft); n++; diff --git a/tmux.1 b/tmux.1 index 0f3204b6..be62588f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1055,12 +1055,18 @@ List the syntax of .Ar command or - if omitted - of all commands supported by .Nm . -.It Ic list-sessions Op Fl F Ar format +.It Xo Ic list-sessions +.Op Fl F Ar format +.Op Fl f Ar filter +.Xc .D1 (alias: Ic ls ) List all sessions managed by the server. -For the meaning of the .Fl F -flag, see the +specifies the format of each line and +.Fl f +a filter. +Only sessions for which the filter is true are shown. +See the .Sx FORMATS section. .It Ic lock-client Op Fl t Ar target-client @@ -2062,6 +2068,7 @@ is given, the newly linked window is not selected. .It Xo Ic list-panes .Op Fl as .Op Fl F Ar format +.Op Fl f Ar filter .Op Fl t Ar target .Xc .D1 (alias: Ic lsp ) @@ -2078,14 +2085,18 @@ is a session (or the current session). If neither is given, .Ar target is a window (or the current window). -For the meaning of the .Fl F -flag, see the +specifies the format of each line and +.Fl f +a filter. +Only panes for which the filter is true are shown. +See the .Sx FORMATS section. .It Xo Ic list-windows .Op Fl a .Op Fl F Ar format +.Op Fl f Ar filter .Op Fl t Ar target-session .Xc .D1 (alias: Ic lsw ) @@ -2094,9 +2105,12 @@ If is given, list all windows on the server. Otherwise, list windows in the current session or in .Ar target-session . -For the meaning of the .Fl F -flag, see the +specifies the format of each line and +.Fl f +a filter. +Only windows for which the filter is true are shown. +See the .Sx FORMATS section. .It Xo Ic move-pane @@ -5261,12 +5275,16 @@ Delete the buffer named or the most recently added automatically named buffer if not specified. .It Xo Ic list-buffers .Op Fl F Ar format +.Op Fl f Ar filter .Xc .D1 (alias: Ic lsb ) List the global buffers. -For the meaning of the .Fl F -flag, see the +specifies the format of each line and +.Fl f +a filter. +Only buffers for which the filter is true are shown. +See the .Sx FORMATS section. .It Xo Ic load-buffer From da4034944dcf7b35495a4171bd9d96fb7e1310ab Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 12 Apr 2020 08:36:18 +0000 Subject: [PATCH 0145/1006] Add a -f filter argument to the list commands like choose-tree. From de6b30a51ca2acf77831de2a71c465a07e95df19 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 12 Apr 2020 20:16:36 +0000 Subject: [PATCH 0146/1006] Mention RGB, pointed out by Jody Frankowski. --- tmux.1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index be62588f..956a3911 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5470,7 +5470,7 @@ The server crashed or otherwise exited without telling the client the reason. understands some unofficial extensions to .Xr terminfo 5 : .Bl -tag -width Ds -.It Em Cs , Cr +.It Em \&Cs , Cr Set the cursor colour. The first takes a single string argument and is used to set the colour; the second takes no arguments and restores the default cursor colour. @@ -5534,6 +5534,11 @@ capabilities to the .Nm .Xr terminfo 5 entry). +.Pp +This is equivalent to the +.Em RGB +.Xr terminfo 5 +capability. .It Em \&Ms Store the current buffer in the host terminal's selection (clipboard). See the From ad38ef6ff43b5794f09911c1ae72f44bb6f0869f Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 12 Apr 2020 20:54:28 +0000 Subject: [PATCH 0147/1006] Print empty arguments properly. --- arguments.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arguments.c b/arguments.c index e573249a..5b882ea1 100644 --- a/arguments.c +++ b/arguments.c @@ -216,8 +216,10 @@ args_escape(const char *s) char *escaped, *result; int flags; - if (*s == '\0') - return (xstrdup(s)); + if (*s == '\0') { + xasprintf(&result, "''"); + return (result); + } if (s[0] != ' ' && (strchr(quoted, s[0]) != NULL || s[0] == '~') && s[1] == '\0') { From 9cbe9675ea8a8efb01dcc5f267e6d5853b2cd58f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 07:25:33 +0000 Subject: [PATCH 0148/1006] Change so that the appropriate hooks for windows and panes belong to pane/window options rather than all being session options. This is useful for example to create a pane that is automatically closed on some condition. From Anindya Mukherjee. --- cmd-set-option.c | 6 +++--- cmd-show-options.c | 6 +++--- notify.c | 8 ++++++++ options-table.c | 42 ++++++++++++++++++++++++++++++------------ tmux.1 | 29 ++++++++++------------------- 5 files changed, 54 insertions(+), 37 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 2709dcdc..d466093e 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -69,10 +69,10 @@ const struct cmd_entry cmd_set_hook_entry = { .name = "set-hook", .alias = NULL, - .args = { "agRt:u", 1, 2 }, - .usage = "[-agRu] " CMD_TARGET_SESSION_USAGE " hook [command]", + .args = { "agpRt:uw", 1, 2 }, + .usage = "[-agpRuw] " CMD_TARGET_PANE_USAGE " hook [command]", - .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, + .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_set_option_exec diff --git a/cmd-show-options.c b/cmd-show-options.c index b5c5f59d..fe3cddc5 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -65,10 +65,10 @@ const struct cmd_entry cmd_show_hooks_entry = { .name = "show-hooks", .alias = NULL, - .args = { "gt:", 0, 1 }, - .usage = "[-g] " CMD_TARGET_SESSION_USAGE, + .args = { "gpt:w", 0, 1 }, + .usage = "[-gpw] " CMD_TARGET_PANE_USAGE, - .target = { 't', CMD_FIND_SESSION, 0 }, + .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_show_options_exec diff --git a/notify.c b/notify.c index c91a4399..772b3e1f 100644 --- a/notify.c +++ b/notify.c @@ -76,6 +76,14 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) else oo = fs.s->options; o = options_get(oo, ne->name); + if (o == NULL && fs.wp != NULL) { + oo = fs.wp->options; + o = options_get(oo, ne->name); + } + if (o == NULL && fs.wl != NULL) { + oo = fs.wl->window->options; + o = options_get(oo, ne->name); + } if (o == NULL) return; diff --git a/options-table.c b/options-table.c index d8612ff2..33ee4402 100644 --- a/options-table.c +++ b/options-table.c @@ -140,7 +140,7 @@ static const char *options_table_status_format_default[] = { OPTIONS_TABLE_STATUS_FORMAT1, OPTIONS_TABLE_STATUS_FORMAT2, NULL }; -/* Helper for hook options. */ +/* Helpers for hook options. */ #define OPTIONS_TABLE_HOOK(hook_name, default_value) \ { .name = hook_name, \ .type = OPTIONS_TABLE_COMMAND, \ @@ -150,6 +150,24 @@ static const char *options_table_status_format_default[] = { .separator = "" \ } +#define OPTIONS_TABLE_PANE_HOOK(hook_name, default_value) \ + { .name = hook_name, \ + .type = OPTIONS_TABLE_COMMAND, \ + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, \ + .flags = OPTIONS_TABLE_IS_ARRAY|OPTIONS_TABLE_IS_HOOK, \ + .default_str = default_value, \ + .separator = "" \ + } + +#define OPTIONS_TABLE_WINDOW_HOOK(hook_name, default_value) \ + { .name = hook_name, \ + .type = OPTIONS_TABLE_COMMAND, \ + .scope = OPTIONS_TABLE_WINDOW, \ + .flags = OPTIONS_TABLE_IS_ARRAY|OPTIONS_TABLE_IS_HOOK, \ + .default_str = default_value, \ + .separator = "" \ + } + /* Top-level options. */ const struct options_table_entry options_table[] = { /* Server options. */ @@ -851,21 +869,21 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("client-detached", ""), OPTIONS_TABLE_HOOK("client-resized", ""), OPTIONS_TABLE_HOOK("client-session-changed", ""), - OPTIONS_TABLE_HOOK("pane-died", ""), - OPTIONS_TABLE_HOOK("pane-exited", ""), - OPTIONS_TABLE_HOOK("pane-focus-in", ""), - OPTIONS_TABLE_HOOK("pane-focus-out", ""), - OPTIONS_TABLE_HOOK("pane-mode-changed", ""), - OPTIONS_TABLE_HOOK("pane-set-clipboard", ""), + OPTIONS_TABLE_PANE_HOOK("pane-died", ""), + OPTIONS_TABLE_PANE_HOOK("pane-exited", ""), + OPTIONS_TABLE_PANE_HOOK("pane-focus-in", ""), + OPTIONS_TABLE_PANE_HOOK("pane-focus-out", ""), + OPTIONS_TABLE_PANE_HOOK("pane-mode-changed", ""), + OPTIONS_TABLE_PANE_HOOK("pane-set-clipboard", ""), OPTIONS_TABLE_HOOK("session-closed", ""), OPTIONS_TABLE_HOOK("session-created", ""), OPTIONS_TABLE_HOOK("session-renamed", ""), OPTIONS_TABLE_HOOK("session-window-changed", ""), - OPTIONS_TABLE_HOOK("window-layout-changed", ""), - OPTIONS_TABLE_HOOK("window-linked", ""), - OPTIONS_TABLE_HOOK("window-pane-changed", ""), - OPTIONS_TABLE_HOOK("window-renamed", ""), - OPTIONS_TABLE_HOOK("window-unlinked", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-layout-changed", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-linked", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-pane-changed", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-renamed", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-unlinked", ""), { .name = NULL } }; diff --git a/tmux.1 b/tmux.1 index 956a3911..81a5c613 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3897,6 +3897,7 @@ hook and there are a number of hooks not associated with commands. .Pp Hooks are stored as array options, members of the array are executed in order when the hook is triggered. +Like options different hooks may be global or belong to a session, window or pane. Hooks may be configured with the .Ic set-hook or @@ -3989,8 +3990,8 @@ Run when a window is unlinked from a session. Hooks are managed with these commands: .Bl -tag -width Ds .It Xo Ic set-hook -.Op Fl agRu -.Op Fl t Ar target-session +.Op Fl agpRuw +.Op Fl t Ar target-pane .Ar hook-name .Ar command .Xc @@ -4002,18 +4003,8 @@ unsets) hook .Ar hook-name to .Ar command . -If -.Fl g -is given, -.Em hook-name -is added to the global list of hooks, otherwise it is added to the session -hooks (for -.Ar target-session -with -.Fl t ) . -.Fl a -appends to a hook. -Like options, session hooks inherit from the global ones. +The flags are the same as for +.Ic set-option . .Pp With .Fl R , @@ -4021,12 +4012,12 @@ run .Ar hook-name immediately. .It Xo Ic show-hooks -.Op Fl g -.Op Fl t Ar target-session +.Op Fl gpw +.Op Fl t Ar target-pane .Xc -Shows the global list of hooks with -.Fl g , -otherwise the session hooks. +Shows hooks. +The flags are the same as for +.Ic show-options . .El .Sh MOUSE SUPPORT If the From c20eb0c0ae3347c768894a6355adfd7ebae6f2f3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 08:26:27 +0000 Subject: [PATCH 0149/1006] Make struct cmd local to cmd.c and move it out of tmux.h. --- client.c | 7 +-- cmd-attach-session.c | 2 +- cmd-bind-key.c | 2 +- cmd-break-pane.c | 4 +- cmd-capture-pane.c | 4 +- cmd-choose-tree.c | 6 +- cmd-command-prompt.c | 2 +- cmd-confirm-before.c | 2 +- cmd-copy-mode.c | 6 +- cmd-detach-client.c | 4 +- cmd-display-menu.c | 4 +- cmd-display-message.c | 6 +- cmd-display-panes.c | 2 +- cmd-find-window.c | 2 +- cmd-if-shell.c | 14 ++--- cmd-join-pane.c | 4 +- cmd-kill-pane.c | 3 +- cmd-kill-server.c | 2 +- cmd-kill-session.c | 2 +- cmd-kill-window.c | 6 +- cmd-list-buffers.c | 2 +- cmd-list-clients.c | 2 +- cmd-list-keys.c | 6 +- cmd-list-panes.c | 4 +- cmd-list-sessions.c | 2 +- cmd-list-windows.c | 4 +- cmd-load-buffer.c | 2 +- cmd-lock-server.c | 6 +- cmd-move-window.c | 12 ++-- cmd-new-session.c | 4 +- cmd-new-window.c | 2 +- cmd-paste-buffer.c | 2 +- cmd-pipe-pane.c | 8 +-- cmd-queue.c | 33 ++++++----- cmd-refresh-client.c | 2 +- cmd-rename-session.c | 2 +- cmd-rename-window.c | 2 +- cmd-resize-pane.c | 2 +- cmd-resize-window.c | 2 +- cmd-respawn-pane.c | 2 +- cmd-respawn-window.c | 2 +- cmd-rotate-window.c | 5 +- cmd-run-shell.c | 2 +- cmd-save-buffer.c | 6 +- cmd-select-layout.c | 6 +- cmd-select-pane.c | 33 +++++------ cmd-select-window.c | 17 +++--- cmd-send-keys.c | 4 +- cmd-set-buffer.c | 4 +- cmd-set-environment.c | 8 +-- cmd-set-option.c | 8 +-- cmd-show-environment.c | 8 +-- cmd-show-messages.c | 2 +- cmd-show-options.c | 20 ++++--- cmd-source-file.c | 2 +- cmd-split-window.c | 2 +- cmd-swap-pane.c | 2 +- cmd-swap-window.c | 3 +- cmd-switch-client.c | 4 +- cmd-unbind-key.c | 44 +------------- cmd-wait-for.c | 2 +- cmd.c | 127 ++++++++++++++++++++++++++++++++++++++--- format.c | 6 +- key-bindings.c | 10 +--- tmux.h | 49 +++++++--------- 65 files changed, 314 insertions(+), 247 deletions(-) diff --git a/client.c b/client.c index 5aae8d0d..c1d6e1b5 100644 --- a/client.c +++ b/client.c @@ -236,7 +236,6 @@ int client_main(struct event_base *base, int argc, char **argv, int flags) { struct cmd_parse_result *pr; - struct cmd *cmd; struct msg_command *data; int fd, i; const char *ttynam, *cwd; @@ -265,10 +264,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) */ pr = cmd_parse_from_arguments(argc, argv, NULL); if (pr->status == CMD_PARSE_SUCCESS) { - TAILQ_FOREACH(cmd, &pr->cmdlist->list, qentry) { - if (cmd->entry->flags & CMD_STARTSERVER) - flags |= CLIENT_STARTSERVER; - } + if (cmd_list_any_have(pr->cmdlist, CMD_STARTSERVER)) + flags |= CLIENT_STARTSERVER; cmd_list_free(pr->cmdlist); } else free(pr->error); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 477d3517..23a321bd 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -177,7 +177,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, static enum cmd_retval cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); return (cmd_attach_session(item, args_get(args, 't'), args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'), diff --git a/cmd-bind-key.c b/cmd-bind-key.c index bc6a3d40..dcb56c06 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -44,7 +44,7 @@ const struct cmd_entry cmd_bind_key_entry = { static enum cmd_retval cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); key_code key; const char *tablename, *note; struct cmd_parse_result *pr; diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 6c638103..b4997f91 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -48,7 +48,7 @@ const struct cmd_entry cmd_break_pane_entry = { static enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_find_state *current = &item->shared->current; struct client *c = cmd_find_client(item, NULL, 1); struct winlink *wl = item->source.wl; @@ -98,7 +98,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) if (idx == -1) idx = -1 - options_get_number(dst_s->options, "base-index"); wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ - if (!args_has(self->args, 'd')) { + if (!args_has(args, 'd')) { session_select(dst_s, wl->idx); cmd_find_from_session(current, dst_s, 0); } diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index ad6755ba..3510142e 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -192,14 +192,14 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, static enum cmd_retval cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c = item->client; struct window_pane *wp = item->target.wp; char *buf, *cause; const char *bufname; size_t len; - if (self->entry == &cmd_clear_history_entry) { + if (cmd_get_entry(self) == &cmd_clear_history_entry) { window_pane_reset_mode_all(wp); grid_clear_history(wp->base.grid); return (CMD_RETURN_NORMAL); diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index e4cc754e..8b667ec5 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -71,15 +71,15 @@ const struct cmd_entry cmd_choose_buffer_entry = { static enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct window_pane *wp = item->target.wp; const struct window_mode *mode; - if (self->entry == &cmd_choose_buffer_entry) { + if (cmd_get_entry(self) == &cmd_choose_buffer_entry) { if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); mode = &window_buffer_mode; - } else if (self->entry == &cmd_choose_client_entry) { + } else if (cmd_get_entry(self) == &cmd_choose_client_entry) { if (server_client_how_many() == 0) return (CMD_RETURN_NORMAL); mode = &window_client_mode; diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 9f0ea19f..31a1ae78 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -64,7 +64,7 @@ struct cmd_command_prompt_cdata { static enum cmd_retval cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); const char *inputs, *prompts; struct cmd_command_prompt_cdata *cdata; struct client *c; diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index be21a78b..419efda5 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -53,7 +53,7 @@ struct cmd_confirm_before_data { static enum cmd_retval cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_confirm_before_data *cdata; struct client *c; char *cmd, *copy, *new_prompt, *ptr; diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 5ce7a2c4..3b20689c 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -56,7 +56,7 @@ const struct cmd_entry cmd_clock_mode_entry = { static enum cmd_retval cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmdq_shared *shared = item->shared; struct client *c = item->client; struct session *s; @@ -74,7 +74,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (self->entry == &cmd_clock_mode_entry) { + if (cmd_get_entry(self) == &cmd_clock_mode_entry) { window_pane_set_mode(wp, NULL, &window_clock_mode, NULL, NULL); return (CMD_RETURN_NORMAL); } @@ -87,7 +87,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'M')) window_copy_start_drag(c, &shared->mouse); } - if (args_has(self->args, 'u')) + if (args_has(args, 'u')) window_copy_pageup(wp, 0); return (CMD_RETURN_NORMAL); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 85b9a4ed..34682459 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -57,7 +57,7 @@ const struct cmd_entry cmd_suspend_client_entry = { static enum cmd_retval cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c, *cloop; struct session *s; enum msgtype msgtype; @@ -66,7 +66,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item) if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); - if (self->entry == &cmd_suspend_client_entry) { + if (cmd_get_entry(self) == &cmd_suspend_client_entry) { server_client_suspend(c); return (CMD_RETURN_NORMAL); } diff --git a/cmd-display-menu.c b/cmd-display-menu.c index b4db7331..74637ceb 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -171,7 +171,7 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, static enum cmd_retval cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c; struct session *s = item->target.s; struct winlink *wl = item->target.wl; @@ -239,7 +239,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) static enum cmd_retval cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c; struct cmd_find_state *fs = &item->target; const char *value, *cmd = NULL, **lines = NULL; diff --git a/cmd-display-message.c b/cmd-display-message.c index 4d9bccb6..d4d4ad25 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -60,7 +60,7 @@ cmd_display_message_each(const char *key, const char *value, void *arg) static enum cmd_retval cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c, *target_c; struct session *s = item->target.s; struct winlink *wl = item->target.wl; @@ -101,7 +101,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) target_c = c; else target_c = cmd_find_best_client(s); - if (args_has(self->args, 'v')) + if (args_has(args, 'v')) flags = FORMAT_VERBOSE; else flags = 0; @@ -114,7 +114,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) } msg = format_expand_time(ft, template); - if (args_has(self->args, 'p')) + if (args_has(args, 'p')) cmdq_print(item, "%s", msg); else if (c != NULL) status_message_set(c, "%s", msg); diff --git a/cmd-display-panes.c b/cmd-display-panes.c index d8d351c2..41fe50da 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -239,7 +239,7 @@ cmd_display_panes_key(struct client *c, struct key_event *event) static enum cmd_retval cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c; struct session *s; u_int delay; diff --git a/cmd-find-window.c b/cmd-find-window.c index 774c0bb9..798754d1 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -44,7 +44,7 @@ const struct cmd_entry cmd_find_window_entry = { static enum cmd_retval cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args, *new_args; + struct args *args = cmd_get_args(self), *new_args; struct window_pane *wp = item->target.wp; const char *s = args->argv[0]; char *filter, *argv = { NULL }; diff --git a/cmd-if-shell.c b/cmd-if-shell.c index b008241d..a678cf40 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -62,10 +62,11 @@ struct cmd_if_shell_data { static enum cmd_retval cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct mouse_event *m = &item->shared->mouse; struct cmd_if_shell_data *cdata; char *shellcmd, *cmd; + const char *file; struct cmdq_item *new_item; struct cmd_find_state *fs = &item->target; struct client *c = cmd_find_client(item, NULL, 1); @@ -88,9 +89,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); memset(&pi, 0, sizeof pi); - if (self->file != NULL) - pi.file = self->file; - pi.line = self->line; + cmd_get_source(self, &pi.file, &pi.line); pi.item = item; pi.c = c; cmd_find_copy_state(&pi.fs, fs); @@ -134,10 +133,9 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->item = NULL; memset(&cdata->input, 0, sizeof cdata->input); - if (self->file != NULL) - cdata->input.file = xstrdup(self->file); - cdata->input.line = self->line; - cdata->input.item = cdata->item; + cmd_get_source(self, &file, &cdata->input.line); + if (file != NULL) + cdata->input.file = xstrdup(file); cdata->input.c = c; if (cdata->input.c != NULL) cdata->input.c->references++; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 108eab3a..2d26d82b 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -63,7 +63,7 @@ const struct cmd_entry cmd_move_pane_entry = { static enum cmd_retval cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_find_state *current = &item->shared->current; struct session *dst_s; struct winlink *src_wl, *dst_wl; @@ -75,7 +75,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) enum layout_type type; struct layout_cell *lc; - if (self->entry == &cmd_join_pane_entry) + if (cmd_get_entry(self) == &cmd_join_pane_entry) not_same_window = 1; else not_same_window = 0; diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index f0aacb2a..301b9fa6 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -44,10 +44,11 @@ const struct cmd_entry cmd_kill_pane_entry = { static enum cmd_retval cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) { + struct args *args = cmd_get_args(self); struct winlink *wl = item->target.wl; struct window_pane *loopwp, *tmpwp, *wp = item->target.wp; - if (args_has(self->args, 'a')) { + if (args_has(args, 'a')) { server_unzoom_window(wl->window); TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { if (loopwp == wp) diff --git a/cmd-kill-server.c b/cmd-kill-server.c index d7eba692..76bcf267 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -54,7 +54,7 @@ const struct cmd_entry cmd_start_server_entry = { static enum cmd_retval cmd_kill_server_exec(struct cmd *self, __unused struct cmdq_item *item) { - if (self->entry == &cmd_kill_server_entry) + if (cmd_get_entry(self) == &cmd_kill_server_entry) kill(getpid(), SIGTERM); return (CMD_RETURN_NORMAL); diff --git a/cmd-kill-session.c b/cmd-kill-session.c index dcef8097..978d1b9c 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -45,7 +45,7 @@ const struct cmd_entry cmd_kill_session_entry = { static enum cmd_retval cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct session *s, *sloop, *stmp; struct winlink *wl; diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 50df83ee..c788ff38 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -55,13 +55,13 @@ const struct cmd_entry cmd_unlink_window_entry = { static enum cmd_retval cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct winlink *wl = item->target.wl, *wl2, *wl3; struct window *w = wl->window; struct session *s = item->target.s; - if (self->entry == &cmd_unlink_window_entry) { - if (!args_has(self->args, 'k') && !session_is_linked(s, w)) { + if (cmd_get_entry(self) == &cmd_unlink_window_entry) { + if (!args_has(args, 'k') && !session_is_linked(s, w)) { cmdq_error(item, "window only linked to one session"); return (CMD_RETURN_ERROR); } diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 2da8a518..784289d8 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -46,7 +46,7 @@ const struct cmd_entry cmd_list_buffers_entry = { static enum cmd_retval cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct paste_buffer *pb; struct format_tree *ft; const char *template, *filter; diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 9fab8f84..9cec5199 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -51,7 +51,7 @@ const struct cmd_entry cmd_list_clients_entry = { static enum cmd_retval cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c; struct session *s; struct format_tree *ft; diff --git a/cmd-list-keys.c b/cmd-list-keys.c index e1ec4d3c..d6c261d8 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -144,7 +144,7 @@ cmd_list_keys_get_prefix(struct args *args, key_code *prefix) static enum cmd_retval cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct key_table *table; struct key_binding *bd; const char *tablename, *r; @@ -153,7 +153,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) int repeat, width, tablewidth, keywidth, found = 0; size_t tmpsize, tmpused, cplen; - if (self->entry == &cmd_list_commands_entry) + if (cmd_get_entry(self) == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, item)); if (args->argc != 0) { @@ -313,7 +313,7 @@ out: static enum cmd_retval cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); const struct cmd_entry **entryp; const struct cmd_entry *entry; struct format_tree *ft; diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 10789460..ac97558e 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -50,7 +50,7 @@ const struct cmd_entry cmd_list_panes_entry = { static enum cmd_retval cmd_list_panes_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct session *s = item->target.s; struct winlink *wl = item->target.wl; @@ -87,7 +87,7 @@ static void cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, struct cmdq_item *item, int type) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct window_pane *wp; u_int n; struct format_tree *ft; diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 36d6dd78..1c6e1e9c 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -52,7 +52,7 @@ const struct cmd_entry cmd_list_sessions_entry = { static enum cmd_retval cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct session *s; u_int n; struct format_tree *ft; diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 32b7b8f5..905e9eaf 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -61,7 +61,7 @@ const struct cmd_entry cmd_list_windows_entry = { static enum cmd_retval cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); if (args_has(args, 'a')) cmd_list_windows_server(self, item); @@ -84,7 +84,7 @@ static void cmd_list_windows_session(struct cmd *self, struct session *s, struct cmdq_item *item, int type) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct winlink *wl; u_int n; struct format_tree *ft; diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 5e930126..588623f7 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -83,7 +83,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error, static enum cmd_retval cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_load_buffer_data *cdata; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 524fa451..937fb126 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -64,12 +64,12 @@ const struct cmd_entry cmd_lock_client_entry = { static enum cmd_retval cmd_lock_server_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c; - if (self->entry == &cmd_lock_server_entry) + if (cmd_get_entry(self) == &cmd_lock_server_entry) server_lock(); - else if (self->entry == &cmd_lock_session_entry) + else if (cmd_get_entry(self) == &cmd_lock_session_entry) server_lock_session(item->target.s); else { if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) diff --git a/cmd-move-window.c b/cmd-move-window.c index cb64d1e0..aaeb12b0 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -59,7 +59,7 @@ const struct cmd_entry cmd_link_window_entry = { static enum cmd_retval cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); const char *tflag = args_get(args, 't'); struct session *src; struct session *dst; @@ -86,11 +86,11 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) wl = item->source.wl; idx = item->target.idx; - kflag = args_has(self->args, 'k'); - dflag = args_has(self->args, 'd'); - sflag = args_has(self->args, 's'); + kflag = args_has(args, 'k'); + dflag = args_has(args, 'd'); + sflag = args_has(args, 's'); - if (args_has(self->args, 'a')) { + if (args_has(args, 'a')) { if ((idx = winlink_shuffle_up(dst, dst->curw)) == -1) return (CMD_RETURN_ERROR); } @@ -101,7 +101,7 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } - if (self->entry == &cmd_move_window_entry) + if (cmd_get_entry(self) == &cmd_move_window_entry) server_unlink_window(src, wl); /* diff --git a/cmd-new-session.c b/cmd-new-session.c index 725448fd..73193e96 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -66,7 +66,7 @@ const struct cmd_entry cmd_has_session_entry = { static enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c = item->client; struct session *s, *as, *groupwith; struct environ *env; @@ -81,7 +81,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) enum cmd_retval retval; struct cmd_find_state fs; - if (self->entry == &cmd_has_session_entry) { + if (cmd_get_entry(self) == &cmd_has_session_entry) { /* * cmd_find_target() will fail if the session cannot be found, * so always return success here. diff --git a/cmd-new-window.c b/cmd-new-window.c index 033d208a..5f1c11cb 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -51,7 +51,7 @@ const struct cmd_entry cmd_new_window_entry = { static enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_find_state *current = &item->shared->current; struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index baad707a..c79da012 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -47,7 +47,7 @@ const struct cmd_entry cmd_paste_buffer_entry = { static enum cmd_retval cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct window_pane *wp = item->target.wp; struct paste_buffer *pb; const char *sepstr, *bufname, *bufdata, *bufend, *line; diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 084eafe5..2e898dbc 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -56,7 +56,7 @@ const struct cmd_entry cmd_pipe_pane_entry = { static enum cmd_retval cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c = cmd_find_client(item, NULL, 1); struct window_pane *wp = item->target.wp; struct session *s = item->target.s; @@ -89,13 +89,13 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) * * bind ^p pipep -o 'cat >>~/output' */ - if (args_has(self->args, 'o') && old_fd != -1) + if (args_has(args, 'o') && old_fd != -1) return (CMD_RETURN_NORMAL); /* What do we want to do? Neither -I or -O is -O. */ - if (args_has(self->args, 'I')) { + if (args_has(args, 'I')) { in = 1; - out = args_has(self->args, 'O'); + out = args_has(args, 'O'); } else { in = 0; out = 1; diff --git a/cmd-queue.c b/cmd-queue.c index 42e7d31b..892bd03b 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -209,11 +209,13 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, { struct cmdq_item *item, *first = NULL, *last = NULL; struct cmd *cmd; + const struct cmd_entry *entry; struct cmdq_shared *shared = NULL; - u_int group = 0; + u_int group, last_group = 0; - TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { - if (cmd->group != group) { + cmd = cmd_list_first(cmdlist, &group); + while (cmd != NULL) { + if (group != last_group) { shared = xcalloc(1, sizeof *shared); if (current != NULL) cmd_find_copy_state(&shared->current, current); @@ -221,14 +223,15 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, cmd_find_clear_state(&shared->current, 0); if (m != NULL) memcpy(&shared->mouse, m, sizeof shared->mouse); - group = cmd->group; + last_group = group; } + entry = cmd_get_entry(cmd); item = xcalloc(1, sizeof *item); - xasprintf(&item->name, "[%s/%p]", cmd->entry->name, item); + xasprintf(&item->name, "[%s/%p]", entry->name, item); item->type = CMDQ_COMMAND; - item->group = cmd->group; + item->group = group; item->flags = flags; item->shared = shared; @@ -245,6 +248,8 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, if (last != NULL) last->next = item; last = item; + + cmd = cmd_list_next(cmd, &group); } return (first); } @@ -261,7 +266,7 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs, return (CMD_RETURN_NORMAL); } - value = args_get(item->cmd->args, flag->flag); + value = args_get(cmd_get_args(item->cmd), flag->flag); if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) { cmd_find_clear_state(fs, 0); return (CMD_RETURN_ERROR); @@ -277,7 +282,7 @@ cmdq_fire_command(struct cmdq_item *item) const char *name = cmdq_name(c); struct cmdq_shared *shared = item->shared; struct cmd *cmd = item->cmd; - const struct cmd_entry *entry = cmd->entry; + const struct cmd_entry *entry = cmd_get_entry(cmd); enum cmd_retval retval; struct cmd_find_state *fsp, fs; int flags; @@ -528,8 +533,9 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) struct client *c = item->client; struct cmd *cmd = item->cmd; va_list ap; - char *msg; - char *tmp; + char *msg, *tmp; + const char *file; + u_int line; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -537,9 +543,10 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) log_debug("%s: %s", __func__, msg); - if (c == NULL) - cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); - else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + if (c == NULL) { + cmd_get_source(cmd, &file, &line); + cfg_add_cause("%s:%u: %s", file, line, msg); + } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { if (~c->flags & CLIENT_UTF8) { tmp = msg; msg = utf8_sanitize(tmp); diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index b4c5e844..1becaaae 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -45,7 +45,7 @@ const struct cmd_entry cmd_refresh_client_entry = { static enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c; struct tty *tty; struct window *w; diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 8385434a..67acb016 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -46,7 +46,7 @@ const struct cmd_entry cmd_rename_session_entry = { static enum cmd_retval cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; char *newname; diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 4d2ebb75..0f28c665 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -45,7 +45,7 @@ const struct cmd_entry cmd_rename_window_entry = { static enum cmd_retval cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index fd303cc1..7b9eaf88 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -49,7 +49,7 @@ const struct cmd_entry cmd_resize_pane_entry = { static enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmdq_shared *shared = item->shared; struct window_pane *wp = item->target.wp; struct winlink *wl = item->target.wl; diff --git a/cmd-resize-window.c b/cmd-resize-window.c index 9cc74e82..6ac2d235 100644 --- a/cmd-resize-window.c +++ b/cmd-resize-window.c @@ -46,7 +46,7 @@ const struct cmd_entry cmd_resize_window_entry = { static enum cmd_retval cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct winlink *wl = item->target.wl; struct window *w = wl->window; struct session *s = item->target.s; diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 55544f93..3d4686f2 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -47,7 +47,7 @@ const struct cmd_entry cmd_respawn_pane_entry = { static enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct spawn_context sc; struct session *s = item->target.s; struct winlink *wl = item->target.wl; diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 5f44db12..91269ff5 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -47,7 +47,7 @@ const struct cmd_entry cmd_respawn_window_entry = { static enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct spawn_context sc; struct session *s = item->target.s; struct winlink *wl = item->target.wl; diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index cc661163..eaba2faf 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -43,6 +43,7 @@ const struct cmd_entry cmd_rotate_window_entry = { static enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) { + struct args *args = cmd_get_args(self); struct cmd_find_state *current = &item->shared->current; struct winlink *wl = item->target.wl; struct window *w = wl->window; @@ -50,9 +51,9 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) struct layout_cell *lc; u_int sx, sy, xoff, yoff; - window_push_zoom(w, args_has(self->args, 'Z')); + window_push_zoom(w, args_has(args, 'Z')); - if (args_has(self->args, 'D')) { + if (args_has(args, 'D')) { wp = TAILQ_LAST(&w->panes, window_panes); TAILQ_REMOVE(&w->panes, wp, entry); TAILQ_INSERT_HEAD(&w->panes, wp, entry); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 7ce6d55a..b828576a 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -89,7 +89,7 @@ cmd_run_shell_print(struct job *job, const char *msg) static enum cmd_retval cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_run_shell_data *cdata; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 6830e5fc..f96e9849 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -73,7 +73,7 @@ cmd_save_buffer_done(__unused struct client *c, const char *path, int error, static enum cmd_retval cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; @@ -98,11 +98,11 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) } bufdata = paste_buffer_data(pb, &bufsize); - if (self->entry == &cmd_show_buffer_entry) + if (cmd_get_entry(self) == &cmd_show_buffer_entry) path = xstrdup("-"); else path = format_single(item, args->argv[0], c, s, wl, wp); - if (args_has(self->args, 'a')) + if (args_has(args, 'a')) flags = O_APPEND; else flags = 0; diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 775d32c5..b51ab4c1 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -71,7 +71,7 @@ const struct cmd_entry cmd_previous_layout_entry = { static enum cmd_retval cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct winlink *wl = item->target.wl; struct window *w = wl->window; struct window_pane *wp = item->target.wp; @@ -81,10 +81,10 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) server_unzoom_window(w); - next = self->entry == &cmd_next_layout_entry; + next = (cmd_get_entry(self) == &cmd_next_layout_entry); if (args_has(args, 'n')) next = 1; - previous = self->entry == &cmd_previous_layout_entry; + previous = (cmd_get_entry(self) == &cmd_previous_layout_entry); if (args_has(args, 'p')) previous = 1; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index c63c7e61..376bf62e 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -83,7 +83,8 @@ cmd_select_pane_redraw(struct window *w) static enum cmd_retval cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); + const struct cmd_entry *entry = cmd_get_entry(self); struct cmd_find_state *current = &item->shared->current; struct client *c = cmd_find_client(item, NULL, 1); struct winlink *wl = item->target.wl; @@ -95,7 +96,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) struct style *sy; struct options_entry *o; - if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { + if (entry == &cmd_last_pane_entry || args_has(args, 'l')) { lastwp = w->last; if (lastwp == NULL && window_count_panes(w) == 2) { lastwp = TAILQ_PREV(w->active, window_panes, entry); @@ -106,12 +107,12 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "no last pane"); return (CMD_RETURN_ERROR); } - if (args_has(self->args, 'e')) + if (args_has(args, 'e')) lastwp->flags &= ~PANE_INPUTOFF; - else if (args_has(self->args, 'd')) + else if (args_has(args, 'd')) lastwp->flags |= PANE_INPUTOFF; else { - if (window_push_zoom(w, args_has(self->args, 'Z'))) + if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, lastwp); if (window_set_active_pane(w, lastwp, 1)) { @@ -146,7 +147,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'P') || args_has(self->args, 'g')) { + if (args_has(args, 'P') || args_has(args, 'g')) { if ((style = args_get(args, 'P')) != NULL) { o = options_set_style(wp->options, "window-style", 0, style); @@ -158,26 +159,26 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) style); wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); } - if (args_has(self->args, 'g')) { + if (args_has(args, 'g')) { sy = options_get_style(wp->options, "window-style"); cmdq_print(item, "%s", style_tostring(sy)); } return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'L')) { + if (args_has(args, 'L')) { window_push_zoom(w, 1); wp = window_pane_find_left(wp); window_pop_zoom(w); - } else if (args_has(self->args, 'R')) { + } else if (args_has(args, 'R')) { window_push_zoom(w, 1); wp = window_pane_find_right(wp); window_pop_zoom(w); - } else if (args_has(self->args, 'U')) { + } else if (args_has(args, 'U')) { window_push_zoom(w, 1); wp = window_pane_find_up(wp); window_pop_zoom(w); - } else if (args_has(self->args, 'D')) { + } else if (args_has(args, 'D')) { window_push_zoom(w, 1); wp = window_pane_find_down(wp); window_pop_zoom(w); @@ -185,17 +186,17 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (wp == NULL) return (CMD_RETURN_NORMAL); - if (args_has(self->args, 'e')) { + if (args_has(args, 'e')) { wp->flags &= ~PANE_INPUTOFF; return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'd')) { + if (args_has(args, 'd')) { wp->flags |= PANE_INPUTOFF; return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'T')) { - pane_title = format_single(item, args_get(self->args, 'T'), + if (args_has(args, 'T')) { + pane_title = format_single(item, args_get(args, 'T'), c, s, wl, wp); if (screen_set_title(&wp->base, pane_title)) server_status_window(wp->window); @@ -205,7 +206,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (wp == w->active) return (CMD_RETURN_NORMAL); - if (window_push_zoom(w, args_has(self->args, 'Z'))) + if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); if (window_set_active_pane(w, wp, 1)) { diff --git a/cmd-select-window.c b/cmd-select-window.c index 54965e89..3d8d02de 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -84,23 +84,24 @@ const struct cmd_entry cmd_last_window_entry = { static enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) { + struct args *args = cmd_get_args(self); struct cmd_find_state *current = &item->shared->current; struct winlink *wl = item->target.wl; struct session *s = item->target.s; int next, previous, last, activity; - next = self->entry == &cmd_next_window_entry; - if (args_has(self->args, 'n')) + next = (cmd_get_entry(self) == &cmd_next_window_entry); + if (args_has(args, 'n')) next = 1; - previous = self->entry == &cmd_previous_window_entry; - if (args_has(self->args, 'p')) + previous = (cmd_get_entry(self) == &cmd_previous_window_entry); + if (args_has(args, 'p')) previous = 1; - last = self->entry == &cmd_last_window_entry; - if (args_has(self->args, 'l')) + last = (cmd_get_entry(self) == &cmd_last_window_entry); + if (args_has(args, 'l')) last = 1; if (next || previous || last) { - activity = args_has(self->args, 'a'); + activity = args_has(args, 'a'); if (next) { if (session_next(s, activity) != 0) { cmdq_error(item, "no next window"); @@ -125,7 +126,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) * If -T and select-window is invoked on same window as * current, switch to previous window. */ - if (args_has(self->args, 'T') && wl == s->curw) { + if (args_has(args, 'T') && wl == s->curw) { if (session_last(s) != 0) { cmdq_error(item, "no last window"); return (-1); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 15967b0c..84996723 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -131,7 +131,7 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, static enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c = cmd_find_client(item, NULL, 1); struct cmd_find_state *fs = &item->target; struct window_pane *wp = item->target.wp; @@ -181,7 +181,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (self->entry == &cmd_send_prefix_entry) { + if (cmd_get_entry(self) == &cmd_send_prefix_entry) { if (args_has(args, '2')) key = options_get_number(s->options, "prefix2"); else diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 96fdf450..0f3fffce 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -54,7 +54,7 @@ const struct cmd_entry cmd_delete_buffer_entry = { static enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct paste_buffer *pb; char *bufdata, *cause; const char *bufname, *olddata; @@ -66,7 +66,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) else pb = paste_get_name(bufname); - if (self->entry == &cmd_delete_buffer_entry) { + if (cmd_get_entry(self) == &cmd_delete_buffer_entry) { if (pb == NULL) pb = paste_get_top(&bufname); if (pb == NULL) { diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 248f734a..72e40ded 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -46,7 +46,7 @@ const struct cmd_entry cmd_set_environment_entry = { static enum cmd_retval cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct environ *env; const char *name, *value, *target; @@ -65,7 +65,7 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) else value = args->argv[1]; - if (args_has(self->args, 'g')) + if (args_has(args, 'g')) env = global_environ; else { if (item->target.s == NULL) { @@ -79,13 +79,13 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) env = item->target.s->environ; } - if (args_has(self->args, 'u')) { + if (args_has(args, 'u')) { if (value != NULL) { cmdq_error(item, "can't specify a value with -u"); return (CMD_RETURN_ERROR); } environ_unset(env, name); - } else if (args_has(self->args, 'r')) { + } else if (args_has(args, 'r')) { if (value != NULL) { cmdq_error(item, "can't specify a value with -r"); return (CMD_RETURN_ERROR); diff --git a/cmd-set-option.c b/cmd-set-option.c index d466093e..041f109b 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -81,7 +81,7 @@ const struct cmd_entry cmd_set_hook_entry = { static enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); struct cmd_find_state *fs = &item->target; struct client *c, *loop; @@ -96,14 +96,14 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) int scope; struct style *sy; - window = (self->entry == &cmd_set_window_option_entry); + window = (cmd_get_entry(self) == &cmd_set_window_option_entry); /* Expand argument. */ c = cmd_find_client(item, NULL, 1); argument = format_single(item, args->argv[0], c, s, wl, NULL); /* If set-hook -R, fire the hook straight away. */ - if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) { + if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) { notify_hook(item, argument); free(argument); return (CMD_RETURN_NORMAL); @@ -288,7 +288,7 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, struct options_entry *parent, const char *value) { const struct options_table_entry *oe; - struct args *args = self->args; + struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); struct options_entry *o; long long number; diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 0d2f7dd9..be6209dd 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -69,7 +69,7 @@ static void cmd_show_environment_print(struct cmd *self, struct cmdq_item *item, struct environ_entry *envent) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); char *escaped; if (!args_has(args, 'h') && (envent->flags & ENVIRON_HIDDEN)) @@ -77,7 +77,7 @@ cmd_show_environment_print(struct cmd *self, struct cmdq_item *item, if (args_has(args, 'h') && (~envent->flags & ENVIRON_HIDDEN)) return; - if (!args_has(self->args, 's')) { + if (!args_has(args, 's')) { if (envent->value != NULL) cmdq_print(item, "%s=%s", envent->name, envent->value); else @@ -97,7 +97,7 @@ cmd_show_environment_print(struct cmd *self, struct cmdq_item *item, static enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct environ *env; struct environ_entry *envent; const char *target; @@ -109,7 +109,7 @@ cmd_show_environment_exec(struct cmd *self, struct cmdq_item *item) } } - if (args_has(self->args, 'g')) + if (args_has(args, 'g')) env = global_environ; else { if (item->target.s == NULL) { diff --git a/cmd-show-messages.c b/cmd-show-messages.c index e4b51fa8..cedf1093 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -69,7 +69,7 @@ cmd_show_messages_terminals(struct cmdq_item *item, int blank) static enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct client *c; struct message_entry *msg; char *tim; diff --git a/cmd-show-options.c b/cmd-show-options.c index fe3cddc5..c1f9fd56 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -77,7 +77,7 @@ const struct cmd_entry cmd_show_hooks_entry = { static enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_find_state *fs = &item->target; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; @@ -87,7 +87,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) int window, idx, ambiguous, parent, scope; struct options_entry *o; - window = (self->entry == &cmd_show_window_options_entry); + window = (cmd_get_entry(self) == &cmd_show_window_options_entry); if (args->argc == 0) { scope = options_scope_from_flags(args, window, fs, &oo, &cause); @@ -143,6 +143,7 @@ static void cmd_show_options_print(struct cmd *self, struct cmdq_item *item, struct options_entry *o, int idx, int parent) { + struct args *args = cmd_get_args(self); struct options_array_item *a; const char *name = options_name(o); char *value, *tmp = NULL, *escaped; @@ -154,7 +155,7 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, if (options_isarray(o)) { a = options_array_first(o); if (a == NULL) { - if (!args_has(self->args, 'v')) + if (!args_has(args, 'v')) cmdq_print(item, "%s", name); return; } @@ -169,7 +170,7 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, } value = options_tostring(o, idx, 0); - if (args_has(self->args, 'v')) + if (args_has(args, 'v')) cmdq_print(item, "%s", value); else if (options_isstring(o)) { escaped = args_escape(value); @@ -193,6 +194,7 @@ static enum cmd_retval cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, struct options *oo) { + struct args *args = cmd_get_args(self); const struct options_table_entry *oe; struct options_entry *o; struct options_array_item *a; @@ -210,16 +212,16 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, if (~oe->scope & scope) continue; - if ((self->entry != &cmd_show_hooks_entry && - !args_has(self->args, 'H') && + if ((cmd_get_entry(self) != &cmd_show_hooks_entry && + !args_has(args, 'H') && (oe->flags & OPTIONS_TABLE_IS_HOOK)) || - (self->entry == &cmd_show_hooks_entry && + (cmd_get_entry(self) == &cmd_show_hooks_entry && (~oe->flags & OPTIONS_TABLE_IS_HOOK))) continue; o = options_get_only(oo, oe->name); if (o == NULL) { - if (!args_has(self->args, 'A')) + if (!args_has(args, 'A')) continue; o = options_get(oo, oe->name); if (o == NULL) @@ -231,7 +233,7 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, if (!options_isarray(o)) cmd_show_options_print(self, item, o, -1, parent); else if ((a = options_array_first(o)) == NULL) { - if (!args_has(self->args, 'v')) { + if (!args_has(args, 'v')) { name = options_name(o); if (parent) cmdq_print(item, "%s*", name); diff --git a/cmd-source-file.c b/cmd-source-file.c index 46dc6d94..50571874 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -123,7 +123,7 @@ cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path) static enum cmd_retval cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_source_file_data *cdata; struct client *c = item->client; enum cmd_retval retval = CMD_RETURN_NORMAL; diff --git a/cmd-split-window.c b/cmd-split-window.c index c1c8be25..6a9210a7 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -53,7 +53,7 @@ const struct cmd_entry cmd_split_window_entry = { static enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_find_state *current = &item->shared->current; struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 3e0e6e60..60be63dc 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -45,7 +45,7 @@ const struct cmd_entry cmd_swap_pane_entry = { static enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 0c15479d..9ed1e8f1 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -45,6 +45,7 @@ const struct cmd_entry cmd_swap_window_entry = { static enum cmd_retval cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item) { + struct args *args = cmd_get_args(self); struct session *src, *dst; struct session_group *sg_src, *sg_dst; struct winlink *wl_src, *wl_dst; @@ -77,7 +78,7 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item) wl_src->window = w_dst; TAILQ_INSERT_TAIL(&w_dst->winlinks, wl_src, wentry); - if (args_has(self->args, 'd')) { + if (args_has(args, 'd')) { session_select(dst, wl_dst->idx); if (src != dst) session_select(src, wl_src->idx); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index cf84c319..def61a73 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -47,7 +47,7 @@ const struct cmd_entry cmd_switch_client_entry = { static enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); const char *tflag = args_get(args, 't'); enum cmd_find_type type; int flags; @@ -115,7 +115,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); if (wl != NULL && wp != NULL) { w = wl->window; - if (window_push_zoom(w, args_has(self->args, 'Z'))) + if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); window_set_active_pane(w, wp, 1); diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 69141346..4b9f39a6 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -28,15 +28,12 @@ static enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmdq_item *); -static enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, - struct cmdq_item *, key_code); - const struct cmd_entry cmd_unbind_key_entry = { .name = "unbind-key", .alias = "unbind", - .args = { "ant:T:", 0, 1 }, - .usage = "[-an] [-t mode-table] [-T key-table] key", + .args = { "anT:", 0, 1 }, + .usage = "[-an] [-T key-table] key", .flags = CMD_AFTERHOOK, .exec = cmd_unbind_key_exec @@ -45,7 +42,7 @@ const struct cmd_entry cmd_unbind_key_entry = { static enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); key_code key; const char *tablename; @@ -67,9 +64,6 @@ cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item) key = KEYC_UNKNOWN; } - if (args_has(args, 't')) - return (cmd_unbind_key_mode_table(self, item, key)); - if (key == KEYC_UNKNOWN) { tablename = args_get(args, 'T'); if (tablename == NULL) { @@ -98,35 +92,3 @@ cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item) key_bindings_remove(tablename, key); return (CMD_RETURN_NORMAL); } - -static enum cmd_retval -cmd_unbind_key_mode_table(struct cmd *self, struct cmdq_item *item, - key_code key) -{ - struct args *args = self->args; - const char *tablename; - const struct mode_key_table *mtab; - struct mode_key_binding *mbind, mtmp; - - tablename = args_get(args, 't'); - if ((mtab = mode_key_findtable(tablename)) == NULL) { - cmdq_error(item, "unknown key table: %s", tablename); - return (CMD_RETURN_ERROR); - } - - if (key == KEYC_UNKNOWN) { - while (!RB_EMPTY(mtab->tree)) { - mbind = RB_ROOT(mtab->tree); - RB_REMOVE(mode_key_tree, mtab->tree, mbind); - free(mbind); - } - return (CMD_RETURN_NORMAL); - } - - mtmp.key = key; - if ((mbind = RB_FIND(mode_key_tree, mtab->tree, &mtmp)) != NULL) { - RB_REMOVE(mode_key_tree, mtab->tree, mbind); - free(mbind); - } - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 4f438a7f..a5803ca5 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -120,7 +120,7 @@ cmd_wait_for_remove(struct wait_channel *wc) static enum cmd_retval cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); const char *name = args->argv[0]; struct wait_channel *wc, wc0; diff --git a/cmd.c b/cmd.c index 52cf5af1..d0a8c267 100644 --- a/cmd.c +++ b/cmd.c @@ -207,8 +207,27 @@ const struct cmd_entry *cmd_table[] = { NULL }; +/* Instance of a command. */ +struct cmd { + const struct cmd_entry *entry; + struct args *args; + u_int group; + + char *file; + u_int line; + + char *alias; + int argc; + char **argv; + + TAILQ_ENTRY(cmd) qentry; +}; +TAILQ_HEAD(cmds, cmd); + +/* Next group number for new command list. */ static u_int cmd_list_next_group = 1; +/* Log an argument vector. */ void printflike(3, 4) cmd_log_argv(int argc, char **argv, const char *fmt, ...) { @@ -225,6 +244,7 @@ cmd_log_argv(int argc, char **argv, const char *fmt, ...) free(prefix); } +/* Prepend to an argument vector. */ void cmd_prepend_argv(int *argc, char ***argv, char *arg) { @@ -241,6 +261,7 @@ cmd_prepend_argv(int *argc, char ***argv, char *arg) (*argc)++; } +/* Append to an argument vector. */ void cmd_append_argv(int *argc, char ***argv, char *arg) { @@ -248,6 +269,7 @@ cmd_append_argv(int *argc, char ***argv, char *arg) (*argv)[(*argc)++] = xstrdup(arg); } +/* Pack an argument vector up into a buffer. */ int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { @@ -270,6 +292,7 @@ cmd_pack_argv(int argc, char **argv, char *buf, size_t len) return (0); } +/* Unpack an argument vector from a packed buffer. */ int cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) { @@ -298,6 +321,7 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) return (0); } +/* Copy an argument vector, ensuring it is terminated by NULL. */ char ** cmd_copy_argv(int argc, char **argv) { @@ -314,6 +338,7 @@ cmd_copy_argv(int argc, char **argv) return (new_argv); } +/* Free an argument vector. */ void cmd_free_argv(int argc, char **argv) { @@ -326,6 +351,7 @@ cmd_free_argv(int argc, char **argv) free(argv); } +/* Convert argument vector to a string. */ char * cmd_stringify_argv(int argc, char **argv) { @@ -352,6 +378,31 @@ cmd_stringify_argv(int argc, char **argv) return (buf); } +/* Get entry for command. */ +const struct cmd_entry * +cmd_get_entry(struct cmd *cmd) +{ + return (cmd->entry); +} + +/* Get arguments for command. */ +struct args * +cmd_get_args(struct cmd *cmd) +{ + return (cmd->args); +} + +/* Get file and line for command. */ +void +cmd_get_source(struct cmd *cmd, const char **file, u_int *line) +{ + if (file != NULL) + *file = cmd->file; + if (line != NULL) + *line = cmd->line; +} + +/* Look for an alias for a command. */ char * cmd_get_alias(const char *name) { @@ -382,6 +433,7 @@ cmd_get_alias(const char *name) return (NULL); } +/* Look up a command entry by name. */ static const struct cmd_entry * cmd_find(const char *name, char **cause) { @@ -431,6 +483,7 @@ ambiguous: return (NULL); } +/* Parse a single command from an argument vector. */ struct cmd * cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) { @@ -479,6 +532,7 @@ usage: return (NULL); } +/* Free a command. */ void cmd_free(struct cmd *cmd) { @@ -491,6 +545,7 @@ cmd_free(struct cmd *cmd) free(cmd); } +/* Get a command as a string. */ char * cmd_print(struct cmd *cmd) { @@ -506,6 +561,7 @@ cmd_print(struct cmd *cmd) return (out); } +/* Create a new command list. */ struct cmd_list * cmd_list_new(void) { @@ -514,29 +570,33 @@ cmd_list_new(void) cmdlist = xcalloc(1, sizeof *cmdlist); cmdlist->references = 1; cmdlist->group = cmd_list_next_group++; - TAILQ_INIT(&cmdlist->list); + cmdlist->list = xcalloc(1, sizeof *cmdlist->list); + TAILQ_INIT(cmdlist->list); return (cmdlist); } +/* Append a command to a command list. */ void cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) { cmd->group = cmdlist->group; - TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); + TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); } +/* Move all commands from one command list to another */ void cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) { struct cmd *cmd, *cmd1; - TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) { - TAILQ_REMOVE(&from->list, cmd, qentry); - TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); + TAILQ_FOREACH_SAFE(cmd, from->list, qentry, cmd1) { + TAILQ_REMOVE(from->list, cmd, qentry); + TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); } cmdlist->group = cmd_list_next_group++; } +/* Free a command list. */ void cmd_list_free(struct cmd_list *cmdlist) { @@ -545,14 +605,15 @@ cmd_list_free(struct cmd_list *cmdlist) if (--cmdlist->references != 0) return; - TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) { - TAILQ_REMOVE(&cmdlist->list, cmd, qentry); + TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) { + TAILQ_REMOVE(cmdlist->list, cmd, qentry); cmd_free(cmd); } - + free(cmdlist->list); free(cmdlist); } +/* Get a command list as a string. */ char * cmd_list_print(struct cmd_list *cmdlist, int escaped) { @@ -563,7 +624,7 @@ cmd_list_print(struct cmd_list *cmdlist, int escaped) len = 1; buf = xcalloc(1, len); - TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { + TAILQ_FOREACH(cmd, cmdlist->list, qentry) { this = cmd_print(cmd); len += strlen(this) + 4; @@ -583,6 +644,54 @@ cmd_list_print(struct cmd_list *cmdlist, int escaped) return (buf); } +/* Get first command in list. */ +struct cmd * +cmd_list_first(struct cmd_list *cmdlist, u_int *group) +{ + struct cmd *cmd; + + cmd = TAILQ_FIRST(cmdlist->list); + if (cmd != NULL && group != NULL) + *group = cmd->group; + return (cmd); +} + +/* Get next command in list. */ +struct cmd * +cmd_list_next(struct cmd *cmd, u_int *group) +{ + cmd = TAILQ_NEXT(cmd, qentry); + if (cmd != NULL && group != NULL) + *group = cmd->group; + return (cmd); +} + +/* Do all of the commands in this command list have this flag? */ +int +cmd_list_all_have(struct cmd_list *cmdlist, int flag) +{ + struct cmd *cmd; + + TAILQ_FOREACH(cmd, cmdlist->list, qentry) { + if (~cmd->entry->flags & flag) + return (0); + } + return (1); +} + +/* Do any of the commands in this command list have this flag? */ +int +cmd_list_any_have(struct cmd_list *cmdlist, int flag) +{ + struct cmd *cmd; + + TAILQ_FOREACH(cmd, cmdlist->list, qentry) { + if (cmd->entry->flags & flag) + return (1); + } + return (0); +} + /* Adjust current mouse position for a pane. */ int cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, diff --git a/format.c b/format.c index efbf5c62..2d8522c9 100644 --- a/format.c +++ b/format.c @@ -1128,8 +1128,10 @@ format_create_add_item(struct format_tree *ft, struct cmdq_item *item) struct window_pane *wp; u_int x, y; - if (item->cmd != NULL) - format_add(ft, "command", "%s", item->cmd->entry->name); + if (item->cmd != NULL) { + format_add(ft, "command", "%s", + cmd_get_entry (item->cmd)->name); + } if (item->shared == NULL) return; diff --git a/key-bindings.c b/key-bindings.c index 3c6f8ff6..b76589ce 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -537,19 +537,13 @@ struct cmdq_item * key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, struct client *c, struct mouse_event *m, struct cmd_find_state *fs) { - struct cmd *cmd; struct cmdq_item *new_item; int readonly; if (c == NULL || (~c->flags & CLIENT_READONLY)) readonly = 1; - else { - readonly = 1; - TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { - if (~cmd->entry->flags & CMD_READONLY) - readonly = 0; - } - } + else + readonly = cmd_list_all_have(bd->cmdlist, CMD_READONLY); if (!readonly) new_item = cmdq_get_callback(key_bindings_read_only, NULL); else { diff --git a/tmux.h b/tmux.h index 8f8640bf..539309da 100644 --- a/tmux.h +++ b/tmux.h @@ -39,9 +39,11 @@ extern char **environ; struct args; struct args_value; struct client; +struct cmd; struct cmd_find_state; struct cmdq_item; struct cmdq_list; +struct cmds; struct environ; struct format_job_tree; struct format_tree; @@ -50,8 +52,8 @@ struct job; struct mode_tree_data; struct mouse_event; struct options; -struct options_entry; struct options_array_item; +struct options_entry; struct session; struct tmuxpeer; struct tmuxproc; @@ -1335,27 +1337,11 @@ struct cmd_find_state { #define CMD_FIND_EXACT_WINDOW 0x20 #define CMD_FIND_CANFAIL 0x40 -/* Command and list of commands. */ -struct cmd { - const struct cmd_entry *entry; - struct args *args; - u_int group; - - char *file; - u_int line; - - char *alias; - int argc; - char **argv; - - TAILQ_ENTRY(cmd) qentry; -}; -TAILQ_HEAD(cmds, cmd); - +/* List of commands. */ struct cmd_list { - int references; - u_int group; - struct cmds list; + int references; + u_int group; + struct cmds *list; }; /* Command return values. */ @@ -2102,6 +2088,7 @@ int cmd_find_from_mouse(struct cmd_find_state *, int cmd_find_from_nothing(struct cmd_find_state *, int); /* cmd.c */ +extern const struct cmd_entry *cmd_table[]; void printflike(3, 4) cmd_log_argv(int, char **, const char *, ...); void cmd_prepend_argv(int *, char ***, char *); void cmd_append_argv(int *, char ***, char *); @@ -2111,16 +2098,27 @@ char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); char *cmd_get_alias(const char *); +const struct cmd_entry *cmd_get_entry(struct cmd *); +struct args *cmd_get_args(struct cmd *); +void cmd_get_source(struct cmd *, const char **, u_int *); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); void cmd_free(struct cmd *); char *cmd_print(struct cmd *); +struct cmd_list *cmd_list_new(void); +void cmd_list_append(struct cmd_list *, struct cmd *); +void cmd_list_move(struct cmd_list *, struct cmd_list *); +void cmd_list_free(struct cmd_list *); +char *cmd_list_print(struct cmd_list *, int); +struct cmd *cmd_list_first(struct cmd_list *, u_int *); +struct cmd *cmd_list_next(struct cmd *, u_int *); +int cmd_list_all_have(struct cmd_list *, int); +int cmd_list_any_have(struct cmd_list *, int); int cmd_mouse_at(struct window_pane *, struct mouse_event *, u_int *, u_int *, int); struct winlink *cmd_mouse_window(struct mouse_event *, struct session **); struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **, struct winlink **); char *cmd_template_replace(const char *, const char *, int); -extern const struct cmd_entry *cmd_table[]; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int, @@ -2136,13 +2134,6 @@ struct cmd_parse_result *cmd_parse_from_buffer(const void *, size_t, struct cmd_parse_result *cmd_parse_from_arguments(int, char **, struct cmd_parse_input *); -/* cmd-list.c */ -struct cmd_list *cmd_list_new(void); -void cmd_list_append(struct cmd_list *, struct cmd *); -void cmd_list_move(struct cmd_list *, struct cmd_list *); -void cmd_list_free(struct cmd_list *); -char *cmd_list_print(struct cmd_list *, int); - /* cmd-queue.c */ struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, struct mouse_event *, int); From 04cdd035250b93b728678d515b69690653dced4e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 10:59:58 +0000 Subject: [PATCH 0150/1006] Also move cmdq_item and cmdq_list into cmd-queue.c (this is to make its use more clearly defined and preparation for some future work). --- cmd-attach-session.c | 16 +++--- cmd-break-pane.c | 15 +++--- cmd-capture-pane.c | 4 +- cmd-choose-tree.c | 5 +- cmd-copy-mode.c | 10 ++-- cmd-detach-client.c | 13 ++--- cmd-display-menu.c | 45 ++++++++-------- cmd-display-message.c | 9 ++-- cmd-find-window.c | 6 +-- cmd-find.c | 11 ++-- cmd-if-shell.c | 22 ++++---- cmd-join-pane.c | 15 +++--- cmd-kill-pane.c | 5 +- cmd-kill-session.c | 9 ++-- cmd-kill-window.c | 5 +- cmd-list-buffers.c | 2 +- cmd-list-clients.c | 5 +- cmd-list-keys.c | 2 +- cmd-list-panes.c | 9 ++-- cmd-list-sessions.c | 2 +- cmd-list-windows.c | 7 +-- cmd-load-buffer.c | 7 +-- cmd-lock-server.c | 7 +-- cmd-move-window.c | 37 ++++++------- cmd-new-session.c | 15 +++--- cmd-new-window.c | 10 ++-- cmd-paste-buffer.c | 3 +- cmd-pipe-pane.c | 9 ++-- cmd-queue.c | 117 +++++++++++++++++++++++++++++++++++++++-- cmd-rename-session.c | 5 +- cmd-rename-window.c | 12 ++--- cmd-resize-pane.c | 11 ++-- cmd-resize-window.c | 5 +- cmd-respawn-pane.c | 7 +-- cmd-respawn-window.c | 5 +- cmd-rotate-window.c | 6 ++- cmd-run-shell.c | 15 +++--- cmd-save-buffer.c | 7 +-- cmd-select-layout.c | 5 +- cmd-select-pane.c | 10 ++-- cmd-select-window.c | 8 +-- cmd-send-keys.c | 27 ++++++---- cmd-set-environment.c | 17 +++--- cmd-set-option.c | 12 ++--- cmd-show-environment.c | 19 +++---- cmd-show-options.c | 12 ++--- cmd-source-file.c | 2 +- cmd-split-window.c | 10 ++-- cmd-swap-pane.c | 10 ++-- cmd-swap-window.c | 15 +++--- cmd-switch-client.c | 17 +++--- cmd-wait-for.c | 4 +- control.c | 4 +- format.c | 29 +++++----- key-bindings.c | 2 +- menu.c | 2 +- notify.c | 13 ++--- popup.c | 12 ++--- server-client.c | 9 ++-- spawn.c | 19 +++---- tmux.h | 51 ++++++------------ window.c | 2 +- 62 files changed, 470 insertions(+), 336 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 23a321bd..4ffd3dd2 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -50,10 +50,12 @@ enum cmd_retval cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, int xflag, int rflag, const char *cflag, int Eflag) { - struct cmd_find_state *current = &item->shared->current; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *current = &shared->current; + struct cmd_find_state target; enum cmd_find_type type; int flags; - struct client *c = item->client, *c_loop; + struct client *c = cmdq_get_client(item), *c_loop; struct session *s; struct winlink *wl; struct window_pane *wp; @@ -80,11 +82,11 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, type = CMD_FIND_SESSION; flags = CMD_FIND_PREFER_UNATTACHED; } - if (cmd_find_target(&item->target, item, tflag, type, flags) != 0) + if (cmd_find_target(&target, item, tflag, type, flags) != 0) return (CMD_RETURN_ERROR); - s = item->target.s; - wl = item->target.wl; - wp = item->target.wp; + s = target.s; + wl = target.wl; + wp = target.wp; if (wl != NULL) { if (wp != NULL) @@ -118,7 +120,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, environ_update(s->options, c->environ, s->environ); c->session = s; - if (~item->shared->flags & CMDQ_SHARED_REPEAT) + if (~shared->flags & CMDQ_SHARED_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index b4997f91..6f82ee42 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -49,15 +49,18 @@ static enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmd_find_state *current = &item->shared->current; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *current = &shared->current; + struct cmd_find_state *target = cmdq_get_target(item); + struct cmd_find_state *source = cmdq_get_source(item); struct client *c = cmd_find_client(item, NULL, 1); - struct winlink *wl = item->source.wl; - struct session *src_s = item->source.s; - struct session *dst_s = item->target.s; - struct window_pane *wp = item->source.wp; + struct winlink *wl = source->wl; + struct session *src_s = source->s; + struct session *dst_s = target->s; + struct window_pane *wp = source->wp; struct window *w = wl->window; char *name, *cause; - int idx = item->target.idx; + int idx = target->idx; const char *template; char *cp; diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 3510142e..588b0fd5 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -193,8 +193,8 @@ static enum cmd_retval cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct client *c = item->client; - struct window_pane *wp = item->target.wp; + struct client *c = cmdq_get_client(item); + struct window_pane *wp = cmdq_get_target(item)->wp; char *buf, *cause; const char *bufname; size_t len; diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 8b667ec5..0ada8fd4 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -72,7 +72,8 @@ static enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct window_pane *wp = item->target.wp; + struct cmd_find_state *target = cmdq_get_target(item); + struct window_pane *wp = target->wp; const struct window_mode *mode; if (cmd_get_entry(self) == &cmd_choose_buffer_entry) { @@ -86,6 +87,6 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) } else mode = &window_tree_mode; - window_pane_set_mode(wp, NULL, mode, &item->target, args); + window_pane_set_mode(wp, NULL, mode, target, args); return (CMD_RETURN_NORMAL); } diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 3b20689c..d5e132f9 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -57,10 +57,12 @@ static enum cmd_retval cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = item->shared; - struct client *c = item->client; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *source = cmdq_get_source(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *c = cmdq_get_client(item); struct session *s; - struct window_pane *wp = item->target.wp, *swp; + struct window_pane *wp = target->wp, *swp; if (args_has(args, 'q')) { window_pane_reset_mode_all(wp); @@ -80,7 +82,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 's')) - swp = item->source.wp; + swp = source->wp; else swp = wp; if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) { diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 34682459..f4e350a8 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -57,11 +57,12 @@ const struct cmd_entry cmd_suspend_client_entry = { static enum cmd_retval cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct client *c, *cloop; - struct session *s; - enum msgtype msgtype; - const char *cmd = args_get(args, 'E'); + struct args *args = cmd_get_args(self); + struct cmd_find_state *source = cmdq_get_source(item); + struct client *c, *cloop; + struct session *s; + enum msgtype msgtype; + const char *cmd = args_get(args, 'E'); if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); @@ -77,7 +78,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item) msgtype = MSG_DETACH; if (args_has(args, 's')) { - s = item->source.s; + s = source->s; if (s == NULL) return (CMD_RETURN_NORMAL); TAILQ_FOREACH(cloop, &clients, entry) { diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 74637ceb..ada3ef23 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -65,9 +65,11 @@ static void cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, struct args *args, u_int *px, u_int *py, u_int w, u_int h) { + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *target = cmdq_get_target(item); struct session *s = c->session; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; struct style_ranges *ranges; struct style_range *sr; const char *xp, *yp; @@ -97,9 +99,9 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, *px = wp->xoff - ox; else *px = 0; - } else if (strcmp(xp, "M") == 0 && item->shared->mouse.valid) { - if (item->shared->mouse.x > w / 2) - *px = item->shared->mouse.x - w / 2; + } else if (strcmp(xp, "M") == 0 && shared->mouse.valid) { + if (shared->mouse.x > w / 2) + *px = shared->mouse.x - w / 2; else *px = 0; } else if (strcmp(xp, "W") == 0) { @@ -131,8 +133,8 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, *py = wp->yoff + wp->sy - oy; else *py = 0; - } else if (strcmp(yp, "M") == 0 && item->shared->mouse.valid) - *py = item->shared->mouse.y + h; + } else if (strcmp(yp, "M") == 0 && shared->mouse.valid) + *py = shared->mouse.y + h; else if (strcmp(yp, "S") == 0) { if (options_get_number(s->options, "status-position") == 0) { if (lines != 0) @@ -172,11 +174,9 @@ static enum cmd_retval cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *target = cmdq_get_target(item); struct client *c; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; - struct cmd_find_state *fs = &item->target; struct menu *menu = NULL; struct menu_item menu_item; const char *key; @@ -190,16 +190,15 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); if (args_has(args, 'T')) - title = format_single(item, args_get(args, 'T'), c, s, wl, wp); + title = format_single_from_target(item, args_get(args, 'T'), c); else title = xstrdup(""); - menu = menu_create(title); for (i = 0; i != args->argc; /* nothing */) { name = args->argv[i++]; if (*name == '\0') { - menu_add_item(menu, NULL, item, c, fs); + menu_add_item(menu, NULL, item, c, target); continue; } @@ -215,7 +214,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) menu_item.key = key_string_lookup_string(key); menu_item.command = args->argv[i++]; - menu_add_item(menu, &menu_item, item, c, fs); + menu_add_item(menu, &menu_item, item, c, target); } free(title); if (menu == NULL) { @@ -229,9 +228,9 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) cmd_display_menu_get_position(c, item, args, &px, &py, menu->width + 4, menu->count + 2); - if (!item->shared->mouse.valid) + if (!shared->mouse.valid) flags |= MENU_NOMOUSE; - if (menu_display(menu, flags, item, px, py, c, fs, NULL, NULL) != 0) + if (menu_display(menu, flags, item, px, py, c, target, NULL, NULL) != 0) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } @@ -240,8 +239,8 @@ static enum cmd_retval cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct client *c; - struct cmd_find_state *fs = &item->target; const char *value, *cmd = NULL, **lines = NULL; const char *shellcmd = NULL; char *cwd, *cause; @@ -278,7 +277,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) } if (nlines != 0) - w = popup_width(item, nlines, lines, c, fs) + 2; + w = popup_width(item, nlines, lines, c, target) + 2; else w = c->tty.sx / 2; if (args_has(args, 'w')) { @@ -298,13 +297,13 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) value = args_get(args, 'd'); if (value != NULL) - cwd = format_single(item, value, c, fs->s, fs->wl, fs->wp); + cwd = format_single_from_target(item, value, c); else - cwd = xstrdup(server_client_get_cwd(c, fs->s)); + cwd = xstrdup(server_client_get_cwd(c, target->s)); value = args_get(args, 'R'); if (value != NULL) - shellcmd = format_single(item, value, c, fs->s, fs->wl, fs->wp); + shellcmd = format_single_from_target(item, value, c); if (args_has(args, 'K')) flags |= POPUP_WRITEKEYS; @@ -313,7 +312,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, - cmd, cwd, c, fs) != 0) + cmd, cwd, c, target) != 0) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } diff --git a/cmd-display-message.c b/cmd-display-message.c index d4d4ad25..8fb09f20 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -61,10 +61,11 @@ static enum cmd_retval cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct client *c, *target_c; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; const char *template; char *msg, *cause; struct format_tree *ft; @@ -105,7 +106,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) flags = FORMAT_VERBOSE; else flags = 0; - ft = format_create(item->client, item, FORMAT_NONE, flags); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, flags); format_defaults(ft, target_c, s, wl, wp); if (args_has(args, 'a')) { diff --git a/cmd-find-window.c b/cmd-find-window.c index 798754d1..e1faeb2f 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -45,7 +45,8 @@ static enum cmd_retval cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self), *new_args; - struct window_pane *wp = item->target.wp; + struct cmd_find_state *target = cmdq_get_target(item); + struct window_pane *wp = target->wp; const char *s = args->argv[0]; char *filter, *argv = { NULL }; int C, N, T; @@ -116,8 +117,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) args_set(new_args, 'Z', NULL); args_set(new_args, 'f', filter); - window_pane_set_mode(wp, NULL, &window_tree_mode, &item->target, - new_args); + window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args); args_free(new_args); free(filter); diff --git a/cmd-find.c b/cmd-find.c index 81afb423..a258a10f 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -961,10 +961,11 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { fs->current = &marked_pane; log_debug("%s: current is marked pane", __func__); - } else if (cmd_find_valid_state(&item->shared->current)) { - fs->current = &item->shared->current; + } else if (cmd_find_valid_state(&cmdq_get_shared(item)->current)) { + fs->current = &cmdq_get_shared(item)->current; log_debug("%s: current is from queue", __func__); - } else if (cmd_find_from_client(¤t, item->client, flags) == 0) { + } else if (cmd_find_from_client(¤t, cmdq_get_client(item), + flags) == 0) { fs->current = ¤t; log_debug("%s: current is from client", __func__); } else { @@ -981,7 +982,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, /* Mouse target is a plain = or {mouse}. */ if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { - m = &item->shared->mouse; + m = &cmdq_get_shared(item)->mouse; switch (type) { case CMD_FIND_PANE: fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); @@ -1237,7 +1238,7 @@ cmd_find_current_client(struct cmdq_item *item, int quiet) struct cmd_find_state fs; if (item != NULL) - c = item->client; + c = cmdq_get_client(item); if (c != NULL && c->session != NULL) return (c); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index a678cf40..a385f9f7 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -63,20 +63,19 @@ static enum cmd_retval cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct mouse_event *m = &item->shared->mouse; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct mouse_event *m = &shared->mouse; struct cmd_if_shell_data *cdata; char *shellcmd, *cmd; const char *file; struct cmdq_item *new_item; - struct cmd_find_state *fs = &item->target; struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = fs->s; - struct winlink *wl = fs->wl; - struct window_pane *wp = fs->wp; + struct session *s = target->s; struct cmd_parse_input pi; struct cmd_parse_result *pr; - shellcmd = format_single(item, args->argv[0], c, s, wl, wp); + shellcmd = format_single_from_target(item, args->argv[0], c); if (args_has(args, 'F')) { if (*shellcmd != '0' && *shellcmd != '\0') cmd = args->argv[1]; @@ -92,7 +91,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cmd_get_source(self, &pi.file, &pi.line); pi.item = item; pi.c = c; - cmd_find_copy_state(&pi.fs, fs); + cmd_find_copy_state(&pi.fs, target); pr = cmd_parse_from_string(cmd, &pi); switch (pr->status) { @@ -103,7 +102,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) free(pr->error); return (CMD_RETURN_ERROR); case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); + new_item = cmdq_get_command(pr->cmdlist, target, m, 0); cmdq_insert_after(item, new_item); cmd_list_free(pr->cmdlist); break; @@ -121,7 +120,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) memcpy(&cdata->mouse, m, sizeof cdata->mouse); if (!args_has(args, 'b')) - cdata->client = item->client; + cdata->client = cmdq_get_client(item); else cdata->client = c; if (cdata->client != NULL) @@ -139,9 +138,10 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->input.c = c; if (cdata->input.c != NULL) cdata->input.c->references++; - cmd_find_copy_state(&cdata->input.fs, fs); + cmd_find_copy_state(&cdata->input.fs, target); - if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, + if (job_run(shellcmd, s, + server_client_get_cwd(cmdq_get_client(item), s), NULL, cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1, -1) == NULL) { cmdq_error(item, "failed to run command: %s", shellcmd); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 2d26d82b..ab0fea62 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -64,7 +64,10 @@ static enum cmd_retval cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmd_find_state *current = &item->shared->current; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *current = &shared->current; + struct cmd_find_state *target = cmdq_get_target(item); + struct cmd_find_state *source = cmdq_get_source(item); struct session *dst_s; struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; @@ -80,15 +83,15 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) else not_same_window = 0; - dst_s = item->target.s; - dst_wl = item->target.wl; - dst_wp = item->target.wp; + dst_s = target->s; + dst_wl = target->wl; + dst_wp = target->wp; dst_w = dst_wl->window; dst_idx = dst_wl->idx; server_unzoom_window(dst_w); - src_wl = item->source.wl; - src_wp = item->source.wp; + src_wl = source->wl; + src_wp = source->wp; src_w = src_wl->window; server_unzoom_window(src_w); diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 301b9fa6..2302d7bb 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -45,8 +45,9 @@ static enum cmd_retval cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct winlink *wl = item->target.wl; - struct window_pane *loopwp, *tmpwp, *wp = item->target.wp; + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; + struct window_pane *loopwp, *tmpwp, *wp = target->wp; if (args_has(args, 'a')) { server_unzoom_window(wl->window); diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 978d1b9c..c10efba6 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -45,11 +45,10 @@ const struct cmd_entry cmd_kill_session_entry = { static enum cmd_retval cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct session *s, *sloop, *stmp; - struct winlink *wl; - - s = item->target.s; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct session *s = target->s, *sloop, *stmp; + struct winlink *wl; if (args_has(args, 'C')) { RB_FOREACH(wl, winlinks, &s->windows) { diff --git a/cmd-kill-window.c b/cmd-kill-window.c index c788ff38..68139faa 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -56,9 +56,10 @@ static enum cmd_retval cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct winlink *wl = item->target.wl, *wl2, *wl3; + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl, *wl2, *wl3; struct window *w = wl->window; - struct session *s = item->target.s; + struct session *s = target->s; if (cmd_get_entry(self) == &cmd_unlink_window_entry) { if (!args_has(args, 'k') && !session_is_linked(s, w)) { diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 784289d8..45d5a4ee 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -59,7 +59,7 @@ cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item) pb = NULL; while ((pb = paste_walk(pb)) != NULL) { - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_defaults_paste_buffer(ft, pb); if (filter != NULL) { diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 9cec5199..75118c8e 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -52,6 +52,7 @@ static enum cmd_retval cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct client *c; struct session *s; struct format_tree *ft; @@ -60,7 +61,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) char *line; if (args_has(args, 't')) - s = item->target.s; + s = target->s; else s = NULL; @@ -72,7 +73,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) if (c->session == NULL || (s != NULL && s != c->session)) continue; - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_add(ft, "line", "%u", idx); format_defaults(ft, c, NULL, NULL, NULL); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index d6c261d8..cfdceee7 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -329,7 +329,7 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) "#{command_list_usage}"; } - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_defaults(ft, NULL, NULL, NULL, NULL); for (entryp = cmd_table; *entryp != NULL; entryp++) { diff --git a/cmd-list-panes.c b/cmd-list-panes.c index ac97558e..c6dcff23 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -50,9 +50,10 @@ const struct cmd_entry cmd_list_panes_entry = { static enum cmd_retval cmd_list_panes_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct session *s = target->s; + struct winlink *wl = target->wl; if (args_has(args, 'a')) cmd_list_panes_server(self, item); @@ -125,7 +126,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 1c6e1e9c..fbc3db1d 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -66,7 +66,7 @@ cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item) n = 0; RB_FOREACH(s, sessions, &sessions) { - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, NULL, NULL); diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 905e9eaf..9c33c2d0 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -61,12 +61,13 @@ const struct cmd_entry cmd_list_windows_entry = { static enum cmd_retval cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); if (args_has(args, 'a')) cmd_list_windows_server(self, item); else - cmd_list_windows_session(self, item->target.s, item, 0); + cmd_list_windows_session(self, target->s, item, 0); return (CMD_RETURN_NORMAL); } @@ -107,7 +108,7 @@ cmd_list_windows_session(struct cmd *self, struct session *s, n = 0; RB_FOREACH(wl, winlinks, &s->windows) { - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, NULL); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 588623f7..1fc45b0f 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -86,9 +86,6 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_load_buffer_data *cdata; struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; const char *bufname = args_get(args, 'b'); char *path; @@ -99,8 +96,8 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) else cdata->name = NULL; - path = format_single(item, args->argv[0], c, s, wl, wp); - file_read(item->client, path, cmd_load_buffer_done, cdata); + path = format_single_from_target(item, args->argv[0], c); + file_read(cmdq_get_client(item), path, cmd_load_buffer_done, cdata); free(path); return (CMD_RETURN_WAIT); diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 937fb126..086cf639 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -64,13 +64,14 @@ const struct cmd_entry cmd_lock_client_entry = { static enum cmd_retval cmd_lock_server_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct client *c; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *c; if (cmd_get_entry(self) == &cmd_lock_server_entry) server_lock(); else if (cmd_get_entry(self) == &cmd_lock_session_entry) - server_lock_session(item->target.s); + server_lock_session(target->s); else { if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); diff --git a/cmd-move-window.c b/cmd-move-window.c index aaeb12b0..eb6f4f1a 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -59,32 +59,34 @@ const struct cmd_entry cmd_link_window_entry = { static enum cmd_retval cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - const char *tflag = args_get(args, 't'); - struct session *src; - struct session *dst; - struct winlink *wl; - char *cause; - int idx, kflag, dflag, sflag; + struct args *args = cmd_get_args(self); + struct cmd_find_state *source = cmdq_get_source(item); + struct cmd_find_state target; + const char *tflag = args_get(args, 't'); + struct session *src; + struct session *dst; + struct winlink *wl; + char *cause; + int idx, kflag, dflag, sflag; if (args_has(args, 'r')) { - if (cmd_find_target(&item->target, item, tflag, - CMD_FIND_SESSION, CMD_FIND_QUIET) != 0) + if (cmd_find_target(&target, item, tflag, CMD_FIND_SESSION, + CMD_FIND_QUIET) != 0) return (CMD_RETURN_ERROR); - session_renumber_windows(item->target.s); + session_renumber_windows(target.s); recalculate_sizes(); - server_status_session(item->target.s); + server_status_session(target.s); return (CMD_RETURN_NORMAL); } - if (cmd_find_target(&item->target, item, tflag, CMD_FIND_WINDOW, + if (cmd_find_target(&target, item, tflag, CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX) != 0) return (CMD_RETURN_ERROR); - src = item->source.s; - dst = item->target.s; - wl = item->source.wl; - idx = item->target.idx; + src = source->s; + dst = target.s; + wl = source->wl; + idx = target.idx; kflag = args_has(args, 'k'); dflag = args_has(args, 'd'); @@ -95,8 +97,7 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } - if (server_link_window(src, wl, dst, idx, kflag, !dflag, - &cause) != 0) { + if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { cmdq_error(item, "can't link window: %s", cause); free(cause); return (CMD_RETURN_ERROR); diff --git a/cmd-new-session.c b/cmd-new-session.c index 73193e96..8ce428c9 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -67,7 +67,10 @@ static enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct client *c = item->client; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *current = &shared->current; + struct cmd_find_state *target = cmdq_get_target(item); + struct client *c = cmdq_get_client(item); struct session *s, *as, *groupwith; struct environ *env; struct options *oo; @@ -106,7 +109,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (newname != NULL) as = session_find(newname); else - as = item->target.s; + as = target->s; if (as != NULL) { retval = cmd_attach_session(item, as->name, args_has(args, 'D'), args_has(args, 'X'), 0, NULL, @@ -123,7 +126,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) /* Is this going to be part of a session group? */ group = args_get(args, 't'); if (group != NULL) { - groupwith = item->target.s; + groupwith = target->s; if (groupwith == NULL) { if (!session_check_name(group)) { cmdq_error(item, "bad group name: %s", group); @@ -172,7 +175,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) * over. */ if (!detached && !already_attached && c->tty.fd != -1) { - if (server_client_check_nested(item->client)) { + if (server_client_check_nested(cmdq_get_client(item))) { cmdq_error(item, "sessions should be nested with care, " "unset $TMUX to force"); goto fail; @@ -314,7 +317,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) } else if (c->session != NULL) c->last_session = c->session; c->session = s; - if (~item->shared->flags & CMDQ_SHARED_REPEAT) + if (~shared->flags & CMDQ_SHARED_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); @@ -344,7 +347,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (!detached) { c->flags |= CLIENT_ATTACHED; - cmd_find_from_session(&item->shared->current, s, 0); + cmd_find_from_session(current, s, 0); } cmd_find_from_session(&fs, s, 0); diff --git a/cmd-new-window.c b/cmd-new-window.c index 5f1c11cb..fcd480c6 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -52,12 +52,14 @@ static enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmd_find_state *current = &item->shared->current; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *current = &shared->current; + struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - int idx = item->target.idx; + struct session *s = target->s; + struct winlink *wl = target->wl; + int idx = target->idx; struct winlink *new_wl; char *cause = NULL, *cp; const char *template, *add; diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index c79da012..22fa8bd5 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -48,7 +48,8 @@ static enum cmd_retval cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct window_pane *wp = item->target.wp; + struct cmd_find_state *target = cmdq_get_target(item); + struct window_pane *wp = target->wp; struct paste_buffer *pb; const char *sepstr, *bufname, *bufdata, *bufend, *line; size_t seplen, bufsize; diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 2e898dbc..4ccc22c6 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -57,10 +57,11 @@ static enum cmd_retval cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmd_find_client(item, NULL, 1); - struct window_pane *wp = item->target.wp; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; + struct window_pane *wp = target->wp; + struct session *s = target->s; + struct winlink *wl = target->wl; char *cmd; int old_fd, pipe_fd[2], null_fd, in, out; struct format_tree *ft; @@ -108,7 +109,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) } /* Expand the command. */ - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_defaults(ft, c, s, wl, wp); cmd = format_expand_time(ft, args->argv[0]); format_free(ft); diff --git a/cmd-queue.c b/cmd-queue.c index 892bd03b..28642721 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -25,8 +25,41 @@ #include "tmux.h" -/* Global command queue. */ -static struct cmdq_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue); +/* Command queue item type. */ +enum cmdq_type { + CMDQ_COMMAND, + CMDQ_CALLBACK, +}; + +/* Command queue item. */ +struct cmdq_item { + char *name; + struct cmdq_list *queue; + struct cmdq_item *next; + + struct client *client; + + enum cmdq_type type; + u_int group; + + u_int number; + time_t time; + + int flags; + + struct cmdq_shared *shared; + struct cmd_find_state source; + struct cmd_find_state target; + + struct cmd_list *cmdlist; + struct cmd *cmd; + + cmdq_cb cb; + void *data; + + TAILQ_ENTRY(cmdq_item) entry; +}; +TAILQ_HEAD(cmdq_list, cmdq_item); /* Get command queue name. */ static const char * @@ -47,9 +80,83 @@ cmdq_name(struct client *c) static struct cmdq_list * cmdq_get(struct client *c) { - if (c == NULL) - return (&global_queue); - return (&c->queue); + static struct cmdq_list *global_queue; + + if (c == NULL) { + if (global_queue == NULL) + global_queue = cmdq_new(); + return (global_queue); + } + return (c->queue); +} + +/* Create a queue. */ +struct cmdq_list * +cmdq_new(void) +{ + struct cmdq_list *queue; + + queue = xcalloc (1, sizeof *queue); + TAILQ_INIT (queue); + return (queue); +} + +/* Free a queue. */ +void +cmdq_free(struct cmdq_list *queue) +{ + if (!TAILQ_EMPTY(queue)) + fatalx("queue not empty"); + free(queue); +} + +/* Get item name. */ +const char * +cmdq_get_name(struct cmdq_item *item) +{ + return (item->name); +} + +/* Get item client. */ +struct client * +cmdq_get_client(struct cmdq_item *item) +{ + return (item->client); +} + +/* Get item target. */ +struct cmd_find_state * +cmdq_get_target(struct cmdq_item *item) +{ + return (&item->target); +} + +/* Get item source. */ +struct cmd_find_state * +cmdq_get_source(struct cmdq_item *item) +{ + return (&item->source); +} + +/* Get item shared. */ +struct cmdq_shared * +cmdq_get_shared(struct cmdq_item *item) +{ + return (item->shared); +} + +/* Merge formats from item. */ +void +cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft) +{ + const struct cmd_entry *entry; + + if (item->cmd != NULL) { + entry = cmd_get_entry (item->cmd); + format_add(ft, "command", "%s", entry->name); + } + if (item->shared->formats != NULL) + format_merge(ft, item->shared->formats); } /* Append an item. */ diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 67acb016..5f003473 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -47,11 +47,12 @@ static enum cmd_retval cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; + struct session *s = target->s; char *newname; - newname = format_single(item, args->argv[0], c, s, NULL, NULL); + newname = format_single_from_target(item, args->argv[0], c); if (strcmp(newname, s->name) == 0) { free(newname); return (CMD_RETURN_NORMAL); diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 0f28c665..d3e6d883 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -45,13 +45,13 @@ const struct cmd_entry cmd_rename_window_entry = { static enum cmd_retval cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - char *newname; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *c = cmd_find_client(item, NULL, 1); + struct winlink *wl = target->wl; + char *newname; - newname = format_single(item, args->argv[0], c, s, wl, NULL); + newname = format_single_from_target(item, args->argv[0], c); window_set_name(wl->window, newname); options_set_number(wl->window->options, "automatic-rename", 0); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 7b9eaf88..0579616a 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -50,12 +50,13 @@ static enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = item->shared; - struct window_pane *wp = item->target.wp; - struct winlink *wl = item->target.wl; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct window_pane *wp = target->wp; + struct winlink *wl = target->wl; struct window *w = wl->window; - struct client *c = item->client; - struct session *s = item->target.s; + struct client *c = cmdq_get_client(item); + struct session *s = target->s; const char *errstr; char *cause; u_int adjust; diff --git a/cmd-resize-window.c b/cmd-resize-window.c index 6ac2d235..1ebb7aca 100644 --- a/cmd-resize-window.c +++ b/cmd-resize-window.c @@ -47,9 +47,10 @@ static enum cmd_retval cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct winlink *wl = item->target.wl; + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; struct window *w = wl->window; - struct session *s = item->target.s; + struct session *s = target->s; const char *errstr; char *cause; u_int adjust, sx, sy; diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 3d4686f2..498a89a7 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -48,10 +48,11 @@ static enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; char *cause = NULL; const char *add; struct args_value *value; diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 91269ff5..9e745dda 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -48,9 +48,10 @@ static enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; + struct session *s = target->s; + struct winlink *wl = target->wl; char *cause = NULL; const char *add; struct args_value *value; diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index eaba2faf..8d47688e 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -44,8 +44,10 @@ static enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmd_find_state *current = &item->shared->current; - struct winlink *wl = item->target.wl; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *current = &shared->current; + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; struct window *w = wl->window; struct window_pane *wp, *wp2; struct layout_cell *lc; diff --git a/cmd-run-shell.c b/cmd-run-shell.c index b828576a..3c82ffdb 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -90,11 +90,11 @@ static enum cmd_retval cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct cmd_run_shell_data *cdata; struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; + struct session *s = target->s; + struct window_pane *wp = target->wp; const char *delay; double d; struct timeval tv; @@ -102,7 +102,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) cdata = xcalloc(1, sizeof *cdata); if (args->argc != 0) - cdata->cmd = format_single(item, args->argv[0], c, s, wl, wp); + cdata->cmd = format_single_from_target(item, args->argv[0], c); if (args_has(args, 't') && wp != NULL) cdata->wp_id = wp->id; @@ -112,7 +112,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'b')) cdata->item = item; - cdata->cwd = xstrdup(server_client_get_cwd(item->client, s)); + cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s)); cdata->s = s; if (s != NULL) session_add_ref(s, __func__); @@ -197,8 +197,9 @@ cmd_run_shell_callback(struct job *job) free(msg); if (item != NULL) { - if (item->client != NULL && item->client->session == NULL) - item->client->retval = retcode; + if (cmdq_get_client(item) != NULL && + cmdq_get_client(item)->session == NULL) + cmdq_get_client(item)->retval = retcode; cmdq_continue(item); } } diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index f96e9849..9aaa365f 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -75,9 +75,6 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; struct paste_buffer *pb; int flags; const char *bufname = args_get(args, 'b'), *bufdata; @@ -101,12 +98,12 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) if (cmd_get_entry(self) == &cmd_show_buffer_entry) path = xstrdup("-"); else - path = format_single(item, args->argv[0], c, s, wl, wp); + path = format_single_from_target(item, args->argv[0], c); if (args_has(args, 'a')) flags = O_APPEND; else flags = 0; - file_write(item->client, path, flags, bufdata, bufsize, + file_write(cmdq_get_client(item), path, flags, bufdata, bufsize, cmd_save_buffer_done, item); free(path); diff --git a/cmd-select-layout.c b/cmd-select-layout.c index b51ab4c1..7069eccc 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -72,9 +72,10 @@ static enum cmd_retval cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct winlink *wl = item->target.wl; + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; struct window *w = wl->window; - struct window_pane *wp = item->target.wp; + struct window_pane *wp = target->wp; const char *layoutname; char *oldlayout; int next, previous, layout; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 376bf62e..b3dd28ba 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -85,12 +85,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); const struct cmd_entry *entry = cmd_get_entry(self); - struct cmd_find_state *current = &item->shared->current; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *current = &shared->current; + struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmd_find_client(item, NULL, 1); - struct winlink *wl = item->target.wl; + struct winlink *wl = target->wl; struct window *w = wl->window; - struct session *s = item->target.s; - struct window_pane *wp = item->target.wp, *lastwp, *markedwp; + struct session *s = target->s; + struct window_pane *wp = target->wp, *lastwp, *markedwp; char *pane_title; const char *style; struct style *sy; diff --git a/cmd-select-window.c b/cmd-select-window.c index 3d8d02de..18011ce0 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -85,9 +85,11 @@ static enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmd_find_state *current = &item->shared->current; - struct winlink *wl = item->target.wl; - struct session *s = item->target.s; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *current = &shared->current; + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; + struct session *s = target->s; int next, previous, last, activity; next = (cmd_get_entry(self) == &cmd_next_window_entry); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 84996723..384cb460 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -60,6 +60,7 @@ static struct cmdq_item * cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, struct cmdq_item *item, key_code key) { + struct cmd_find_state *target = cmdq_get_target(item); struct session *s = fs->s; struct winlink *wl = fs->wl; struct window_pane *wp = fs->wp; @@ -71,7 +72,8 @@ cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, if (wme == NULL || wme->mode->key_table == NULL) { if (options_get_number(fs->wp->window->options, "xterm-keys")) key |= KEYC_XTERM; - if (window_pane_key(wp, item->client, s, wl, key, NULL) != 0) + if (window_pane_key(wp, cmdq_get_client(item), s, wl, key, + NULL) != 0) return (NULL); return (item); } @@ -80,7 +82,7 @@ cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, bd = key_bindings_get(table, key & ~KEYC_XTERM); if (bd != NULL) { table->references++; - item = key_bindings_dispatch(bd, item, c, NULL, &item->target); + item = key_bindings_dispatch(bd, item, c, NULL, target); key_bindings_unref_table(table); } return (item); @@ -132,12 +134,13 @@ static enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmd_find_client(item, NULL, 1); - struct cmd_find_state *fs = &item->target; - struct window_pane *wp = item->target.wp; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct mouse_event *m = &item->shared->mouse; + struct window_pane *wp = target->wp; + struct session *s = target->s; + struct winlink *wl = target->wl; + struct mouse_event *m = &shared->mouse; struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); int i; key_code key; @@ -177,7 +180,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "no mouse target"); return (CMD_RETURN_ERROR); } - window_pane_key(wp, item->client, s, wl, m->key, m); + window_pane_key(wp, cmdq_get_client(item), s, wl, m->key, m); return (CMD_RETURN_NORMAL); } @@ -186,7 +189,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) key = options_get_number(s->options, "prefix2"); else key = options_get_number(s->options, "prefix"); - cmd_send_keys_inject_key(c, fs, item, key); + cmd_send_keys_inject_key(c, target, item, key); return (CMD_RETURN_NORMAL); } @@ -196,8 +199,10 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) } for (; np != 0; np--) { - for (i = 0; i < args->argc; i++) - item = cmd_send_keys_inject_string(c, fs, item, args, i); + for (i = 0; i < args->argc; i++) { + item = cmd_send_keys_inject_string(c, target, item, + args, i); + } } return (CMD_RETURN_NORMAL); diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 72e40ded..3c43b635 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -46,9 +46,10 @@ const struct cmd_entry cmd_set_environment_entry = { static enum cmd_retval cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct environ *env; - const char *name, *value, *target; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct environ *env; + const char *name, *value, *tflag; name = args->argv[0]; if (*name == '\0') { @@ -68,15 +69,15 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'g')) env = global_environ; else { - if (item->target.s == NULL) { - target = args_get(args, 't'); - if (target != NULL) - cmdq_error(item, "no such session: %s", target); + if (target->s == NULL) { + tflag = args_get(args, 't'); + if (tflag != NULL) + cmdq_error(item, "no such session: %s", tflag); else cmdq_error(item, "no current session"); return (CMD_RETURN_ERROR); } - env = item->target.s->environ; + env = target->s->environ; } if (args_has(args, 'u')) { diff --git a/cmd-set-option.c b/cmd-set-option.c index 041f109b..4e81dc5e 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -83,10 +83,9 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); - struct cmd_find_state *fs = &item->target; + struct cmd_find_state *target = cmdq_get_target(item); struct client *c, *loop; - struct session *s = fs->s; - struct winlink *wl = fs->wl; + struct session *s = target->s; struct window *w; struct window_pane *wp; struct options *oo; @@ -100,7 +99,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) /* Expand argument. */ c = cmd_find_client(item, NULL, 1); - argument = format_single(item, args->argv[0], c, s, wl, NULL); + argument = format_single_from_target(item, args->argv[0], c); /* If set-hook -R, fire the hook straight away. */ if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) { @@ -123,12 +122,13 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) if (args->argc < 2) value = NULL; else if (args_has(args, 'F')) - value = format_single(item, args->argv[1], c, s, wl, NULL); + value = format_single_from_target(item, args->argv[1], c); else value = xstrdup(args->argv[1]); /* Get the scope and table for the option .*/ - scope = options_scope_from_name(args, window, name, fs, &oo, &cause); + scope = options_scope_from_name(args, window, name, target, &oo, + &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) goto out; diff --git a/cmd-show-environment.c b/cmd-show-environment.c index be6209dd..3ad31400 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -98,13 +98,14 @@ static enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct environ *env; struct environ_entry *envent; - const char *target; + const char *tflag; - if ((target = args_get(args, 't')) != NULL) { - if (item->target.s == NULL) { - cmdq_error(item, "no such session: %s", target); + if ((tflag = args_get(args, 't')) != NULL) { + if (target->s == NULL) { + cmdq_error(item, "no such session: %s", tflag); return (CMD_RETURN_ERROR); } } @@ -112,15 +113,15 @@ cmd_show_environment_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'g')) env = global_environ; else { - if (item->target.s == NULL) { - target = args_get(args, 't'); - if (target != NULL) - cmdq_error(item, "no such session: %s", target); + if (target->s == NULL) { + tflag = args_get(args, 't'); + if (tflag != NULL) + cmdq_error(item, "no such session: %s", tflag); else cmdq_error(item, "no current session"); return (CMD_RETURN_ERROR); } - env = item->target.s->environ; + env = target->s->environ; } if (args->argc != 0) { diff --git a/cmd-show-options.c b/cmd-show-options.c index c1f9fd56..5b62a78e 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -78,10 +78,8 @@ static enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmd_find_state *fs = &item->target; + struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; struct options *oo; char *argument, *name = NULL, *cause; int window, idx, ambiguous, parent, scope; @@ -90,7 +88,8 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) window = (cmd_get_entry(self) == &cmd_show_window_options_entry); if (args->argc == 0) { - scope = options_scope_from_flags(args, window, fs, &oo, &cause); + scope = options_scope_from_flags(args, window, target, &oo, + &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) return (CMD_RETURN_NORMAL); @@ -100,7 +99,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) } return (cmd_show_options_all(self, item, scope, oo)); } - argument = format_single(item, args->argv[0], c, s, wl, NULL); + argument = format_single_from_target(item, args->argv[0], c); name = options_match(argument, &idx, &ambiguous); if (name == NULL) { @@ -112,7 +111,8 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "invalid option: %s", argument); goto fail; } - scope = options_scope_from_name(args, window, name, fs, &oo, &cause); + scope = options_scope_from_name(args, window, name, target, &oo, + &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) goto fail; diff --git a/cmd-source-file.c b/cmd-source-file.c index 50571874..62773872 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -125,7 +125,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_source_file_data *cdata; - struct client *c = item->client; + struct client *c = cmdq_get_client(item); enum cmd_retval retval = CMD_RETURN_NORMAL; char *pattern, *cwd; const char *path, *error; diff --git a/cmd-split-window.c b/cmd-split-window.c index 6a9210a7..9ec4b09e 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -54,12 +54,14 @@ static enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmd_find_state *current = &item->shared->current; + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *current = &shared->current; + struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp, *new_wp; + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp, *new_wp; enum layout_type type; struct layout_cell *lc; struct cmd_find_state fs; diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 60be63dc..021ac224 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -46,15 +46,17 @@ static enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmd_find_state *source = cmdq_get_source(item); + struct cmd_find_state *target = cmdq_get_target(item); struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; - dst_w = item->target.wl->window; - dst_wp = item->target.wp; - src_w = item->source.wl->window; - src_wp = item->source.wp; + dst_w = target->wl->window; + dst_wp = target->wp; + src_w = source->wl->window; + src_wp = source->wp; if (window_push_zoom(dst_w, args_has(args, 'Z'))) server_redraw_window(dst_w); diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 9ed1e8f1..651a44da 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -46,20 +46,19 @@ static enum cmd_retval cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct session *src, *dst; + struct cmd_find_state *source = cmdq_get_source(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct session *src = source->s, *dst = target->s; struct session_group *sg_src, *sg_dst; - struct winlink *wl_src, *wl_dst; + struct winlink *wl_src = source->wl, *wl_dst = target->wl; struct window *w_src, *w_dst; - wl_src = item->source.wl; - src = item->source.s; sg_src = session_group_contains(src); - - wl_dst = item->target.wl; - dst = item->target.s; sg_dst = session_group_contains(dst); - if (src != dst && sg_src != NULL && sg_dst != NULL && + if (src != dst && + sg_src != NULL && + sg_dst != NULL && sg_src == sg_dst) { cmdq_error(item, "can't move window, sessions are grouped"); return (CMD_RETURN_ERROR); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index def61a73..c33b74a1 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -48,6 +48,9 @@ static enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmd_find_state *current = &shared->current; + struct cmd_find_state target; const char *tflag = args_get(args, 't'); enum cmd_find_type type; int flags; @@ -69,11 +72,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) type = CMD_FIND_SESSION; flags = CMD_FIND_PREFER_UNATTACHED; } - if (cmd_find_target(&item->target, item, tflag, type, flags) != 0) + if (cmd_find_target(&target, item, tflag, type, flags) != 0) return (CMD_RETURN_ERROR); - s = item->target.s; - wl = item->target.wl; - wp = item->target.wp; + s = target.s; + wl = target.wl; + wp = target.wp; if (args_has(args, 'r')) c->flags ^= CLIENT_READONLY; @@ -111,7 +114,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } } else { - if (item->client == NULL) + if (cmdq_get_client(item) == NULL) return (CMD_RETURN_NORMAL); if (wl != NULL && wp != NULL) { w = wl->window; @@ -124,7 +127,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) } if (wl != NULL) { session_set_current(s, wl); - cmd_find_from_session(&item->shared->current, s, 0); + cmd_find_from_session(current, s, 0); } } @@ -134,7 +137,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; - if (~item->shared->flags & CMDQ_SHARED_REPEAT) + if (~shared->flags & CMDQ_SHARED_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); diff --git a/cmd-wait-for.c b/cmd-wait-for.c index a5803ca5..807a661a 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -167,7 +167,7 @@ static enum cmd_retval cmd_wait_for_wait(struct cmdq_item *item, const char *name, struct wait_channel *wc) { - struct client *c = item->client; + struct client *c = cmdq_get_client(item); struct wait_item *wi; if (c == NULL) { @@ -198,7 +198,7 @@ cmd_wait_for_lock(struct cmdq_item *item, const char *name, { struct wait_item *wi; - if (item->client == NULL) { + if (cmdq_get_client(item) == NULL) { cmdq_error(item, "not able to lock"); return (CMD_RETURN_ERROR); } diff --git a/control.c b/control.c index b8a16e73..fe9d28c6 100644 --- a/control.c +++ b/control.c @@ -42,7 +42,7 @@ control_write(struct client *c, const char *fmt, ...) static enum cmd_retval control_error(struct cmdq_item *item, void *data) { - struct client *c = item->client; + struct client *c = cmdq_get_client(item); char *error = data; cmdq_guard(item, "begin", 1); @@ -86,7 +86,7 @@ control_callback(__unused struct client *c, __unused const char *path, break; case CMD_PARSE_SUCCESS: item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); - item->shared->flags |= CMDQ_SHARED_CONTROL; + cmdq_get_shared(item)->flags |= CMDQ_SHARED_CONTROL; cmdq_append(c, item); cmd_list_free(pr->cmdlist); break; diff --git a/format.c b/format.c index 2d8522c9..3b361e00 100644 --- a/format.c +++ b/format.c @@ -1108,8 +1108,8 @@ format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) fe->value = s; } -/* Merge a format tree. */ -static void +/* Merge one format tree into another. */ +void format_merge(struct format_tree *ft, struct format_tree *from) { struct format_entry *fe; @@ -1124,21 +1124,16 @@ format_merge(struct format_tree *ft, struct format_tree *from) static void format_create_add_item(struct format_tree *ft, struct cmdq_item *item) { + struct cmdq_shared *shared = cmdq_get_shared(item); struct mouse_event *m; struct window_pane *wp; u_int x, y; - if (item->cmd != NULL) { - format_add(ft, "command", "%s", - cmd_get_entry (item->cmd)->name); - } + cmdq_merge_formats(item, ft); - if (item->shared == NULL) + if (shared == NULL) return; - if (item->shared->formats != NULL) - format_merge(ft, item->shared->formats); - - m = &item->shared->mouse; + m = &shared->mouse; if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) { format_add(ft, "mouse_pane", "%%%u", wp->id); if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) { @@ -2425,7 +2420,7 @@ format_single(struct cmdq_item *item, const char *fmt, struct client *c, char *expanded; if (item != NULL) - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); else ft = format_create(NULL, item, FORMAT_NONE, 0); format_defaults(ft, c, s, wl, wp); @@ -2435,6 +2430,16 @@ format_single(struct cmdq_item *item, const char *fmt, struct client *c, return (expanded); } +/* Expand a single string using target. */ +char * +format_single_from_target(struct cmdq_item *item, const char *fmt, + struct client *c) +{ + struct cmd_find_state *target = cmdq_get_target(item); + + return (format_single(item, fmt, c, target->s, target->wl, target->wp)); +} + /* Set defaults for any of arguments that are not NULL. */ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, diff --git a/key-bindings.c b/key-bindings.c index b76589ce..3d840a80 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -549,7 +549,7 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, else { new_item = cmdq_get_command(bd->cmdlist, fs, m, 0); if (bd->flags & KEY_BINDING_REPEAT) - new_item->shared->flags |= CMDQ_SHARED_REPEAT; + cmdq_get_shared(new_item)->flags |= CMDQ_SHARED_REPEAT; } if (item != NULL) new_item = cmdq_insert_after(item, new_item); diff --git a/menu.c b/menu.c index 7ca6253e..e38b5fa5 100644 --- a/menu.c +++ b/menu.c @@ -282,7 +282,7 @@ chosen: break; case CMD_PARSE_SUCCESS: if (md->item != NULL) - m = &md->item->shared->mouse; + m = &cmdq_get_shared(md->item)->mouse; else m = NULL; new_item = cmdq_get_command(pr->cmdlist, &md->fs, m, 0); diff --git a/notify.c b/notify.c index 772b3e1f..8ce5e803 100644 --- a/notify.c +++ b/notify.c @@ -189,17 +189,18 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c, void notify_hook(struct cmdq_item *item, const char *name) { - struct notify_entry ne; + struct cmd_find_state *target = cmdq_get_target(item); + struct notify_entry ne; memset(&ne, 0, sizeof ne); ne.name = name; - cmd_find_copy_state(&ne.fs, &item->target); + cmd_find_copy_state(&ne.fs, target); - ne.client = item->client; - ne.session = item->target.s; - ne.window = item->target.w; - ne.pane = item->target.wp->id; + ne.client = cmdq_get_client(item); + ne.session = target->s; + ne.window = target->w; + ne.pane = target->wp->id; notify_insert_hook(item, &ne); } diff --git a/popup.c b/popup.c index d23c2001..91246538 100644 --- a/popup.c +++ b/popup.c @@ -64,7 +64,7 @@ popup_write_screen(struct client *c, struct popup_data *pd) struct format_tree *ft; u_int i, y; - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(c, item, FORMAT_NONE, 0); if (cmd_find_valid_state(&pd->fs)) format_defaults(ft, c, pd->fs.s, pd->fs.wl, pd->fs.wp); else @@ -152,9 +152,9 @@ popup_free_cb(struct client *c) if (item != NULL) { if (pd->ictx != NULL && - item->client != NULL && - item->client->session == NULL) - item->client->retval = pd->status; + cmdq_get_client(item) != NULL && + cmdq_get_client(item)->session == NULL) + cmdq_get_client(item)->retval = pd->status; cmdq_continue(item); } server_client_unref(pd->c); @@ -305,7 +305,7 @@ popup_key_cb(struct client *c, struct key_event *event) break; case CMD_PARSE_SUCCESS: if (pd->item != NULL) - m = &pd->item->shared->mouse; + m = &cmdq_get_shared(pd->item)->mouse; else m = NULL; new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); @@ -382,7 +382,7 @@ popup_width(struct cmdq_item *item, u_int nlines, const char **lines, struct format_tree *ft; u_int i, width = 0, tmpwidth; - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); if (fs != NULL && cmd_find_valid_state(fs)) format_defaults(ft, c, fs->s, fs->wl, fs->wp); else diff --git a/server-client.c b/server-client.c index b6ce94a9..12a65af2 100644 --- a/server-client.c +++ b/server-client.c @@ -210,7 +210,7 @@ server_client_create(int fd) c->fd = -1; c->cwd = NULL; - TAILQ_INIT(&c->queue); + c->queue = cmdq_new(); c->tty.fd = -1; c->title = NULL; @@ -355,8 +355,7 @@ server_client_free(__unused int fd, __unused short events, void *arg) log_debug("free client %p (%d references)", c, c->references); - if (!TAILQ_EMPTY(&c->queue)) - fatalx("queue not empty"); + cmdq_free(c->queue); if (c->references == 0) { free((void *)c->name); @@ -1082,7 +1081,7 @@ server_client_update_latest(struct client *c) static enum cmd_retval server_client_key_callback(struct cmdq_item *item, void *data) { - struct client *c = item->client; + struct client *c = cmdq_get_client(item); struct key_event *event = data; key_code key = event->key; struct mouse_event *m = &event->m; @@ -1893,7 +1892,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) static enum cmd_retval server_client_command_done(struct cmdq_item *item, __unused void *data) { - struct client *c = item->client; + struct client *c = cmdq_get_client(item); if (~c->flags & CLIENT_ATTACHED) c->flags |= CLIENT_EXIT; diff --git a/spawn.c b/spawn.c index 865a77e2..49ab2a1e 100644 --- a/spawn.c +++ b/spawn.c @@ -56,10 +56,10 @@ spawn_log(const char *from, struct spawn_context *sc) struct session *s = sc->s; struct winlink *wl = sc->wl; struct window_pane *wp0 = sc->wp0; + const char *name = cmdq_get_name(sc->item); char tmp[128]; - const char *name; - log_debug("%s: %s, flags=%#x", from, sc->item->name, sc->flags); + log_debug("%s: %s, flags=%#x", from, name, sc->flags); if (wl != NULL && wp0 != NULL) xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id); @@ -70,18 +70,14 @@ spawn_log(const char *from, struct spawn_context *sc) else xsnprintf(tmp, sizeof tmp, "wl=none wp0=none"); log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx); - - name = sc->name; - if (name == NULL) - name = "none"; - log_debug("%s: name=%s", from, name); + log_debug("%s: name=%s", from, sc->name == NULL ? "none" : sc->name); } struct winlink * spawn_window(struct spawn_context *sc, char **cause) { struct cmdq_item *item = sc->item; - struct client *c = item->client; + struct client *c = cmdq_get_client(item); struct session *s = sc->s; struct window *w; struct window_pane *wp; @@ -207,7 +203,8 @@ struct window_pane * spawn_pane(struct spawn_context *sc, char **cause) { struct cmdq_item *item = sc->item; - struct client *c = item->client; + struct cmd_find_state *target = cmdq_get_target(item); + struct client *c = cmdq_get_client(item); struct session *s = sc->s; struct window *w = sc->wl->window; struct window_pane *new_wp; @@ -230,9 +227,9 @@ spawn_pane(struct spawn_context *sc, char **cause) * the pane's stored one unless specified. */ if (sc->cwd != NULL) - cwd = format_single(item, sc->cwd, c, item->target.s, NULL, NULL); + cwd = format_single(item, sc->cwd, c, target->s, NULL, NULL); else if (~sc->flags & SPAWN_RESPAWN) - cwd = xstrdup(server_client_get_cwd(c, item->target.s)); + cwd = xstrdup(server_client_get_cwd(c, target->s)); else cwd = NULL; diff --git a/tmux.h b/tmux.h index 539309da..68e111cb 100644 --- a/tmux.h +++ b/tmux.h @@ -1378,12 +1378,6 @@ struct cmd_parse_input { struct cmd_find_state fs; }; -/* Command queue item type. */ -enum cmdq_type { - CMDQ_COMMAND, - CMDQ_CALLBACK, -}; - /* Command queue item shared state. */ struct cmdq_shared { int references; @@ -1398,39 +1392,13 @@ struct cmdq_shared { struct cmd_find_state current; }; -/* Command queue item. */ -typedef enum cmd_retval (*cmdq_cb) (struct cmdq_item *, void *); -struct cmdq_item { - char *name; - struct cmdq_list *queue; - struct cmdq_item *next; - - struct client *client; - - enum cmdq_type type; - u_int group; - - u_int number; - time_t time; - - int flags; +/* Command queue flags. */ #define CMDQ_FIRED 0x1 #define CMDQ_WAITING 0x2 #define CMDQ_NOHOOKS 0x4 - struct cmdq_shared *shared; - struct cmd_find_state source; - struct cmd_find_state target; - - struct cmd_list *cmdlist; - struct cmd *cmd; - - cmdq_cb cb; - void *data; - - TAILQ_ENTRY(cmdq_item) entry; -}; -TAILQ_HEAD(cmdq_list, cmdq_item); +/* Command queue callback. */ +typedef enum cmd_retval (*cmdq_cb) (struct cmdq_item *, void *); /* Command definition flag. */ struct cmd_entry_flag { @@ -1513,7 +1481,7 @@ typedef void (*overlay_free_cb)(struct client *); struct client { const char *name; struct tmuxpeer *peer; - struct cmdq_list queue; + struct cmdq_list *queue; pid_t pid; int fd; @@ -1827,6 +1795,7 @@ int format_true(const char *); struct format_tree *format_create(struct client *, struct cmdq_item *, int, int); void format_free(struct format_tree *); +void format_merge(struct format_tree *, struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); void format_each(struct format_tree *, void (*)(const char *, @@ -1836,6 +1805,8 @@ char *format_expand(struct format_tree *, const char *); char *format_single(struct cmdq_item *, const char *, struct client *, struct session *, struct winlink *, struct window_pane *); +char *format_single_from_target(struct cmdq_item *, const char *, + struct client *); void format_defaults(struct format_tree *, struct client *, struct session *, struct winlink *, struct window_pane *); void format_defaults_window(struct format_tree *, struct window *); @@ -2135,6 +2106,14 @@ struct cmd_parse_result *cmd_parse_from_arguments(int, char **, struct cmd_parse_input *); /* cmd-queue.c */ +struct cmdq_list *cmdq_new(void); +void cmdq_free(struct cmdq_list *); +const char *cmdq_get_name(struct cmdq_item *); +struct client *cmdq_get_client(struct cmdq_item *); +struct cmd_find_state *cmdq_get_target(struct cmdq_item *); +struct cmd_find_state *cmdq_get_source(struct cmdq_item *); +struct cmdq_shared *cmdq_get_shared(struct cmdq_item *); +void cmdq_merge_formats(struct cmdq_item *, struct format_tree *); struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, struct mouse_event *, int); #define cmdq_get_callback(cb, data) cmdq_get_callback1(#cb, cb, data) diff --git a/window.c b/window.c index 38e31810..13fb3bec 100644 --- a/window.c +++ b/window.c @@ -1536,7 +1536,7 @@ int window_pane_start_input(struct window_pane *wp, struct cmdq_item *item, char **cause) { - struct client *c = item->client; + struct client *c = cmdq_get_client(item); struct window_pane_input_data *cdata; if (~wp->flags & PANE_EMPTY) { From 53d6b94e8aef09c0494ed8a53191063b605b3127 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 13:32:09 +0000 Subject: [PATCH 0151/1006] Move the NOHOOKS flag into the shared flags. --- cmd-queue.c | 14 ++++++++------ notify.c | 3 ++- tmux.h | 6 +----- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 28642721..aecb7734 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -25,6 +25,10 @@ #include "tmux.h" +/* Command queue flags. */ +#define CMDQ_FIRED 0x1 +#define CMDQ_WAITING 0x2 + /* Command queue item type. */ enum cmdq_type { CMDQ_COMMAND, @@ -224,7 +228,7 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, struct options_array_item *a; struct cmd_list *cmdlist; - if (item->flags & CMDQ_NOHOOKS) + if (item->shared->flags & CMDQ_SHARED_NOHOOKS) return; if (s == NULL) oo = global_s_options; @@ -250,7 +254,8 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, continue; } - new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS); + new_item = cmdq_get_command(cmdlist, fs, NULL, + CMDQ_SHARED_NOHOOKS); cmdq_format(new_item, "hook", "%s", name); if (item != NULL) item = cmdq_insert_after(item, new_item); @@ -330,6 +335,7 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, cmd_find_clear_state(&shared->current, 0); if (m != NULL) memcpy(&shared->mouse, m, sizeof shared->mouse); + shared->flags = flags; last_group = group; } entry = cmd_get_entry(cmd); @@ -337,9 +343,7 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, item = xcalloc(1, sizeof *item); xasprintf(&item->name, "[%s/%p]", entry->name, item); item->type = CMDQ_COMMAND; - item->group = group; - item->flags = flags; item->shared = shared; item->cmdlist = cmdlist; @@ -447,9 +451,7 @@ cmdq_get_callback1(const char *name, cmdq_cb cb, void *data) item = xcalloc(1, sizeof *item); xasprintf(&item->name, "[%s/%p]", name, item); item->type = CMDQ_CALLBACK; - item->group = 0; - item->flags = 0; item->cb = cb; item->data = data; diff --git a/notify.c b/notify.c index 8ce5e803..558bc92c 100644 --- a/notify.c +++ b/notify.c @@ -95,7 +95,8 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) continue; } - new_item = cmdq_get_command(cmdlist, &fs, NULL, CMDQ_NOHOOKS); + new_item = cmdq_get_command(cmdlist, &fs, NULL, + CMDQ_SHARED_NOHOOKS); cmdq_format(new_item, "hook", "%s", ne->name); notify_hook_formats(new_item, s, w, ne->pane); item = cmdq_insert_after(item, new_item); diff --git a/tmux.h b/tmux.h index 68e111cb..88cf16d8 100644 --- a/tmux.h +++ b/tmux.h @@ -1385,6 +1385,7 @@ struct cmdq_shared { int flags; #define CMDQ_SHARED_REPEAT 0x1 #define CMDQ_SHARED_CONTROL 0x2 +#define CMDQ_SHARED_NOHOOKS 0x4 struct format_tree *formats; @@ -1392,11 +1393,6 @@ struct cmdq_shared { struct cmd_find_state current; }; -/* Command queue flags. */ -#define CMDQ_FIRED 0x1 -#define CMDQ_WAITING 0x2 -#define CMDQ_NOHOOKS 0x4 - /* Command queue callback. */ typedef enum cmd_retval (*cmdq_cb) (struct cmdq_item *, void *); From 77d5b0cc538138fd036dca0f9b2ba198a94c009d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 13:42:35 +0000 Subject: [PATCH 0152/1006] Store a key event not a mouse event in the shared data. --- cmd-copy-mode.c | 4 ++-- cmd-display-menu.c | 20 +++++++++++--------- cmd-find.c | 2 +- cmd-if-shell.c | 2 +- cmd-queue.c | 7 +++++-- cmd-resize-pane.c | 4 ++-- cmd-send-keys.c | 2 +- format.c | 2 +- menu.c | 2 +- popup.c | 2 +- tmux.h | 2 +- 11 files changed, 27 insertions(+), 22 deletions(-) diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index d5e132f9..2fa6ae2e 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -70,7 +70,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'M')) { - if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL) + if ((wp = cmd_mouse_pane(&shared->event.m, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); @@ -87,7 +87,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) swp = wp; if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) { if (args_has(args, 'M')) - window_copy_start_drag(c, &shared->mouse); + window_copy_start_drag(c, &shared->event.m); } if (args_has(args, 'u')) window_copy_pageup(wp, 0); diff --git a/cmd-display-menu.c b/cmd-display-menu.c index ada3ef23..ff9276d3 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -99,9 +99,9 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, *px = wp->xoff - ox; else *px = 0; - } else if (strcmp(xp, "M") == 0 && shared->mouse.valid) { - if (shared->mouse.x > w / 2) - *px = shared->mouse.x - w / 2; + } else if (strcmp(xp, "M") == 0) { + if (shared->event.m.valid && shared->event.m.x > w / 2) + *px = shared->event.m.x - w / 2; else *px = 0; } else if (strcmp(xp, "W") == 0) { @@ -133,9 +133,12 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, *py = wp->yoff + wp->sy - oy; else *py = 0; - } else if (strcmp(yp, "M") == 0 && shared->mouse.valid) - *py = shared->mouse.y + h; - else if (strcmp(yp, "S") == 0) { + } else if (strcmp(yp, "M") == 0) { + if (shared->event.m.valid) + *py = shared->event.m.y + h; + else + *py = 0; + } else if (strcmp(yp, "S") == 0) { if (options_get_number(s->options, "status-position") == 0) { if (lines != 0) *py = lines + h; @@ -147,8 +150,7 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, else *py = c->tty.sy; } - } - else if (strcmp(yp, "W") == 0) { + } else if (strcmp(yp, "W") == 0) { if (options_get_number(s->options, "status-position") == 0) { if (lines != 0) *py = line + 1 + h; @@ -228,7 +230,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) cmd_display_menu_get_position(c, item, args, &px, &py, menu->width + 4, menu->count + 2); - if (!shared->mouse.valid) + if (!shared->event.m.valid) flags |= MENU_NOMOUSE; if (menu_display(menu, flags, item, px, py, c, target, NULL, NULL) != 0) return (CMD_RETURN_NORMAL); diff --git a/cmd-find.c b/cmd-find.c index a258a10f..665f8eb5 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -982,7 +982,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, /* Mouse target is a plain = or {mouse}. */ if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { - m = &cmdq_get_shared(item)->mouse; + m = &cmdq_get_shared(item)->event.m; switch (type) { case CMD_FIND_PANE: fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index a385f9f7..ee056520 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -65,7 +65,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmdq_shared *shared = cmdq_get_shared(item); struct cmd_find_state *target = cmdq_get_target(item); - struct mouse_event *m = &shared->mouse; + struct mouse_event *m = &shared->event.m; struct cmd_if_shell_data *cdata; char *shellcmd, *cmd; const char *file; diff --git a/cmd-queue.c b/cmd-queue.c index aecb7734..7e1788cc 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -333,8 +333,11 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, cmd_find_copy_state(&shared->current, current); else cmd_find_clear_state(&shared->current, 0); - if (m != NULL) - memcpy(&shared->mouse, m, sizeof shared->mouse); + if (m != NULL) { + shared->event.key = KEYC_NONE; + memcpy(&shared->event.m, m, + sizeof shared->event.m); + } shared->flags = flags; last_group = group; } diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 0579616a..cd68fd4a 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -76,12 +76,12 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'M')) { - if (cmd_mouse_window(&shared->mouse, &s) == NULL) + if (cmd_mouse_window(&shared->event.m, &s) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); c->tty.mouse_drag_update = cmd_resize_pane_mouse_update; - cmd_resize_pane_mouse_update(c, &shared->mouse); + cmd_resize_pane_mouse_update(c, &shared->event.m); return (CMD_RETURN_NORMAL); } diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 384cb460..7c73a995 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -140,7 +140,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *wp = target->wp; struct session *s = target->s; struct winlink *wl = target->wl; - struct mouse_event *m = &shared->mouse; + struct mouse_event *m = &shared->event.m; struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); int i; key_code key; diff --git a/format.c b/format.c index 3b361e00..e29c17af 100644 --- a/format.c +++ b/format.c @@ -1133,7 +1133,7 @@ format_create_add_item(struct format_tree *ft, struct cmdq_item *item) if (shared == NULL) return; - m = &shared->mouse; + m = &shared->event.m; if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) { format_add(ft, "mouse_pane", "%%%u", wp->id); if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) { diff --git a/menu.c b/menu.c index e38b5fa5..87b4782b 100644 --- a/menu.c +++ b/menu.c @@ -282,7 +282,7 @@ chosen: break; case CMD_PARSE_SUCCESS: if (md->item != NULL) - m = &cmdq_get_shared(md->item)->mouse; + m = &cmdq_get_shared(md->item)->event.m; else m = NULL; new_item = cmdq_get_command(pr->cmdlist, &md->fs, m, 0); diff --git a/popup.c b/popup.c index 91246538..66c71741 100644 --- a/popup.c +++ b/popup.c @@ -305,7 +305,7 @@ popup_key_cb(struct client *c, struct key_event *event) break; case CMD_PARSE_SUCCESS: if (pd->item != NULL) - m = &cmdq_get_shared(pd->item)->mouse; + m = &cmdq_get_shared(pd->item)->event.m; else m = NULL; new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); diff --git a/tmux.h b/tmux.h index 88cf16d8..fc10d880 100644 --- a/tmux.h +++ b/tmux.h @@ -1389,7 +1389,7 @@ struct cmdq_shared { struct format_tree *formats; - struct mouse_event mouse; + struct key_event event; struct cmd_find_state current; }; From 9a65102bfc2ed5d1e1f41e47451b8296c84f133d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 14:04:25 +0000 Subject: [PATCH 0153/1006] Rename cmdq_shared to cmdq_state which will better reflect what it is (going to be) used for. --- cmd-attach-session.c | 6 ++--- cmd-break-pane.c | 4 +-- cmd-copy-mode.c | 6 ++--- cmd-display-menu.c | 14 +++++----- cmd-find.c | 6 ++--- cmd-if-shell.c | 4 +-- cmd-join-pane.c | 4 +-- cmd-new-session.c | 6 ++--- cmd-new-window.c | 4 +-- cmd-queue.c | 62 ++++++++++++++++++++++---------------------- cmd-resize-pane.c | 6 ++--- cmd-rotate-window.c | 4 +-- cmd-select-pane.c | 4 +-- cmd-select-window.c | 4 +-- cmd-send-keys.c | 4 +-- cmd-split-window.c | 4 +-- cmd-switch-client.c | 6 ++--- control.c | 2 +- format.c | 6 ++--- key-bindings.c | 2 +- menu.c | 2 +- notify.c | 2 +- popup.c | 2 +- tmux.h | 12 ++++----- 24 files changed, 88 insertions(+), 88 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 4ffd3dd2..00a852fc 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -50,8 +50,8 @@ enum cmd_retval cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, int xflag, int rflag, const char *cflag, int Eflag) { - struct cmdq_shared *shared = cmdq_get_shared(item); - struct cmd_find_state *current = &shared->current; + struct cmdq_state *state = cmdq_get_state(item); + struct cmd_find_state *current = &state->current; struct cmd_find_state target; enum cmd_find_type type; int flags; @@ -120,7 +120,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, environ_update(s->options, c->environ, s->environ); c->session = s; - if (~shared->flags & CMDQ_SHARED_REPEAT) + if (~state->flags & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 6f82ee42..1d0ad2ce 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -49,8 +49,8 @@ static enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); - struct cmd_find_state *current = &shared->current; + struct cmdq_state *state = cmdq_get_state(item); + struct cmd_find_state *current = &state->current; struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *source = cmdq_get_source(item); struct client *c = cmd_find_client(item, NULL, 1); diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 2fa6ae2e..01006e5b 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -57,7 +57,7 @@ static enum cmd_retval cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *source = cmdq_get_source(item); struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmdq_get_client(item); @@ -70,7 +70,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'M')) { - if ((wp = cmd_mouse_pane(&shared->event.m, &s, NULL)) == NULL) + if ((wp = cmd_mouse_pane(&state->event.m, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); @@ -87,7 +87,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) swp = wp; if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) { if (args_has(args, 'M')) - window_copy_start_drag(c, &shared->event.m); + window_copy_start_drag(c, &state->event.m); } if (args_has(args, 'u')) window_copy_pageup(wp, 0); diff --git a/cmd-display-menu.c b/cmd-display-menu.c index ff9276d3..df8e0329 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -65,7 +65,7 @@ static void cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, struct args *args, u_int *px, u_int *py, u_int w, u_int h) { - struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *target = cmdq_get_target(item); struct session *s = c->session; struct winlink *wl = target->wl; @@ -100,8 +100,8 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, else *px = 0; } else if (strcmp(xp, "M") == 0) { - if (shared->event.m.valid && shared->event.m.x > w / 2) - *px = shared->event.m.x - w / 2; + if (state->event.m.valid && state->event.m.x > w / 2) + *px = state->event.m.x - w / 2; else *px = 0; } else if (strcmp(xp, "W") == 0) { @@ -134,8 +134,8 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, else *py = 0; } else if (strcmp(yp, "M") == 0) { - if (shared->event.m.valid) - *py = shared->event.m.y + h; + if (state->event.m.valid) + *py = state->event.m.y + h; else *py = 0; } else if (strcmp(yp, "S") == 0) { @@ -176,7 +176,7 @@ static enum cmd_retval cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *target = cmdq_get_target(item); struct client *c; struct menu *menu = NULL; @@ -230,7 +230,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) cmd_display_menu_get_position(c, item, args, &px, &py, menu->width + 4, menu->count + 2); - if (!shared->event.m.valid) + if (!state->event.m.valid) flags |= MENU_NOMOUSE; if (menu_display(menu, flags, item, px, py, c, target, NULL, NULL) != 0) return (CMD_RETURN_NORMAL); diff --git a/cmd-find.c b/cmd-find.c index 665f8eb5..a063f115 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -961,8 +961,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { fs->current = &marked_pane; log_debug("%s: current is marked pane", __func__); - } else if (cmd_find_valid_state(&cmdq_get_shared(item)->current)) { - fs->current = &cmdq_get_shared(item)->current; + } else if (cmd_find_valid_state(&cmdq_get_state(item)->current)) { + fs->current = &cmdq_get_state(item)->current; log_debug("%s: current is from queue", __func__); } else if (cmd_find_from_client(¤t, cmdq_get_client(item), flags) == 0) { @@ -982,7 +982,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, /* Mouse target is a plain = or {mouse}. */ if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { - m = &cmdq_get_shared(item)->event.m; + m = &cmdq_get_state(item)->event.m; switch (type) { case CMD_FIND_PANE: fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index ee056520..4210392e 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -63,9 +63,9 @@ static enum cmd_retval cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *target = cmdq_get_target(item); - struct mouse_event *m = &shared->event.m; + struct mouse_event *m = &state->event.m; struct cmd_if_shell_data *cdata; char *shellcmd, *cmd; const char *file; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index ab0fea62..99d7f366 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -64,8 +64,8 @@ static enum cmd_retval cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); - struct cmd_find_state *current = &shared->current; + struct cmdq_state *state = cmdq_get_state(item); + struct cmd_find_state *current = &state->current; struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *source = cmdq_get_source(item); struct session *dst_s; diff --git a/cmd-new-session.c b/cmd-new-session.c index 8ce428c9..551cd74c 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -67,8 +67,8 @@ static enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); - struct cmd_find_state *current = &shared->current; + struct cmdq_state *state = cmdq_get_state(item); + struct cmd_find_state *current = &state->current; struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmdq_get_client(item); struct session *s, *as, *groupwith; @@ -317,7 +317,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) } else if (c->session != NULL) c->last_session = c->session; c->session = s; - if (~shared->flags & CMDQ_SHARED_REPEAT) + if (~state->flags & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); diff --git a/cmd-new-window.c b/cmd-new-window.c index fcd480c6..f2a7fbc2 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -52,8 +52,8 @@ static enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); - struct cmd_find_state *current = &shared->current; + struct cmdq_state *state = cmdq_get_state(item); + struct cmd_find_state *current = &state->current; struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); diff --git a/cmd-queue.c b/cmd-queue.c index 7e1788cc..12ad8c2d 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -51,7 +51,7 @@ struct cmdq_item { int flags; - struct cmdq_shared *shared; + struct cmdq_state *state; struct cmd_find_state source; struct cmd_find_state target; @@ -142,11 +142,11 @@ cmdq_get_source(struct cmdq_item *item) return (&item->source); } -/* Get item shared. */ -struct cmdq_shared * -cmdq_get_shared(struct cmdq_item *item) +/* Get item state. */ +struct cmdq_state * +cmdq_get_state(struct cmdq_item *item) { - return (item->shared); + return (item->state); } /* Merge formats from item. */ @@ -159,8 +159,8 @@ cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft) entry = cmd_get_entry (item->cmd); format_add(ft, "command", "%s", entry->name); } - if (item->shared->formats != NULL) - format_merge(ft, item->shared->formats); + if (item->state->formats != NULL) + format_merge(ft, item->state->formats); } /* Append an item. */ @@ -228,7 +228,7 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, struct options_array_item *a; struct cmd_list *cmdlist; - if (item->shared->flags & CMDQ_SHARED_NOHOOKS) + if (item->state->flags & CMDQ_STATE_NOHOOKS) return; if (s == NULL) oo = global_s_options; @@ -255,7 +255,7 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, } new_item = cmdq_get_command(cmdlist, fs, NULL, - CMDQ_SHARED_NOHOOKS); + CMDQ_STATE_NOHOOKS); cmdq_format(new_item, "hook", "%s", name); if (item != NULL) item = cmdq_insert_after(item, new_item); @@ -279,10 +279,10 @@ cmdq_continue(struct cmdq_item *item) static void cmdq_remove(struct cmdq_item *item) { - if (item->shared != NULL && --item->shared->references == 0) { - if (item->shared->formats != NULL) - format_free(item->shared->formats); - free(item->shared); + if (item->state != NULL && --item->state->references == 0) { + if (item->state->formats != NULL) + format_free(item->state->formats); + free(item->state); } if (item->client != NULL) @@ -322,23 +322,23 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, struct cmdq_item *item, *first = NULL, *last = NULL; struct cmd *cmd; const struct cmd_entry *entry; - struct cmdq_shared *shared = NULL; + struct cmdq_state *state = NULL; u_int group, last_group = 0; cmd = cmd_list_first(cmdlist, &group); while (cmd != NULL) { if (group != last_group) { - shared = xcalloc(1, sizeof *shared); + state = xcalloc(1, sizeof *state); if (current != NULL) - cmd_find_copy_state(&shared->current, current); + cmd_find_copy_state(&state->current, current); else - cmd_find_clear_state(&shared->current, 0); + cmd_find_clear_state(&state->current, 0); if (m != NULL) { - shared->event.key = KEYC_NONE; - memcpy(&shared->event.m, m, - sizeof shared->event.m); + state->event.key = KEYC_NONE; + memcpy(&state->event.m, m, + sizeof state->event.m); } - shared->flags = flags; + state->flags = flags; last_group = group; } entry = cmd_get_entry(cmd); @@ -348,13 +348,13 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, item->type = CMDQ_COMMAND; item->group = group; - item->shared = shared; + item->state = state; item->cmdlist = cmdlist; item->cmd = cmd; log_debug("%s: %s group %u", __func__, item->name, item->group); - shared->references++; + state->references++; cmdlist->references++; if (first == NULL) @@ -394,7 +394,7 @@ cmdq_fire_command(struct cmdq_item *item) { struct client *c = item->client; const char *name = cmdq_name(c); - struct cmdq_shared *shared = item->shared; + struct cmdq_state *state = item->state; struct cmd *cmd = item->cmd; const struct cmd_entry *entry = cmd_get_entry(cmd); enum cmd_retval retval; @@ -408,7 +408,7 @@ cmdq_fire_command(struct cmdq_item *item) free(tmp); } - flags = !!(shared->flags & CMDQ_SHARED_CONTROL); + flags = !!(state->flags & CMDQ_STATE_CONTROL); cmdq_guard(item, "begin", flags); if (item->client == NULL) @@ -427,8 +427,8 @@ cmdq_fire_command(struct cmdq_item *item) if (entry->flags & CMD_AFTERHOOK) { if (cmd_find_valid_state(&item->target)) fsp = &item->target; - else if (cmd_find_valid_state(&item->shared->current)) - fsp = &item->shared->current; + else if (cmd_find_valid_state(&item->state->current)) + fsp = &item->state->current; else if (cmd_find_from_client(&fs, item->client, 0) == 0) fsp = &fs; else @@ -492,7 +492,7 @@ cmdq_fire_callback(struct cmdq_item *item) void cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...) { - struct cmdq_shared *shared = item->shared; + struct cmdq_state *state = item->state; va_list ap; char *value; @@ -500,9 +500,9 @@ cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...) xvasprintf(&value, fmt, ap); va_end(ap); - if (shared->formats == NULL) - shared->formats = format_create(NULL, NULL, FORMAT_NONE, 0); - format_add(shared->formats, key, "%s", value); + if (state->formats == NULL) + state->formats = format_create(NULL, NULL, FORMAT_NONE, 0); + format_add(state->formats, key, "%s", value); free(value); } diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index cd68fd4a..e8c2a5bc 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -50,7 +50,7 @@ static enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *target = cmdq_get_target(item); struct window_pane *wp = target->wp; struct winlink *wl = target->wl; @@ -76,12 +76,12 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'M')) { - if (cmd_mouse_window(&shared->event.m, &s) == NULL) + if (cmd_mouse_window(&state->event.m, &s) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); c->tty.mouse_drag_update = cmd_resize_pane_mouse_update; - cmd_resize_pane_mouse_update(c, &shared->event.m); + cmd_resize_pane_mouse_update(c, &state->event.m); return (CMD_RETURN_NORMAL); } diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 8d47688e..6ba2056e 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -44,8 +44,8 @@ static enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); - struct cmd_find_state *current = &shared->current; + struct cmdq_state *state = cmdq_get_state(item); + struct cmd_find_state *current = &state->current; struct cmd_find_state *target = cmdq_get_target(item); struct winlink *wl = target->wl; struct window *w = wl->window; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index b3dd28ba..e0907812 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -85,8 +85,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); const struct cmd_entry *entry = cmd_get_entry(self); - struct cmdq_shared *shared = cmdq_get_shared(item); - struct cmd_find_state *current = &shared->current; + struct cmdq_state *state = cmdq_get_state(item); + struct cmd_find_state *current = &state->current; struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmd_find_client(item, NULL, 1); struct winlink *wl = target->wl; diff --git a/cmd-select-window.c b/cmd-select-window.c index 18011ce0..0a84bf0e 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -85,8 +85,8 @@ static enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); - struct cmd_find_state *current = &shared->current; + struct cmdq_state *state = cmdq_get_state(item); + struct cmd_find_state *current = &state->current; struct cmd_find_state *target = cmdq_get_target(item); struct winlink *wl = target->wl; struct session *s = target->s; diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 7c73a995..1f70db16 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -134,13 +134,13 @@ static enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmd_find_client(item, NULL, 1); struct window_pane *wp = target->wp; struct session *s = target->s; struct winlink *wl = target->wl; - struct mouse_event *m = &shared->event.m; + struct mouse_event *m = &state->event.m; struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); int i; key_code key; diff --git a/cmd-split-window.c b/cmd-split-window.c index 9ec4b09e..01d7cbc8 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -54,8 +54,8 @@ static enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); - struct cmd_find_state *current = &shared->current; + struct cmdq_state *state = cmdq_get_state(item); + struct cmd_find_state *current = &state->current; struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index c33b74a1..de85122f 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -48,8 +48,8 @@ static enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_shared *shared = cmdq_get_shared(item); - struct cmd_find_state *current = &shared->current; + struct cmdq_state *state = cmdq_get_state(item); + struct cmd_find_state *current = &state->current; struct cmd_find_state target; const char *tflag = args_get(args, 't'); enum cmd_find_type type; @@ -137,7 +137,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; - if (~shared->flags & CMDQ_SHARED_REPEAT) + if (~state->flags & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); diff --git a/control.c b/control.c index fe9d28c6..088371f5 100644 --- a/control.c +++ b/control.c @@ -86,7 +86,7 @@ control_callback(__unused struct client *c, __unused const char *path, break; case CMD_PARSE_SUCCESS: item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); - cmdq_get_shared(item)->flags |= CMDQ_SHARED_CONTROL; + cmdq_get_state(item)->flags |= CMDQ_STATE_CONTROL; cmdq_append(c, item); cmd_list_free(pr->cmdlist); break; diff --git a/format.c b/format.c index e29c17af..9274e353 100644 --- a/format.c +++ b/format.c @@ -1124,16 +1124,16 @@ format_merge(struct format_tree *ft, struct format_tree *from) static void format_create_add_item(struct format_tree *ft, struct cmdq_item *item) { - struct cmdq_shared *shared = cmdq_get_shared(item); + struct cmdq_state *state = cmdq_get_state(item); struct mouse_event *m; struct window_pane *wp; u_int x, y; cmdq_merge_formats(item, ft); - if (shared == NULL) + if (state == NULL) return; - m = &shared->event.m; + m = &state->event.m; if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) { format_add(ft, "mouse_pane", "%%%u", wp->id); if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) { diff --git a/key-bindings.c b/key-bindings.c index 3d840a80..04a5cef7 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -549,7 +549,7 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, else { new_item = cmdq_get_command(bd->cmdlist, fs, m, 0); if (bd->flags & KEY_BINDING_REPEAT) - cmdq_get_shared(new_item)->flags |= CMDQ_SHARED_REPEAT; + cmdq_get_state(new_item)->flags |= CMDQ_STATE_REPEAT; } if (item != NULL) new_item = cmdq_insert_after(item, new_item); diff --git a/menu.c b/menu.c index 87b4782b..6c571473 100644 --- a/menu.c +++ b/menu.c @@ -282,7 +282,7 @@ chosen: break; case CMD_PARSE_SUCCESS: if (md->item != NULL) - m = &cmdq_get_shared(md->item)->event.m; + m = &cmdq_get_state(md->item)->event.m; else m = NULL; new_item = cmdq_get_command(pr->cmdlist, &md->fs, m, 0); diff --git a/notify.c b/notify.c index 558bc92c..1f48fb6e 100644 --- a/notify.c +++ b/notify.c @@ -96,7 +96,7 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) } new_item = cmdq_get_command(cmdlist, &fs, NULL, - CMDQ_SHARED_NOHOOKS); + CMDQ_STATE_NOHOOKS); cmdq_format(new_item, "hook", "%s", ne->name); notify_hook_formats(new_item, s, w, ne->pane); item = cmdq_insert_after(item, new_item); diff --git a/popup.c b/popup.c index 66c71741..8359397c 100644 --- a/popup.c +++ b/popup.c @@ -305,7 +305,7 @@ popup_key_cb(struct client *c, struct key_event *event) break; case CMD_PARSE_SUCCESS: if (pd->item != NULL) - m = &cmdq_get_shared(pd->item)->event.m; + m = &cmdq_get_state(pd->item)->event.m; else m = NULL; new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); diff --git a/tmux.h b/tmux.h index fc10d880..dbd046c4 100644 --- a/tmux.h +++ b/tmux.h @@ -1378,14 +1378,14 @@ struct cmd_parse_input { struct cmd_find_state fs; }; -/* Command queue item shared state. */ -struct cmdq_shared { +/* Command queue item state. */ +struct cmdq_state { int references; int flags; -#define CMDQ_SHARED_REPEAT 0x1 -#define CMDQ_SHARED_CONTROL 0x2 -#define CMDQ_SHARED_NOHOOKS 0x4 +#define CMDQ_STATE_REPEAT 0x1 +#define CMDQ_STATE_CONTROL 0x2 +#define CMDQ_STATE_NOHOOKS 0x4 struct format_tree *formats; @@ -2108,7 +2108,7 @@ const char *cmdq_get_name(struct cmdq_item *); struct client *cmdq_get_client(struct cmdq_item *); struct cmd_find_state *cmdq_get_target(struct cmdq_item *); struct cmd_find_state *cmdq_get_source(struct cmdq_item *); -struct cmdq_shared *cmdq_get_shared(struct cmdq_item *); +struct cmdq_state *cmdq_get_state(struct cmdq_item *); void cmdq_merge_formats(struct cmdq_item *, struct format_tree *); struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, struct mouse_event *, int); From adb76fd1ce8753a958d4ffe14db724f9f4d674ea Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 14:46:04 +0000 Subject: [PATCH 0154/1006] Move cmdq_state into cmd-queue.c. --- cmd-attach-session.c | 5 ++--- cmd-break-pane.c | 3 +-- cmd-copy-mode.c | 6 +++--- cmd-display-menu.c | 14 ++++++------- cmd-find.c | 6 +++--- cmd-if-shell.c | 14 ++++++------- cmd-join-pane.c | 3 +-- cmd-new-session.c | 5 ++--- cmd-new-window.c | 3 +-- cmd-queue.c | 48 +++++++++++++++++++++++++++++++++++--------- cmd-resize-pane.c | 6 +++--- cmd-rotate-window.c | 3 +-- cmd-select-pane.c | 3 +-- cmd-select-window.c | 3 +-- cmd-send-keys.c | 4 ++-- cmd-split-window.c | 3 +-- cmd-switch-client.c | 5 ++--- control.c | 4 ++-- format.c | 7 ++----- key-bindings.c | 8 ++++---- menu.c | 6 +++--- popup.c | 6 +++--- server-client.c | 2 +- tmux.h | 21 +++++++------------ 24 files changed, 99 insertions(+), 89 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 00a852fc..8c30c767 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -50,8 +50,7 @@ enum cmd_retval cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, int xflag, int rflag, const char *cflag, int Eflag) { - struct cmdq_state *state = cmdq_get_state(item); - struct cmd_find_state *current = &state->current; + struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state target; enum cmd_find_type type; int flags; @@ -120,7 +119,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, environ_update(s->options, c->environ, s->environ); c->session = s; - if (~state->flags & CMDQ_STATE_REPEAT) + if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 1d0ad2ce..e12b09b6 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -49,8 +49,7 @@ static enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); - struct cmd_find_state *current = &state->current; + struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *source = cmdq_get_source(item); struct client *c = cmd_find_client(item, NULL, 1); diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 01006e5b..d8b4fd3e 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -57,7 +57,7 @@ static enum cmd_retval cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); + struct key_event *event = cmdq_get_event(item); struct cmd_find_state *source = cmdq_get_source(item); struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmdq_get_client(item); @@ -70,7 +70,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'M')) { - if ((wp = cmd_mouse_pane(&state->event.m, &s, NULL)) == NULL) + if ((wp = cmd_mouse_pane(&event->m, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); @@ -87,7 +87,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) swp = wp; if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) { if (args_has(args, 'M')) - window_copy_start_drag(c, &state->event.m); + window_copy_start_drag(c, &event->m); } if (args_has(args, 'u')) window_copy_pageup(wp, 0); diff --git a/cmd-display-menu.c b/cmd-display-menu.c index df8e0329..830491dc 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -65,8 +65,8 @@ static void cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, struct args *args, u_int *px, u_int *py, u_int w, u_int h) { - struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *target = cmdq_get_target(item); + struct key_event *event = cmdq_get_event(item); struct session *s = c->session; struct winlink *wl = target->wl; struct window_pane *wp = target->wp; @@ -100,8 +100,8 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, else *px = 0; } else if (strcmp(xp, "M") == 0) { - if (state->event.m.valid && state->event.m.x > w / 2) - *px = state->event.m.x - w / 2; + if (event->m.valid && event->m.x > w / 2) + *px = event->m.x - w / 2; else *px = 0; } else if (strcmp(xp, "W") == 0) { @@ -134,8 +134,8 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, else *py = 0; } else if (strcmp(yp, "M") == 0) { - if (state->event.m.valid) - *py = state->event.m.y + h; + if (event->m.valid) + *py = event->m.y + h; else *py = 0; } else if (strcmp(yp, "S") == 0) { @@ -176,8 +176,8 @@ static enum cmd_retval cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *target = cmdq_get_target(item); + struct key_event *event = cmdq_get_event(item); struct client *c; struct menu *menu = NULL; struct menu_item menu_item; @@ -230,7 +230,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) cmd_display_menu_get_position(c, item, args, &px, &py, menu->width + 4, menu->count + 2); - if (!state->event.m.valid) + if (!event->m.valid) flags |= MENU_NOMOUSE; if (menu_display(menu, flags, item, px, py, c, target, NULL, NULL) != 0) return (CMD_RETURN_NORMAL); diff --git a/cmd-find.c b/cmd-find.c index a063f115..9c8bcbc1 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -961,8 +961,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { fs->current = &marked_pane; log_debug("%s: current is marked pane", __func__); - } else if (cmd_find_valid_state(&cmdq_get_state(item)->current)) { - fs->current = &cmdq_get_state(item)->current; + } else if (cmd_find_valid_state(cmdq_get_current(item))) { + fs->current = cmdq_get_current(item); log_debug("%s: current is from queue", __func__); } else if (cmd_find_from_client(¤t, cmdq_get_client(item), flags) == 0) { @@ -982,7 +982,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, /* Mouse target is a plain = or {mouse}. */ if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { - m = &cmdq_get_state(item)->event.m; + m = &cmdq_get_event(item)->m; switch (type) { case CMD_FIND_PANE: fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 4210392e..3657c730 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -56,16 +56,15 @@ struct cmd_if_shell_data { struct client *client; struct cmdq_item *item; - struct mouse_event mouse; + struct key_event event; }; static enum cmd_retval cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *target = cmdq_get_target(item); - struct mouse_event *m = &state->event.m; + struct key_event *event = cmdq_get_event(item); struct cmd_if_shell_data *cdata; char *shellcmd, *cmd; const char *file; @@ -102,7 +101,8 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) free(pr->error); return (CMD_RETURN_ERROR); case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, target, m, 0); + new_item = cmdq_get_command(pr->cmdlist, target, event, + 0); cmdq_insert_after(item, new_item); cmd_list_free(pr->cmdlist); break; @@ -117,7 +117,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->cmd_else = xstrdup(args->argv[2]); else cdata->cmd_else = NULL; - memcpy(&cdata->mouse, m, sizeof cdata->mouse); + memcpy(&cdata->event, event, sizeof cdata->event); if (!args_has(args, 'b')) cdata->client = cmdq_get_client(item); @@ -161,7 +161,7 @@ cmd_if_shell_callback(struct job *job) { struct cmd_if_shell_data *cdata = job_get_data(job); struct client *c = cdata->client; - struct mouse_event *m = &cdata->mouse; + struct key_event *event = &cdata->event; struct cmdq_item *new_item = NULL; char *cmd; int status; @@ -185,7 +185,7 @@ cmd_if_shell_callback(struct job *job) free(pr->error); break; case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0); + new_item = cmdq_get_command(pr->cmdlist, NULL, event, 0); cmd_list_free(pr->cmdlist); break; } diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 99d7f366..45a56fc4 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -64,8 +64,7 @@ static enum cmd_retval cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); - struct cmd_find_state *current = &state->current; + struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *source = cmdq_get_source(item); struct session *dst_s; diff --git a/cmd-new-session.c b/cmd-new-session.c index 551cd74c..5286d320 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -67,8 +67,7 @@ static enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); - struct cmd_find_state *current = &state->current; + struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmdq_get_client(item); struct session *s, *as, *groupwith; @@ -317,7 +316,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) } else if (c->session != NULL) c->last_session = c->session; c->session = s; - if (~state->flags & CMDQ_STATE_REPEAT) + if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); diff --git a/cmd-new-window.c b/cmd-new-window.c index f2a7fbc2..87aae659 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -52,8 +52,7 @@ static enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); - struct cmd_find_state *current = &state->current; + struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); diff --git a/cmd-queue.c b/cmd-queue.c index 12ad8c2d..40db3fef 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -65,6 +65,23 @@ struct cmdq_item { }; TAILQ_HEAD(cmdq_list, cmdq_item); +/* + * Command queue state. This is the context for commands on the command queue. + * It holds information about how the commands were fired (the key and flags), + * any additional formats for the commands, and the current default target. + * Multiple commands can share the same state and a command may update the + * default target. + */ +struct cmdq_state { + int references; + int flags; + + struct format_tree *formats; + + struct key_event event; + struct cmd_find_state current; +}; + /* Get command queue name. */ static const char * cmdq_name(struct client *c) @@ -142,11 +159,25 @@ cmdq_get_source(struct cmdq_item *item) return (&item->source); } -/* Get item state. */ -struct cmdq_state * -cmdq_get_state(struct cmdq_item *item) +/* Get state event. */ +struct key_event * +cmdq_get_event(struct cmdq_item *item) { - return (item->state); + return (&item->state->event); +} + +/* Get state current target. */ +struct cmd_find_state * +cmdq_get_current(struct cmdq_item *item) +{ + return (&item->state->current); +} + +/* Get state flags. */ +int +cmdq_get_flags(struct cmdq_item *item) +{ + return (item->state->flags); } /* Merge formats from item. */ @@ -317,7 +348,7 @@ cmdq_remove_group(struct cmdq_item *item) /* Get a command for the command queue. */ struct cmdq_item * cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, - struct mouse_event *m, int flags) + struct key_event *event, int flags) { struct cmdq_item *item, *first = NULL, *last = NULL; struct cmd *cmd; @@ -333,10 +364,9 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, cmd_find_copy_state(&state->current, current); else cmd_find_clear_state(&state->current, 0); - if (m != NULL) { - state->event.key = KEYC_NONE; - memcpy(&state->event.m, m, - sizeof state->event.m); + if (event != NULL) { + memcpy(&state->event, event, + sizeof state->event); } state->flags = flags; last_group = group; diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index e8c2a5bc..105f48e0 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -50,8 +50,8 @@ static enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *target = cmdq_get_target(item); + struct key_event *event = cmdq_get_event(item); struct window_pane *wp = target->wp; struct winlink *wl = target->wl; struct window *w = wl->window; @@ -76,12 +76,12 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'M')) { - if (cmd_mouse_window(&state->event.m, &s) == NULL) + if (!event->m.valid || cmd_mouse_window(&event->m, &s) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); c->tty.mouse_drag_update = cmd_resize_pane_mouse_update; - cmd_resize_pane_mouse_update(c, &state->event.m); + cmd_resize_pane_mouse_update(c, &event->m); return (CMD_RETURN_NORMAL); } diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 6ba2056e..55c1dde2 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -44,8 +44,7 @@ static enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); - struct cmd_find_state *current = &state->current; + struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct winlink *wl = target->wl; struct window *w = wl->window; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index e0907812..5f4a2e63 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -85,8 +85,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); const struct cmd_entry *entry = cmd_get_entry(self); - struct cmdq_state *state = cmdq_get_state(item); - struct cmd_find_state *current = &state->current; + struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmd_find_client(item, NULL, 1); struct winlink *wl = target->wl; diff --git a/cmd-select-window.c b/cmd-select-window.c index 0a84bf0e..377e3633 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -85,8 +85,7 @@ static enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); - struct cmd_find_state *current = &state->current; + struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct winlink *wl = target->wl; struct session *s = target->s; diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 1f70db16..de910904 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -134,13 +134,13 @@ static enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmd_find_client(item, NULL, 1); struct window_pane *wp = target->wp; struct session *s = target->s; struct winlink *wl = target->wl; - struct mouse_event *m = &state->event.m; + struct key_event *event = cmdq_get_event(item); + struct mouse_event *m = &event->m; struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); int i; key_code key; diff --git a/cmd-split-window.c b/cmd-split-window.c index 01d7cbc8..84419a1e 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -54,8 +54,7 @@ static enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); - struct cmd_find_state *current = &state->current; + struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index de85122f..bc6a8174 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -48,8 +48,7 @@ static enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct cmdq_state *state = cmdq_get_state(item); - struct cmd_find_state *current = &state->current; + struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state target; const char *tflag = args_get(args, 't'); enum cmd_find_type type; @@ -137,7 +136,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; - if (~state->flags & CMDQ_STATE_REPEAT) + if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); diff --git a/control.c b/control.c index 088371f5..d3515005 100644 --- a/control.c +++ b/control.c @@ -85,8 +85,8 @@ control_callback(__unused struct client *c, __unused const char *path, cmdq_append(c, item); break; case CMD_PARSE_SUCCESS: - item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); - cmdq_get_state(item)->flags |= CMDQ_STATE_CONTROL; + item = cmdq_get_command(pr->cmdlist, NULL, NULL, + CMDQ_STATE_CONTROL); cmdq_append(c, item); cmd_list_free(pr->cmdlist); break; diff --git a/format.c b/format.c index 9274e353..08fa38df 100644 --- a/format.c +++ b/format.c @@ -1124,16 +1124,13 @@ format_merge(struct format_tree *ft, struct format_tree *from) static void format_create_add_item(struct format_tree *ft, struct cmdq_item *item) { - struct cmdq_state *state = cmdq_get_state(item); - struct mouse_event *m; + struct key_event *event = cmdq_get_event(item); + struct mouse_event *m = &event->m; struct window_pane *wp; u_int x, y; cmdq_merge_formats(item, ft); - if (state == NULL) - return; - m = &state->event.m; if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) { format_add(ft, "mouse_pane", "%%%u", wp->id); if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) { diff --git a/key-bindings.c b/key-bindings.c index 04a5cef7..13caa889 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -535,10 +535,10 @@ key_bindings_read_only(struct cmdq_item *item, __unused void *data) struct cmdq_item * key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, - struct client *c, struct mouse_event *m, struct cmd_find_state *fs) + struct client *c, struct key_event *event, struct cmd_find_state *fs) { struct cmdq_item *new_item; - int readonly; + int readonly, flags = 0; if (c == NULL || (~c->flags & CLIENT_READONLY)) readonly = 1; @@ -547,9 +547,9 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, if (!readonly) new_item = cmdq_get_callback(key_bindings_read_only, NULL); else { - new_item = cmdq_get_command(bd->cmdlist, fs, m, 0); if (bd->flags & KEY_BINDING_REPEAT) - cmdq_get_state(new_item)->flags |= CMDQ_STATE_REPEAT; + flags |= CMDQ_STATE_REPEAT; + new_item = cmdq_get_command(bd->cmdlist, fs, event, flags); } if (item != NULL) new_item = cmdq_insert_after(item, new_item); diff --git a/menu.c b/menu.c index 6c571473..c72be5ce 100644 --- a/menu.c +++ b/menu.c @@ -282,10 +282,10 @@ chosen: break; case CMD_PARSE_SUCCESS: if (md->item != NULL) - m = &cmdq_get_state(md->item)->event.m; + event = cmdq_get_event(md->item); else - m = NULL; - new_item = cmdq_get_command(pr->cmdlist, &md->fs, m, 0); + event = NULL; + new_item = cmdq_get_command(pr->cmdlist, &md->fs, event, 0); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; diff --git a/popup.c b/popup.c index 8359397c..c1530347 100644 --- a/popup.c +++ b/popup.c @@ -305,10 +305,10 @@ popup_key_cb(struct client *c, struct key_event *event) break; case CMD_PARSE_SUCCESS: if (pd->item != NULL) - m = &cmdq_get_state(pd->item)->event.m; + event = cmdq_get_event(pd->item); else - m = NULL; - new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); + event = NULL; + new_item = cmdq_get_command(pr->cmdlist, fs, event, 0); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; diff --git a/server-client.c b/server-client.c index 12a65af2..9575fd4d 100644 --- a/server-client.c +++ b/server-client.c @@ -1223,7 +1223,7 @@ try_again: server_status_client(c); /* Execute the key binding. */ - key_bindings_dispatch(bd, item, c, m, &fs); + key_bindings_dispatch(bd, item, c, event, &fs); key_bindings_unref_table(table); goto out; } diff --git a/tmux.h b/tmux.h index dbd046c4..e5ecab9b 100644 --- a/tmux.h +++ b/tmux.h @@ -43,6 +43,7 @@ struct cmd; struct cmd_find_state; struct cmdq_item; struct cmdq_list; +struct cmdq_state; struct cmds; struct environ; struct format_job_tree; @@ -1378,21 +1379,11 @@ struct cmd_parse_input { struct cmd_find_state fs; }; -/* Command queue item state. */ -struct cmdq_state { - int references; - - int flags; +/* Command queue flags. */ #define CMDQ_STATE_REPEAT 0x1 #define CMDQ_STATE_CONTROL 0x2 #define CMDQ_STATE_NOHOOKS 0x4 - struct format_tree *formats; - - struct key_event event; - struct cmd_find_state current; -}; - /* Command queue callback. */ typedef enum cmd_retval (*cmdq_cb) (struct cmdq_item *, void *); @@ -2108,10 +2099,12 @@ const char *cmdq_get_name(struct cmdq_item *); struct client *cmdq_get_client(struct cmdq_item *); struct cmd_find_state *cmdq_get_target(struct cmdq_item *); struct cmd_find_state *cmdq_get_source(struct cmdq_item *); -struct cmdq_state *cmdq_get_state(struct cmdq_item *); +struct key_event *cmdq_get_event(struct cmdq_item *); +struct cmd_find_state *cmdq_get_current(struct cmdq_item *); +int cmdq_get_flags(struct cmdq_item *); void cmdq_merge_formats(struct cmdq_item *, struct format_tree *); struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, - struct mouse_event *, int); + struct key_event *, int); #define cmdq_get_callback(cb, data) cmdq_get_callback1(#cb, cb, data) struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *); struct cmdq_item *cmdq_get_error(const char *); @@ -2147,7 +2140,7 @@ void key_bindings_remove(const char *, key_code); void key_bindings_remove_table(const char *); void key_bindings_init(void); struct cmdq_item *key_bindings_dispatch(struct key_binding *, - struct cmdq_item *, struct client *, struct mouse_event *, + struct cmdq_item *, struct client *, struct key_event *, struct cmd_find_state *); /* key-string.c */ From 3f86d6d46014ca55e42cecd570d7f269b1d386b3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 15:55:51 +0000 Subject: [PATCH 0155/1006] When adding a list of commands to the queue, instead of automatically creating a new state for each group of commands, require the caller to create one and use it for all the commands in the list. This means the current target works even with list with multiple groups (which can happen if they are defined with newlines). --- cfg.c | 4 +- cmd-command-prompt.c | 2 +- cmd-confirm-before.c | 2 +- cmd-display-panes.c | 2 +- cmd-if-shell.c | 17 +++-- cmd-new-session.c | 4 +- cmd-queue.c | 170 +++++++++++++++++++++++++++---------------- cmd.c | 23 +++--- control.c | 6 +- key-bindings.c | 7 +- menu.c | 5 +- mode-tree.c | 5 +- notify.c | 34 ++++----- popup.c | 5 +- server-client.c | 2 +- tmux.h | 20 +++-- 16 files changed, 187 insertions(+), 121 deletions(-) diff --git a/cfg.c b/cfg.c index 6740bce4..f67a765c 100644 --- a/cfg.c +++ b/cfg.c @@ -183,7 +183,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, return (0); } - new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); + new_item0 = cmdq_get_command(pr->cmdlist, NULL); if (item != NULL) new_item0 = cmdq_insert_after(item, new_item0); else @@ -229,7 +229,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path, return (0); } - new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); + new_item0 = cmdq_get_command(pr->cmdlist, NULL); if (item != NULL) new_item0 = cmdq_insert_after(item, new_item0); else diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 31a1ae78..81f392f5 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -177,7 +177,7 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, cmdq_append(c, new_item); break; case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); + new_item = cmdq_get_command(pr->cmdlist, NULL); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 419efda5..7850706e 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -109,7 +109,7 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, cmdq_append(c, new_item); break; case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); + new_item = cmdq_get_command(pr->cmdlist, NULL); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 41fe50da..011e3d13 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -225,7 +225,7 @@ cmd_display_panes_key(struct client *c, struct key_event *event) cmdq_append(c, new_item); break; case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); + new_item = cmdq_get_command(pr->cmdlist, NULL); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 3657c730..ab1b588d 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -56,7 +56,6 @@ struct cmd_if_shell_data { struct client *client; struct cmdq_item *item; - struct key_event event; }; static enum cmd_retval @@ -64,7 +63,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct key_event *event = cmdq_get_event(item); + struct cmdq_state *state = cmdq_get_state(item); struct cmd_if_shell_data *cdata; char *shellcmd, *cmd; const char *file; @@ -101,8 +100,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) free(pr->error); return (CMD_RETURN_ERROR); case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, target, event, - 0); + new_item = cmdq_get_command(pr->cmdlist, state); cmdq_insert_after(item, new_item); cmd_list_free(pr->cmdlist); break; @@ -117,7 +115,6 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->cmd_else = xstrdup(args->argv[2]); else cdata->cmd_else = NULL; - memcpy(&cdata->event, event, sizeof cdata->event); if (!args_has(args, 'b')) cdata->client = cmdq_get_client(item); @@ -161,8 +158,8 @@ cmd_if_shell_callback(struct job *job) { struct cmd_if_shell_data *cdata = job_get_data(job); struct client *c = cdata->client; - struct key_event *event = &cdata->event; struct cmdq_item *new_item = NULL; + struct cmdq_state *new_state = NULL; char *cmd; int status; struct cmd_parse_result *pr; @@ -185,7 +182,13 @@ cmd_if_shell_callback(struct job *job) free(pr->error); break; case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL, event, 0); + if (cdata->item == NULL) + new_state = cmdq_new_state(NULL, NULL, 0); + else + new_state = cmdq_get_state(cdata->item); + new_item = cmdq_get_command(pr->cmdlist, new_state); + if (cdata->item == NULL) + cmdq_free_state(new_state); cmd_list_free(pr->cmdlist); break; } diff --git a/cmd-new-session.c b/cmd-new-session.c index 5286d320..574cd5cd 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -344,10 +344,10 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) free(cp); } - if (!detached) { + if (!detached) c->flags |= CLIENT_ATTACHED; + if (!args_has(args, 'd')) cmd_find_from_session(current, s, 0); - } cmd_find_from_session(&fs, s, 0); cmdq_insert_hook(s, item, &fs, "after-new-session"); diff --git a/cmd-queue.c b/cmd-queue.c index 40db3fef..97d19f81 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -145,6 +145,13 @@ cmdq_get_client(struct cmdq_item *item) return (item->client); } +/* Get item state. */ +struct cmdq_state * +cmdq_get_state(struct cmdq_item *item) +{ + return (item->state); +} + /* Get item target. */ struct cmd_find_state * cmdq_get_target(struct cmdq_item *item) @@ -180,6 +187,70 @@ cmdq_get_flags(struct cmdq_item *item) return (item->state->flags); } +/* Create a new state. */ +struct cmdq_state * +cmdq_new_state(struct cmd_find_state *current, struct key_event *event, + int flags) +{ + struct cmdq_state *state; + + state = xcalloc(1, sizeof *state); + state->references = 1; + state->flags = flags; + + if (event != NULL) + memcpy(&state->event, event, sizeof state->event); + else + state->event.key = KEYC_NONE; + if (current != NULL && cmd_find_valid_state(current)) + cmd_find_copy_state(&state->current, current); + else + cmd_find_clear_state(&state->current, 0); + + return (state); +} + +/* Add a reference to a state. */ +struct cmdq_state * +cmdq_link_state(struct cmdq_state *state) +{ + state->references++; + return (state); +} + +/* Make a copy of a state. */ +struct cmdq_state * +cmdq_copy_state(struct cmdq_state *state) +{ + return (cmdq_new_state(&state->current, &state->event, state->flags)); +} + +/* Free a state. */ +void +cmdq_free_state(struct cmdq_state *state) +{ + if (--state->references == 0) + free(state); +} + +/* Add a format to command queue. */ +void +cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...) +{ + va_list ap; + char *value; + + va_start(ap, fmt); + xvasprintf(&value, fmt, ap); + va_end(ap); + + if (state->formats == NULL) + state->formats = format_create(NULL, NULL, FORMAT_NONE, 0); + format_add(state->formats, key, "%s", value); + + free(value); +} + /* Merge formats from item. */ void cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft) @@ -249,12 +320,14 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) /* Insert a hook. */ void cmdq_insert_hook(struct session *s, struct cmdq_item *item, - struct cmd_find_state *fs, const char *fmt, ...) + struct cmd_find_state *current, const char *fmt, ...) { + struct cmdq_state *state = item->state; struct options *oo; va_list ap; char *name; struct cmdq_item *new_item; + struct cmdq_state *new_state; struct options_entry *o; struct options_array_item *a; struct cmd_list *cmdlist; @@ -277,25 +350,27 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, } log_debug("running hook %s (parent %p)", name, item); + /* + * The hooks get a new state because they should not update the current + * target or formats for any subsequent commands. + */ + new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS); + cmdq_add_format(new_state, "hook", "%s", name); + a = options_array_first(o); while (a != NULL) { cmdlist = options_array_item_value(a)->cmdlist; - if (cmdlist == NULL) { - a = options_array_next(a); - continue; + if (cmdlist != NULL) { + new_item = cmdq_get_command(cmdlist, new_state); + if (item != NULL) + item = cmdq_insert_after(item, new_item); + else + item = cmdq_append(NULL, new_item); } - - new_item = cmdq_get_command(cmdlist, fs, NULL, - CMDQ_STATE_NOHOOKS); - cmdq_format(new_item, "hook", "%s", name); - if (item != NULL) - item = cmdq_insert_after(item, new_item); - else - item = cmdq_append(NULL, new_item); - a = options_array_next(a); } + cmdq_free_state(new_state); free(name); } @@ -310,17 +385,11 @@ cmdq_continue(struct cmdq_item *item) static void cmdq_remove(struct cmdq_item *item) { - if (item->state != NULL && --item->state->references == 0) { - if (item->state->formats != NULL) - format_free(item->state->formats); - free(item->state); - } - if (item->client != NULL) server_client_unref(item->client); - if (item->cmdlist != NULL) cmd_list_free(item->cmdlist); + cmdq_free_state(item->state); TAILQ_REMOVE(item->queue, item, entry); @@ -347,45 +416,34 @@ cmdq_remove_group(struct cmdq_item *item) /* Get a command for the command queue. */ struct cmdq_item * -cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, - struct key_event *event, int flags) +cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state) { struct cmdq_item *item, *first = NULL, *last = NULL; struct cmd *cmd; const struct cmd_entry *entry; - struct cmdq_state *state = NULL; - u_int group, last_group = 0; + int created = 0; - cmd = cmd_list_first(cmdlist, &group); + if (state == NULL) { + state = cmdq_new_state(NULL, NULL, 0); + created = 1; + } + + cmd = cmd_list_first(cmdlist); while (cmd != NULL) { - if (group != last_group) { - state = xcalloc(1, sizeof *state); - if (current != NULL) - cmd_find_copy_state(&state->current, current); - else - cmd_find_clear_state(&state->current, 0); - if (event != NULL) { - memcpy(&state->event, event, - sizeof state->event); - } - state->flags = flags; - last_group = group; - } entry = cmd_get_entry(cmd); item = xcalloc(1, sizeof *item); xasprintf(&item->name, "[%s/%p]", entry->name, item); item->type = CMDQ_COMMAND; - item->group = group; - item->state = state; + item->group = cmd_get_group(cmd); + item->state = cmdq_link_state(state); + item->cmdlist = cmdlist; item->cmd = cmd; - log_debug("%s: %s group %u", __func__, item->name, item->group); - - state->references++; cmdlist->references++; + log_debug("%s: %s group %u", __func__, item->name, item->group); if (first == NULL) first = item; @@ -393,8 +451,11 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, last->next = item; last = item; - cmd = cmd_list_next(cmd, &group); + cmd = cmd_list_next(cmd); } + + if (created) + cmdq_free_state(state); return (first); } @@ -484,7 +545,9 @@ cmdq_get_callback1(const char *name, cmdq_cb cb, void *data) item = xcalloc(1, sizeof *item); xasprintf(&item->name, "[%s/%p]", name, item); item->type = CMDQ_CALLBACK; + item->group = 0; + item->state = cmdq_new_state(NULL, NULL, 0); item->cb = cb; item->data = data; @@ -518,25 +581,6 @@ cmdq_fire_callback(struct cmdq_item *item) return (item->cb(item, item->data)); } -/* Add a format to command queue. */ -void -cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...) -{ - struct cmdq_state *state = item->state; - va_list ap; - char *value; - - va_start(ap, fmt); - xvasprintf(&value, fmt, ap); - va_end(ap); - - if (state->formats == NULL) - state->formats = format_create(NULL, NULL, FORMAT_NONE, 0); - format_add(state->formats, key, "%s", value); - - free(value); -} - /* Process next item on command queue. */ u_int cmdq_next(struct client *c) diff --git a/cmd.c b/cmd.c index d0a8c267..b7b144b4 100644 --- a/cmd.c +++ b/cmd.c @@ -392,6 +392,13 @@ cmd_get_args(struct cmd *cmd) return (cmd->args); } +/* Get group for command. */ +u_int +cmd_get_group(struct cmd *cmd) +{ + return (cmd->group); +} + /* Get file and line for command. */ void cmd_get_source(struct cmd *cmd, const char **file, u_int *line) @@ -646,24 +653,16 @@ cmd_list_print(struct cmd_list *cmdlist, int escaped) /* Get first command in list. */ struct cmd * -cmd_list_first(struct cmd_list *cmdlist, u_int *group) +cmd_list_first(struct cmd_list *cmdlist) { - struct cmd *cmd; - - cmd = TAILQ_FIRST(cmdlist->list); - if (cmd != NULL && group != NULL) - *group = cmd->group; - return (cmd); + return (TAILQ_FIRST(cmdlist->list)); } /* Get next command in list. */ struct cmd * -cmd_list_next(struct cmd *cmd, u_int *group) +cmd_list_next(struct cmd *cmd) { - cmd = TAILQ_NEXT(cmd, qentry); - if (cmd != NULL && group != NULL) - *group = cmd->group; - return (cmd); + return (TAILQ_NEXT(cmd, qentry)); } /* Do all of the commands in this command list have this flag? */ diff --git a/control.c b/control.c index d3515005..ddbbda8e 100644 --- a/control.c +++ b/control.c @@ -60,6 +60,7 @@ control_callback(__unused struct client *c, __unused const char *path, { char *line; struct cmdq_item *item; + struct cmdq_state *state; struct cmd_parse_result *pr; if (closed || error != 0) @@ -85,9 +86,10 @@ control_callback(__unused struct client *c, __unused const char *path, cmdq_append(c, item); break; case CMD_PARSE_SUCCESS: - item = cmdq_get_command(pr->cmdlist, NULL, NULL, - CMDQ_STATE_CONTROL); + state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL); + item = cmdq_get_command(pr->cmdlist, state); cmdq_append(c, item); + cmdq_free_state(state); cmd_list_free(pr->cmdlist); break; } diff --git a/key-bindings.c b/key-bindings.c index 13caa889..09f0e0b1 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -521,7 +521,7 @@ key_bindings_init(void) pr = cmd_parse_from_string(defaults[i], NULL); if (pr->status != CMD_PARSE_SUCCESS) fatalx("bad default key: %s", defaults[i]); - cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL, NULL, 0)); + cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL)); cmd_list_free(pr->cmdlist); } } @@ -538,6 +538,7 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, struct client *c, struct key_event *event, struct cmd_find_state *fs) { struct cmdq_item *new_item; + struct cmdq_state *new_state; int readonly, flags = 0; if (c == NULL || (~c->flags & CLIENT_READONLY)) @@ -549,7 +550,9 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, else { if (bd->flags & KEY_BINDING_REPEAT) flags |= CMDQ_STATE_REPEAT; - new_item = cmdq_get_command(bd->cmdlist, fs, event, flags); + new_state = cmdq_new_state(fs, event, flags); + new_item = cmdq_get_command(bd->cmdlist, new_state); + cmdq_free_state(new_state); } if (item != NULL) new_item = cmdq_insert_after(item, new_item); diff --git a/menu.c b/menu.c index c72be5ce..b76869bc 100644 --- a/menu.c +++ b/menu.c @@ -185,6 +185,7 @@ menu_key_cb(struct client *c, struct key_event *event) int count = menu->count, old = md->choice; const struct menu_item *item; struct cmdq_item *new_item; + struct cmdq_state *new_state; struct cmd_parse_result *pr; const char *name; @@ -285,7 +286,9 @@ chosen: event = cmdq_get_event(md->item); else event = NULL; - new_item = cmdq_get_command(pr->cmdlist, &md->fs, event, 0); + new_state = cmdq_new_state(&md->fs, event, 0); + new_item = cmdq_get_command(pr->cmdlist, new_state); + cmdq_free_state(new_state); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; diff --git a/mode-tree.c b/mode-tree.c index 843a74bc..d9af2ee3 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1064,6 +1064,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs, const char *template, const char *name) { struct cmdq_item *new_item; + struct cmdq_state *new_state; char *command; struct cmd_parse_result *pr; @@ -1085,7 +1086,9 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs, free(pr->error); break; case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, fs, NULL, 0); + new_state = cmdq_new_state(fs, NULL, 0); + new_item = cmdq_get_command(pr->cmdlist, new_state); + cmdq_free_state(new_state); cmdq_append(c, new_item); cmd_list_free(pr->cmdlist); break; diff --git a/notify.c b/notify.c index 1f48fb6e..3147c4e0 100644 --- a/notify.c +++ b/notify.c @@ -36,19 +36,19 @@ struct notify_entry { }; static void -notify_hook_formats(struct cmdq_item *item, struct session *s, struct window *w, - int pane) +notify_hook_formats(struct cmdq_state *state, struct session *s, + struct window *w, int pane) { if (s != NULL) { - cmdq_format(item, "hook_session", "$%u", s->id); - cmdq_format(item, "hook_session_name", "%s", s->name); + cmdq_add_format(state, "hook_session", "$%u", s->id); + cmdq_add_format(state, "hook_session_name", "%s", s->name); } if (w != NULL) { - cmdq_format(item, "hook_window", "@%u", w->id); - cmdq_format(item, "hook_window_name", "%s", w->name); + cmdq_add_format(state, "hook_window", "@%u", w->id); + cmdq_add_format(state, "hook_window_name", "%s", w->name); } if (pane != -1) - cmdq_format(item, "hook_pane", "%%%d", pane); + cmdq_add_format(state, "hook_pane", "%%%d", pane); } static void @@ -57,6 +57,7 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) struct cmd_find_state fs; struct options *oo; struct cmdq_item *new_item; + struct cmdq_state *new_state; struct session *s = ne->session; struct window *w = ne->window; struct options_entry *o; @@ -87,22 +88,21 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) if (o == NULL) return; + new_state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); + cmdq_add_format(new_state, "hook", "%s", ne->name); + notify_hook_formats(new_state, s, w, ne->pane); + a = options_array_first(o); while (a != NULL) { cmdlist = options_array_item_value(a)->cmdlist; - if (cmdlist == NULL) { - a = options_array_next(a); - continue; + if (cmdlist != NULL) { + new_item = cmdq_get_command(cmdlist, new_state); + item = cmdq_insert_after(item, new_item); } - - new_item = cmdq_get_command(cmdlist, &fs, NULL, - CMDQ_STATE_NOHOOKS); - cmdq_format(new_item, "hook", "%s", ne->name); - notify_hook_formats(new_item, s, w, ne->pane); - item = cmdq_insert_after(item, new_item); - a = options_array_next(a); } + + cmdq_free_state(new_state); } static enum cmd_retval diff --git a/popup.c b/popup.c index c1530347..c2db145c 100644 --- a/popup.c +++ b/popup.c @@ -223,6 +223,7 @@ popup_key_cb(struct client *c, struct key_event *event) struct mouse_event *m = &event->m; struct cmd_find_state *fs = &pd->fs; struct cmdq_item *new_item; + struct cmdq_state *new_state; struct cmd_parse_result *pr; struct format_tree *ft; const char *cmd, *buf; @@ -308,7 +309,9 @@ popup_key_cb(struct client *c, struct key_event *event) event = cmdq_get_event(pd->item); else event = NULL; - new_item = cmdq_get_command(pr->cmdlist, fs, event, 0); + new_state = cmdq_new_state(&pd->fs, event, 0); + new_item = cmdq_get_command(pr->cmdlist, new_state); + cmdq_free_state(new_state); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; diff --git a/server-client.c b/server-client.c index 9575fd4d..8042da9a 100644 --- a/server-client.c +++ b/server-client.c @@ -1949,7 +1949,7 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) } cmd_free_argv(argc, argv); - cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL, NULL, 0)); + cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL)); cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL)); cmd_list_free(pr->cmdlist); diff --git a/tmux.h b/tmux.h index e5ecab9b..74b16c89 100644 --- a/tmux.h +++ b/tmux.h @@ -2058,6 +2058,7 @@ char *cmd_stringify_argv(int, char **); char *cmd_get_alias(const char *); const struct cmd_entry *cmd_get_entry(struct cmd *); struct args *cmd_get_args(struct cmd *); +u_int cmd_get_group(struct cmd *); void cmd_get_source(struct cmd *, const char **, u_int *); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); void cmd_free(struct cmd *); @@ -2067,8 +2068,8 @@ void cmd_list_append(struct cmd_list *, struct cmd *); void cmd_list_move(struct cmd_list *, struct cmd_list *); void cmd_list_free(struct cmd_list *); char *cmd_list_print(struct cmd_list *, int); -struct cmd *cmd_list_first(struct cmd_list *, u_int *); -struct cmd *cmd_list_next(struct cmd *, u_int *); +struct cmd *cmd_list_first(struct cmd_list *); +struct cmd *cmd_list_next(struct cmd *); int cmd_list_all_have(struct cmd_list *, int); int cmd_list_any_have(struct cmd_list *, int); int cmd_mouse_at(struct window_pane *, struct mouse_event *, @@ -2093,18 +2094,25 @@ struct cmd_parse_result *cmd_parse_from_arguments(int, char **, struct cmd_parse_input *); /* cmd-queue.c */ +struct cmdq_state *cmdq_new_state(struct cmd_find_state *, struct key_event *, + int); +struct cmdq_state *cmdq_link_state(struct cmdq_state *); +struct cmdq_state *cmdq_copy_state(struct cmdq_state *); +void cmdq_free_state(struct cmdq_state *); +void printflike(3, 4) cmdq_add_format(struct cmdq_state *, const char *, + const char *, ...); +void cmdq_merge_formats(struct cmdq_item *, struct format_tree *); struct cmdq_list *cmdq_new(void); void cmdq_free(struct cmdq_list *); const char *cmdq_get_name(struct cmdq_item *); struct client *cmdq_get_client(struct cmdq_item *); +struct cmdq_state *cmdq_get_state(struct cmdq_item *); struct cmd_find_state *cmdq_get_target(struct cmdq_item *); struct cmd_find_state *cmdq_get_source(struct cmdq_item *); struct key_event *cmdq_get_event(struct cmdq_item *); struct cmd_find_state *cmdq_get_current(struct cmdq_item *); int cmdq_get_flags(struct cmdq_item *); -void cmdq_merge_formats(struct cmdq_item *, struct format_tree *); -struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, - struct key_event *, int); +struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmdq_state *); #define cmdq_get_callback(cb, data) cmdq_get_callback1(#cb, cb, data) struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *); struct cmdq_item *cmdq_get_error(const char *); @@ -2113,8 +2121,6 @@ struct cmdq_item *cmdq_append(struct client *, struct cmdq_item *); void cmdq_insert_hook(struct session *, struct cmdq_item *, struct cmd_find_state *, const char *, ...); void cmdq_continue(struct cmdq_item *); -void printflike(3, 4) cmdq_format(struct cmdq_item *, const char *, - const char *, ...); u_int cmdq_next(struct client *); void cmdq_guard(struct cmdq_item *, const char *, int); void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...); From 34804f2709a16dca45dc072fb53d03f79db61e51 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 16:19:37 +0000 Subject: [PATCH 0156/1006] When parsing strings, put all commands in one group even if there are newlines. This means that for example bind q { a \n b } and bind q "a ; b" are the same. Also log commands in different groups separated by ;; rather than ; (a command list like this should never be user visible). --- cmd-parse.y | 21 ++++++++++++++++++--- cmd.c | 23 ++++++++++++++++------- tmux.h | 1 + 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index aabfe2d3..2ca0124a 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -700,15 +700,17 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, /* * Parse each command into a command list. Create a new command list - * for each line so they get a new group (so the queue knows which ones - * to remove if a command fails when executed). + * for each line (unless the flag is set) so they get a new group (so + * the queue knows which ones to remove if a command fails when + * executed). */ result = cmd_list_new(); TAILQ_FOREACH(cmd, cmds, entry) { log_debug("%s: %u %s", __func__, cmd->line, cmd->name); cmd_log_argv(cmd->argc, cmd->argv, __func__); - if (cmdlist == NULL || cmd->line != line) { + if (cmdlist == NULL || + ((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) { if (cmdlist != NULL) { cmd_parse_print_commands(pi, line, cmdlist); cmd_list_move(result, cmdlist); @@ -775,6 +777,19 @@ cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi) struct cmd_parse_result * cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) { + struct cmd_parse_input input; + + if (pi == NULL) { + memset(&input, 0, sizeof input); + pi = &input; + } + + /* + * When parsing a string, put commands in one group even if there are + * multiple lines. This means { a \n b } is identical to "a ; b" when + * given as an argument to another command. + */ + pi->flags |= CMD_PARSE_ONEGROUP; return (cmd_parse_from_buffer(s, strlen(s), pi)); } diff --git a/cmd.c b/cmd.c index b7b144b4..bc807cbe 100644 --- a/cmd.c +++ b/cmd.c @@ -624,7 +624,7 @@ cmd_list_free(struct cmd_list *cmdlist) char * cmd_list_print(struct cmd_list *cmdlist, int escaped) { - struct cmd *cmd; + struct cmd *cmd, *next; char *buf, *this; size_t len; @@ -634,15 +634,24 @@ cmd_list_print(struct cmd_list *cmdlist, int escaped) TAILQ_FOREACH(cmd, cmdlist->list, qentry) { this = cmd_print(cmd); - len += strlen(this) + 4; + len += strlen(this) + 6; buf = xrealloc(buf, len); strlcat(buf, this, len); - if (TAILQ_NEXT(cmd, qentry) != NULL) { - if (escaped) - strlcat(buf, " \\; ", len); - else - strlcat(buf, " ; ", len); + + next = TAILQ_NEXT(cmd, qentry); + if (next != NULL) { + if (cmd->group != next->group) { + if (escaped) + strlcat(buf, " \\;\\; ", len); + else + strlcat(buf, " ;; ", len); + } else { + if (escaped) + strlcat(buf, " \\; ", len); + else + strlcat(buf, " ; ", len); + } } free(this); diff --git a/tmux.h b/tmux.h index 74b16c89..c1b91f93 100644 --- a/tmux.h +++ b/tmux.h @@ -1370,6 +1370,7 @@ struct cmd_parse_input { #define CMD_PARSE_PARSEONLY 0x2 #define CMD_PARSE_NOALIAS 0x4 #define CMD_PARSE_VERBOSE 0x8 +#define CMD_PARSE_ONEGROUP 0x10 const char *file; u_int line; From 187277eaadc4a675659bf7ede88f50bfe6cc7be9 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 18:59:41 +0000 Subject: [PATCH 0157/1006] Add helpers for the simple case of parse string and add to command queue. --- cmd-command-prompt.c | 24 +++++--------------- cmd-confirm-before.c | 23 +++++--------------- cmd-display-panes.c | 24 +++++--------------- cmd-if-shell.c | 21 +++++------------- cmd-parse.y | 52 ++++++++++++++++++++++++++++++++++++++++++++ control.c | 30 ++++++++----------------- menu.c | 40 ++++++++++++++-------------------- mode-tree.c | 40 +++++++++++----------------------- popup.c | 38 +++++++++++++------------------- tmux.h | 6 +++++ 10 files changed, 135 insertions(+), 163 deletions(-) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 81f392f5..510600e3 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -136,10 +136,9 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, int done) { struct cmd_command_prompt_cdata *cdata = data; - struct cmdq_item *new_item; - char *new_template, *prompt, *ptr; + char *new_template, *prompt, *ptr, *error; char *input = NULL; - struct cmd_parse_result *pr; + enum cmd_parse_status status; if (s == NULL) return (0); @@ -166,21 +165,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, return (1); } - pr = cmd_parse_from_string(new_template, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - new_item = NULL; - break; - case CMD_PARSE_ERROR: - new_item = cmdq_get_error(pr->error); - free(pr->error); - cmdq_append(c, new_item); - break; - case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL); - cmd_list_free(pr->cmdlist); - cmdq_append(c, new_item); - break; + status = cmd_parse_and_append(new_template, NULL, c, NULL, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_append(c, cmdq_get_error(error)); + free(error); } if (!done) diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 7850706e..026d0e11 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -87,8 +87,8 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, __unused int done) { struct cmd_confirm_before_data *cdata = data; - struct cmdq_item *new_item; - struct cmd_parse_result *pr; + char *error; + enum cmd_parse_status status; if (c->flags & CLIENT_DEAD) return (0); @@ -98,21 +98,10 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, if (tolower((u_char)s[0]) != 'y' || s[1] != '\0') return (0); - pr = cmd_parse_from_string(cdata->cmd, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - new_item = NULL; - break; - case CMD_PARSE_ERROR: - new_item = cmdq_get_error(pr->error); - free(pr->error); - cmdq_append(c, new_item); - break; - case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL); - cmd_list_free(pr->cmdlist); - cmdq_append(c, new_item); - break; + status = cmd_parse_and_append(cdata->cmd, NULL, c, NULL, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_append(c, cmdq_get_error(error)); + free(error); } return (0); diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 011e3d13..7e1a9e3d 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -197,11 +197,10 @@ static int cmd_display_panes_key(struct client *c, struct key_event *event) { struct cmd_display_panes_data *cdata = c->overlay_data; - struct cmdq_item *new_item; - char *cmd, *expanded; + char *cmd, *expanded, *error; struct window *w = c->session->curw->window; struct window_pane *wp; - struct cmd_parse_result *pr; + enum cmd_parse_status status; if (event->key < '0' || event->key > '9') return (-1); @@ -214,21 +213,10 @@ cmd_display_panes_key(struct client *c, struct key_event *event) xasprintf(&expanded, "%%%u", wp->id); cmd = cmd_template_replace(cdata->command, expanded, 1); - pr = cmd_parse_from_string(cmd, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - new_item = NULL; - break; - case CMD_PARSE_ERROR: - new_item = cmdq_get_error(pr->error); - free(pr->error); - cmdq_append(c, new_item); - break; - case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL); - cmd_list_free(pr->cmdlist); - cmdq_append(c, new_item); - break; + status = cmd_parse_and_append(cmd, NULL, c, NULL, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_append(c, cmdq_get_error(error)); + free(error); } free(cmd); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index ab1b588d..75f462ad 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -65,13 +65,12 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *target = cmdq_get_target(item); struct cmdq_state *state = cmdq_get_state(item); struct cmd_if_shell_data *cdata; - char *shellcmd, *cmd; + char *shellcmd, *cmd, *error; const char *file; - struct cmdq_item *new_item; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = target->s; struct cmd_parse_input pi; - struct cmd_parse_result *pr; + enum cmd_parse_status status; shellcmd = format_single_from_target(item, args->argv[0], c); if (args_has(args, 'F')) { @@ -91,19 +90,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) pi.c = c; cmd_find_copy_state(&pi.fs, target); - pr = cmd_parse_from_string(cmd, &pi); - switch (pr->status) { - case CMD_PARSE_EMPTY: - break; - case CMD_PARSE_ERROR: - cmdq_error(item, "%s", pr->error); - free(pr->error); + status = cmd_parse_and_insert(cmd, &pi, item, state, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_error(item, "%s", error); + free(error); return (CMD_RETURN_ERROR); - case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, state); - cmdq_insert_after(item, new_item); - cmd_list_free(pr->cmdlist); - break; } return (CMD_RETURN_NORMAL); } diff --git a/cmd-parse.y b/cmd-parse.y index 2ca0124a..891f2289 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -793,6 +793,58 @@ cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) return (cmd_parse_from_buffer(s, strlen(s), pi)); } +enum cmd_parse_status +cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi, + struct cmdq_item *after, struct cmdq_state *state, char **error) +{ + struct cmd_parse_result *pr; + struct cmdq_item *item; + + pr = cmd_parse_from_string(s, pi); + switch (pr->status) { + case CMD_PARSE_EMPTY: + break; + case CMD_PARSE_ERROR: + if (error != NULL) + *error = pr->error; + else + free(pr->error); + break; + case CMD_PARSE_SUCCESS: + item = cmdq_get_command(pr->cmdlist, state); + cmdq_insert_after(after, item); + cmd_list_free(pr->cmdlist); + break; + } + return (pr->status); +} + +enum cmd_parse_status +cmd_parse_and_append(const char *s, struct cmd_parse_input *pi, + struct client *c, struct cmdq_state *state, char **error) +{ + struct cmd_parse_result *pr; + struct cmdq_item *item; + + pr = cmd_parse_from_string(s, pi); + switch (pr->status) { + case CMD_PARSE_EMPTY: + break; + case CMD_PARSE_ERROR: + if (error != NULL) + *error = pr->error; + else + free(pr->error); + break; + case CMD_PARSE_SUCCESS: + item = cmdq_get_command(pr->cmdlist, state); + cmdq_append(c, item); + cmd_list_free(pr->cmdlist); + break; + } + return (pr->status); +} + struct cmd_parse_result * cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi) { diff --git a/control.c b/control.c index ddbbda8e..bdc89de4 100644 --- a/control.c +++ b/control.c @@ -56,14 +56,13 @@ control_error(struct cmdq_item *item, void *data) /* Control input callback. Read lines and fire commands. */ static void control_callback(__unused struct client *c, __unused const char *path, - int error, int closed, struct evbuffer *buffer, __unused void *data) + int read_error, int closed, struct evbuffer *buffer, __unused void *data) { - char *line; - struct cmdq_item *item; + char *line, *error; struct cmdq_state *state; - struct cmd_parse_result *pr; + enum cmd_parse_status status; - if (closed || error != 0) + if (closed || read_error != 0) c->flags |= CLIENT_EXIT; for (;;) { @@ -77,22 +76,11 @@ control_callback(__unused struct client *c, __unused const char *path, break; } - pr = cmd_parse_from_string(line, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - break; - case CMD_PARSE_ERROR: - item = cmdq_get_callback(control_error, pr->error); - cmdq_append(c, item); - break; - case CMD_PARSE_SUCCESS: - state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL); - item = cmdq_get_command(pr->cmdlist, state); - cmdq_append(c, item); - cmdq_free_state(state); - cmd_list_free(pr->cmdlist); - break; - } + state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL); + status = cmd_parse_and_append(line, NULL, c, state, &error); + if (status == CMD_PARSE_ERROR) + cmdq_append(c, cmdq_get_callback(control_error, error)); + cmdq_free_state(state); free(line); } diff --git a/menu.c b/menu.c index b76869bc..78369218 100644 --- a/menu.c +++ b/menu.c @@ -183,11 +183,11 @@ 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 struct menu_item *item; - struct cmdq_item *new_item; - struct cmdq_state *new_state; - struct cmd_parse_result *pr; const char *name; + const struct menu_item *item; + struct cmdq_state *state; + enum cmd_parse_status status; + char *error; if (KEYC_IS_MOUSE(event->key)) { if (md->flags & MENU_NOMOUSE) { @@ -272,27 +272,19 @@ chosen: return (1); } - pr = cmd_parse_from_string(item->command, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - break; - case CMD_PARSE_ERROR: - new_item = cmdq_get_error(pr->error); - free(pr->error); - cmdq_append(c, new_item); - break; - case CMD_PARSE_SUCCESS: - if (md->item != NULL) - event = cmdq_get_event(md->item); - else - event = NULL; - new_state = cmdq_new_state(&md->fs, event, 0); - new_item = cmdq_get_command(pr->cmdlist, new_state); - cmdq_free_state(new_state); - cmd_list_free(pr->cmdlist); - cmdq_append(c, new_item); - break; + if (md->item != NULL) + event = cmdq_get_event(md->item); + else + event = NULL; + state = cmdq_new_state(&md->fs, event, 0); + + status = cmd_parse_and_append(item->command, NULL, c, state, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_append(c, cmdq_get_error(error)); + free(error); } + cmdq_free_state(state); + return (1); } diff --git a/mode-tree.c b/mode-tree.c index d9af2ee3..0177d618 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1063,36 +1063,22 @@ void mode_tree_run_command(struct client *c, struct cmd_find_state *fs, const char *template, const char *name) { - struct cmdq_item *new_item; - struct cmdq_state *new_state; - char *command; - struct cmd_parse_result *pr; + struct cmdq_state *state; + char *command, *error; + enum cmd_parse_status status; command = cmd_template_replace(template, name, 1); - if (command == NULL || *command == '\0') { - free(command); - return; - } - - pr = cmd_parse_from_string(command, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - break; - case CMD_PARSE_ERROR: - if (c != NULL) { - *pr->error = toupper((u_char)*pr->error); - status_message_set(c, "%s", pr->error); + if (command != NULL && *command != '\0') { + state = cmdq_new_state(fs, NULL, 0); + status = cmd_parse_and_append(command, NULL, c, state, &error); + if (status == CMD_PARSE_ERROR) { + if (c != NULL) { + *error = toupper((u_char)*error); + status_message_set(c, "%s", error); + } + free(error); } - free(pr->error); - break; - case CMD_PARSE_SUCCESS: - new_state = cmdq_new_state(fs, NULL, 0); - new_item = cmdq_get_command(pr->cmdlist, new_state); - cmdq_free_state(new_state); - cmdq_append(c, new_item); - cmd_list_free(pr->cmdlist); - break; + cmdq_free_state(state); } - free(command); } diff --git a/popup.c b/popup.c index c2db145c..5d39e599 100644 --- a/popup.c +++ b/popup.c @@ -222,12 +222,12 @@ popup_key_cb(struct client *c, struct key_event *event) struct popup_data *pd = c->overlay_data; struct mouse_event *m = &event->m; struct cmd_find_state *fs = &pd->fs; - struct cmdq_item *new_item; - struct cmdq_state *new_state; - struct cmd_parse_result *pr; struct format_tree *ft; const char *cmd, *buf; size_t len; + struct cmdq_state *state; + enum cmd_parse_status status; + char *error; if (KEYC_IS_MOUSE(event->key)) { if (pd->dragging != OFF) { @@ -295,27 +295,19 @@ popup_key_cb(struct client *c, struct key_event *event) cmd = format_expand(ft, pd->cmd); format_free(ft); - pr = cmd_parse_from_string(cmd, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - break; - case CMD_PARSE_ERROR: - new_item = cmdq_get_error(pr->error); - free(pr->error); - cmdq_append(c, new_item); - break; - case CMD_PARSE_SUCCESS: - if (pd->item != NULL) - event = cmdq_get_event(pd->item); - else - event = NULL; - new_state = cmdq_new_state(&pd->fs, event, 0); - new_item = cmdq_get_command(pr->cmdlist, new_state); - cmdq_free_state(new_state); - cmd_list_free(pr->cmdlist); - cmdq_append(c, new_item); - break; + if (pd->item != NULL) + event = cmdq_get_event(pd->item); + else + event = NULL; + state = cmdq_new_state(&pd->fs, event, 0); + + status = cmd_parse_and_append(cmd, NULL, c, state, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_append(c, cmdq_get_error(error)); + free(error); } + cmdq_free_state(state); + return (1); out: diff --git a/tmux.h b/tmux.h index c1b91f93..bce2ec69 100644 --- a/tmux.h +++ b/tmux.h @@ -2089,6 +2089,12 @@ void cmd_parse_empty(struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_string(const char *, struct cmd_parse_input *); +enum cmd_parse_status cmd_parse_and_insert(const char *, + struct cmd_parse_input *, struct cmdq_item *, + struct cmdq_state *, char **); +enum cmd_parse_status cmd_parse_and_append(const char *, + struct cmd_parse_input *, struct client *, + struct cmdq_state *, char **); struct cmd_parse_result *cmd_parse_from_buffer(const void *, size_t, struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_arguments(int, char **, From 3f7f9a0e20522c73e33480673496240f1bac724b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 20:51:57 +0000 Subject: [PATCH 0158/1006] Make client -c and -t handling common in cmd-queue.c and try to be clearer about whether the client is the target client (must have a session) or not. --- cmd-break-pane.c | 6 +-- cmd-command-prompt.c | 11 ++--- cmd-confirm-before.c | 12 ++---- cmd-detach-client.c | 31 +++++++------- cmd-display-menu.c | 97 +++++++++++++++++++++---------------------- cmd-display-message.c | 17 ++++---- cmd-display-panes.c | 14 +++---- cmd-if-shell.c | 10 ++--- cmd-list-keys.c | 6 +-- cmd-load-buffer.c | 3 +- cmd-new-session.c | 1 - cmd-new-window.c | 6 +-- cmd-pipe-pane.c | 4 +- cmd-queue.c | 37 +++++++++++++++-- cmd-refresh-client.c | 71 +++++++++++++++---------------- cmd-rename-session.c | 3 +- cmd-rename-window.c | 3 +- cmd-respawn-window.c | 3 +- cmd-run-shell.c | 3 +- cmd-save-buffer.c | 3 +- cmd-select-pane.c | 10 ++--- cmd-send-keys.c | 56 ++++++++++++------------- cmd-split-window.c | 4 +- cmd-switch-client.c | 41 +++++++++--------- format.c | 6 +-- spawn.c | 4 +- tmux.h | 9 ++-- window-copy.c | 3 +- window.c | 2 +- 29 files changed, 239 insertions(+), 237 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index e12b09b6..880ee7f5 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -52,7 +52,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *source = cmdq_get_source(item); - struct client *c = cmd_find_client(item, NULL, 1); + struct client *tc = cmdq_get_target_client(item); struct winlink *wl = source->wl; struct session *src_s = source->s; struct session *dst_s = target->s; @@ -83,7 +83,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) wp->flags |= PANE_STYLECHANGED; TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; - w->latest = c; + w->latest = tc; if (!args_has(args, 'n')) { name = default_window_name(w); @@ -115,7 +115,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; - cp = format_single(item, template, c, dst_s, wl, wp); + cp = format_single(item, template, tc, dst_s, wl, wp); cmdq_print(item, "%s", cp); free(cp); } diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 510600e3..e53c4320 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -44,7 +44,7 @@ const struct cmd_entry cmd_command_prompt_entry = { .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " "[template]", - .flags = 0, + .flags = CMD_CLIENT_TFLAG, .exec = cmd_command_prompt_exec }; @@ -65,16 +65,13 @@ static enum cmd_retval cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); const char *inputs, *prompts; struct cmd_command_prompt_cdata *cdata; - struct client *c; char *prompt, *ptr, *input = NULL; size_t n; - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - - if (c->prompt_string != NULL) + if (tc->prompt_string != NULL) return (CMD_RETURN_NORMAL); cdata = xcalloc(1, sizeof *cdata); @@ -124,7 +121,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_INCREMENTAL; else if (args_has(args, 'k')) cdata->flags |= PROMPT_KEY; - status_prompt_set(c, prompt, input, cmd_command_prompt_callback, + status_prompt_set(tc, prompt, input, cmd_command_prompt_callback, cmd_command_prompt_free, cdata, cdata->flags); free(prompt); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 026d0e11..0d881178 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -42,7 +42,7 @@ const struct cmd_entry cmd_confirm_before_entry = { .args = { "p:t:", 1, 1 }, .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", - .flags = 0, + .flags = CMD_CLIENT_TFLAG, .exec = cmd_confirm_before_exec }; @@ -55,13 +55,10 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_confirm_before_data *cdata; - struct client *c; + struct client *tc = cmdq_get_target_client(item); char *cmd, *copy, *new_prompt, *ptr; const char *prompt; - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if ((prompt = args_get(args, 'p')) != NULL) xasprintf(&new_prompt, "%s ", prompt); else { @@ -74,9 +71,8 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) cdata = xmalloc(sizeof *cdata); cdata->cmd = xstrdup(args->argv[0]); - status_prompt_set(c, new_prompt, NULL, - cmd_confirm_before_callback, cmd_confirm_before_free, cdata, - PROMPT_SINGLE); + status_prompt_set(tc, new_prompt, NULL, cmd_confirm_before_callback, + cmd_confirm_before_free, cdata, PROMPT_SINGLE); free(new_prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index f4e350a8..02a43f4e 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_detach_client_entry = { .source = { 's', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, - .flags = CMD_READONLY, + .flags = CMD_READONLY|CMD_CLIENT_TFLAG, .exec = cmd_detach_client_exec }; @@ -50,7 +50,7 @@ const struct cmd_entry cmd_suspend_client_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, - .flags = 0, + .flags = CMD_CLIENT_TFLAG, .exec = cmd_detach_client_exec }; @@ -59,16 +59,13 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *source = cmdq_get_source(item); - struct client *c, *cloop; + struct client *tc = cmdq_get_target_client(item), *loop; struct session *s; enum msgtype msgtype; const char *cmd = args_get(args, 'E'); - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (cmd_get_entry(self) == &cmd_suspend_client_entry) { - server_client_suspend(c); + server_client_suspend(tc); return (CMD_RETURN_NORMAL); } @@ -81,32 +78,32 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item) s = source->s; if (s == NULL) return (CMD_RETURN_NORMAL); - TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session == s) { + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->session == s) { if (cmd != NULL) - server_client_exec(cloop, cmd); + server_client_exec(loop, cmd); else - server_client_detach(cloop, msgtype); + server_client_detach(loop, msgtype); } } return (CMD_RETURN_STOP); } if (args_has(args, 'a')) { - TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session != NULL && cloop != c) { + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->session != NULL && loop != tc) { if (cmd != NULL) - server_client_exec(cloop, cmd); + server_client_exec(loop, cmd); else - server_client_detach(cloop, msgtype); + server_client_detach(loop, msgtype); } } return (CMD_RETURN_NORMAL); } if (cmd != NULL) - server_client_exec(c, cmd); + server_client_exec(tc, cmd); else - server_client_detach(c, msgtype); + server_client_detach(tc, msgtype); return (CMD_RETURN_STOP); } diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 830491dc..0a5c7f78 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -42,7 +42,7 @@ const struct cmd_entry cmd_display_menu_entry = { .target = { 't', CMD_FIND_PANE, 0 }, - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG, .exec = cmd_display_menu_exec }; @@ -57,17 +57,18 @@ const struct cmd_entry cmd_display_popup_entry = { .target = { 't', CMD_FIND_PANE, 0 }, - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG, .exec = cmd_display_popup_exec }; static void -cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, +cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, struct args *args, u_int *px, u_int *py, u_int w, u_int h) { + struct tty *tty = &tc->tty; struct cmd_find_state *target = cmdq_get_target(item); struct key_event *event = cmdq_get_event(item); - struct session *s = c->session; + struct session *s = tc->session; struct winlink *wl = target->wl; struct window_pane *wp = target->wp; struct style_ranges *ranges; @@ -75,9 +76,9 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, const char *xp, *yp; u_int line, ox, oy, sx, sy, lines; - lines = status_line_size(c); + lines = status_line_size(tc); for (line = 0; line < lines; line++) { - ranges = &c->status.entries[line].ranges; + ranges = &tc->status.entries[line].ranges; TAILQ_FOREACH(sr, ranges, entry) { if (sr->type == STYLE_RANGE_WINDOW) break; @@ -86,15 +87,15 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, break; } if (line == lines) - ranges = &c->status.entries[0].ranges; + ranges = &tc->status.entries[0].ranges; xp = args_get(args, 'x'); if (xp == NULL || strcmp(xp, "C") == 0) - *px = (c->tty.sx - 1) / 2 - w / 2; + *px = (tty->sx - 1) / 2 - w / 2; else if (strcmp(xp, "R") == 0) - *px = c->tty.sx - 1; + *px = tty->sx - 1; else if (strcmp(xp, "P") == 0) { - tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); + tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); if (wp->xoff >= ox) *px = wp->xoff - ox; else @@ -105,7 +106,7 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, else *px = 0; } else if (strcmp(xp, "W") == 0) { - if (status_at_line(c) == -1) + if (status_at_line(tc) == -1) *px = 0; else { TAILQ_FOREACH(sr, ranges, entry) { @@ -121,14 +122,14 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, } } else *px = strtoul(xp, NULL, 10); - if ((*px) + w >= c->tty.sx) - *px = c->tty.sx - w; + if ((*px) + w >= tty->sx) + *px = tty->sx - w; yp = args_get(args, 'y'); if (yp == NULL || strcmp(yp, "C") == 0) - *py = (c->tty.sy - 1) / 2 + h / 2; + *py = (tty->sy - 1) / 2 + h / 2; else if (strcmp(yp, "P") == 0) { - tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); + tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); if (wp->yoff + wp->sy >= oy) *py = wp->yoff + wp->sy - oy; else @@ -146,9 +147,9 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, *py = 0; } else { if (lines != 0) - *py = c->tty.sy - lines; + *py = tty->sy - lines; else - *py = c->tty.sy; + *py = tty->sy; } } else if (strcmp(yp, "W") == 0) { if (options_get_number(s->options, "status-position") == 0) { @@ -158,9 +159,9 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, *py = 0; } else { if (lines != 0) - *py = c->tty.sy - lines + line; + *py = tty->sy - lines + line; else - *py = c->tty.sy; + *py = tty->sy; } } else *py = strtoul(yp, NULL, 10); @@ -168,8 +169,8 @@ cmd_display_menu_get_position(struct client *c, struct cmdq_item *item, *py = 0; else *py -= h; - if ((*py) + h >= c->tty.sy) - *py = c->tty.sy - h; + if ((*py) + h >= tty->sy) + *py = tty->sy - h; } static enum cmd_retval @@ -178,7 +179,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); struct key_event *event = cmdq_get_event(item); - struct client *c; + struct client *tc = cmdq_get_target_client(item); struct menu *menu = NULL; struct menu_item menu_item; const char *key; @@ -186,13 +187,11 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) int flags = 0, i; u_int px, py; - if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (c->overlay_draw != NULL) + if (tc->overlay_draw != NULL) return (CMD_RETURN_NORMAL); if (args_has(args, 'T')) - title = format_single_from_target(item, args_get(args, 'T'), c); + title = format_single_from_target(item, args_get(args, 'T')); else title = xstrdup(""); menu = menu_create(title); @@ -200,7 +199,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) for (i = 0; i != args->argc; /* nothing */) { name = args->argv[i++]; if (*name == '\0') { - menu_add_item(menu, NULL, item, c, target); + menu_add_item(menu, NULL, item, tc, target); continue; } @@ -216,7 +215,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) menu_item.key = key_string_lookup_string(key); menu_item.command = args->argv[i++]; - menu_add_item(menu, &menu_item, item, c, target); + menu_add_item(menu, &menu_item, item, tc, target); } free(title); if (menu == NULL) { @@ -227,12 +226,13 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) menu_free(menu); return (CMD_RETURN_NORMAL); } - cmd_display_menu_get_position(c, item, args, &px, &py, menu->width + 4, + cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4, menu->count + 2); if (!event->m.valid) flags |= MENU_NOMOUSE; - if (menu_display(menu, flags, item, px, py, c, target, NULL, NULL) != 0) + if (menu_display(menu, flags, item, px, py, tc, target, NULL, + NULL) != 0) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } @@ -242,20 +242,19 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct client *c; + struct client *tc = cmdq_get_target_client(item); + struct tty *tty = &tc->tty; const char *value, *cmd = NULL, **lines = NULL; const char *shellcmd = NULL; char *cwd, *cause; int flags = 0; u_int px, py, w, h, nlines = 0; - if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) - return (CMD_RETURN_ERROR); if (args_has(args, 'C')) { - server_client_clear_overlay(c); + server_client_clear_overlay(tc); return (CMD_RETURN_NORMAL); } - if (c->overlay_draw != NULL) + if (tc->overlay_draw != NULL) return (CMD_RETURN_NORMAL); if (args->argc >= 1) @@ -268,9 +267,9 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) if (nlines != 0) h = popup_height(nlines, lines) + 2; else - h = c->tty.sy / 2; + h = tty->sy / 2; if (args_has(args, 'h')) { - h = args_percentage(args, 'h', 1, c->tty.sy, c->tty.sy, &cause); + h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause); if (cause != NULL) { cmdq_error(item, "height %s", cause); free(cause); @@ -279,11 +278,11 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) } if (nlines != 0) - w = popup_width(item, nlines, lines, c, target) + 2; + w = popup_width(item, nlines, lines, tc, target) + 2; else - w = c->tty.sx / 2; + w = tty->sx / 2; if (args_has(args, 'w')) { - w = args_percentage(args, 'w', 1, c->tty.sx, c->tty.sx, &cause); + w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause); if (cause != NULL) { cmdq_error(item, "width %s", cause); free(cause); @@ -291,21 +290,21 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) } } - if (w > c->tty.sx - 1) - w = c->tty.sx - 1; - if (h > c->tty.sy - 1) - h = c->tty.sy - 1; - cmd_display_menu_get_position(c, item, args, &px, &py, w, h); + if (w > tty->sx - 1) + w = tty->sx - 1; + if (h > tty->sy - 1) + h = tty->sy - 1; + cmd_display_menu_get_position(tc, item, args, &px, &py, w, h); value = args_get(args, 'd'); if (value != NULL) - cwd = format_single_from_target(item, value, c); + cwd = format_single_from_target(item, value); else - cwd = xstrdup(server_client_get_cwd(c, target->s)); + cwd = xstrdup(server_client_get_cwd(tc, target->s)); value = args_get(args, 'R'); if (value != NULL) - shellcmd = format_single_from_target(item, value, c); + shellcmd = format_single_from_target(item, value); if (args_has(args, 'K')) flags |= POPUP_WRITEKEYS; @@ -314,7 +313,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, - cmd, cwd, c, target) != 0) + cmd, cwd, tc, target) != 0) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } diff --git a/cmd-display-message.c b/cmd-display-message.c index 8fb09f20..4e69f03a 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -45,7 +45,7 @@ const struct cmd_entry cmd_display_message_entry = { .target = { 't', CMD_FIND_PANE, 0 }, - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL, .exec = cmd_display_message_exec }; @@ -62,7 +62,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct client *c, *target_c; + struct client *tc = cmdq_get_target_client(item), *c; struct session *s = target->s; struct winlink *wl = target->wl; struct window_pane *wp = target->wp; @@ -97,17 +97,16 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) * formats too, assuming it matches the session. If it doesn't, use the * best client for the session. */ - c = cmd_find_client(item, args_get(args, 'c'), 1); - if (c != NULL && c->session == s) - target_c = c; + if (tc != NULL && tc->session == s) + c = tc; else - target_c = cmd_find_best_client(s); + c = cmd_find_best_client(s); if (args_has(args, 'v')) flags = FORMAT_VERBOSE; else flags = 0; ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, flags); - format_defaults(ft, target_c, s, wl, wp); + format_defaults(ft, c, s, wl, wp); if (args_has(args, 'a')) { format_each(ft, cmd_display_message_each, item); @@ -117,8 +116,8 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) msg = format_expand_time(ft, template); if (args_has(args, 'p')) cmdq_print(item, "%s", msg); - else if (c != NULL) - status_message_set(c, "%s", msg); + else if (tc != NULL) + status_message_set(tc, "%s", msg); free(msg); format_free(ft); diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 7e1a9e3d..0f12b14b 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_display_panes_entry = { .args = { "bd:t:", 0, 1 }, .usage = "[-b] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_display_panes_exec }; @@ -228,17 +228,13 @@ static enum cmd_retval cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct client *c; - struct session *s; + struct client *tc = cmdq_get_target_client(item); + struct session *s = tc->session; u_int delay; char *cause; struct cmd_display_panes_data *cdata; - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - s = c->session; - - if (c->overlay_draw != NULL) + if (tc->overlay_draw != NULL) return (CMD_RETURN_NORMAL); if (args_has(args, 'd')) { @@ -261,7 +257,7 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) else cdata->item = item; - server_client_set_overlay(c, delay, NULL, NULL, cmd_display_panes_draw, + server_client_set_overlay(tc, delay, NULL, NULL, cmd_display_panes_draw, cmd_display_panes_key, cmd_display_panes_free, cdata); if (args_has(args, 'b')) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 75f462ad..d980472a 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -67,12 +67,12 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) struct cmd_if_shell_data *cdata; char *shellcmd, *cmd, *error; const char *file; - struct client *c = cmd_find_client(item, NULL, 1); + struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct cmd_parse_input pi; enum cmd_parse_status status; - shellcmd = format_single_from_target(item, args->argv[0], c); + shellcmd = format_single_from_target(item, args->argv[0]); if (args_has(args, 'F')) { if (*shellcmd != '0' && *shellcmd != '\0') cmd = args->argv[1]; @@ -87,7 +87,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) memset(&pi, 0, sizeof pi); cmd_get_source(self, &pi.file, &pi.line); pi.item = item; - pi.c = c; + pi.c = tc; cmd_find_copy_state(&pi.fs, target); status = cmd_parse_and_insert(cmd, &pi, item, state, &error); @@ -110,7 +110,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'b')) cdata->client = cmdq_get_client(item); else - cdata->client = c; + cdata->client = tc; if (cdata->client != NULL) cdata->client->references++; @@ -123,7 +123,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cmd_get_source(self, &file, &cdata->input.line); if (file != NULL) cdata->input.file = xstrdup(file); - cdata->input.c = c; + cdata->input.c = tc; if (cdata->input.c != NULL) cdata->input.c->references++; cmd_find_copy_state(&cdata->input.fs, target); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index cfdceee7..1141bbb5 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -85,7 +85,7 @@ static int cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, const char *tablename, u_int keywidth, key_code only, const char *prefix) { - struct client *c = cmd_find_client(item, NULL, 1); + struct client *tc = cmdq_get_target_client(item); struct key_table *table; struct key_binding *bd; const char *key; @@ -111,8 +111,8 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, else note = xstrdup(bd->note); tmp = utf8_padcstr(key, keywidth + 1); - if (args_has(args, '1') && c != NULL) - status_message_set(c, "%s%s%s", prefix, tmp, note); + if (args_has(args, '1') && tc != NULL) + status_message_set(tc, "%s%s%s", prefix, tmp, note); else cmdq_print(item, "%s%s%s", prefix, tmp, note); free(tmp); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 1fc45b0f..49e834d6 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -85,7 +85,6 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_load_buffer_data *cdata; - struct client *c = cmd_find_client(item, NULL, 1); const char *bufname = args_get(args, 'b'); char *path; @@ -96,7 +95,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) else cdata->name = NULL; - path = format_single_from_target(item, args->argv[0], c); + path = format_single_from_target(item, args->argv[0]); file_read(cmdq_get_client(item), path, cmd_load_buffer_done, cdata); free(path); diff --git a/cmd-new-session.c b/cmd-new-session.c index 574cd5cd..353f7bed 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -269,7 +269,6 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; - sc.c = c; sc.name = args_get(args, 'n'); sc.argc = args->argc; diff --git a/cmd-new-window.c b/cmd-new-window.c index 87aae659..722f89b9 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -55,7 +55,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; - struct client *c = cmd_find_client(item, NULL, 1); + struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct winlink *wl = target->wl; int idx = target->idx; @@ -73,7 +73,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; - sc.c = c; + sc.tc = tc; sc.name = args_get(args, 'n'); sc.argc = args->argc; @@ -109,7 +109,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - cp = format_single(item, template, c, s, new_wl, + cp = format_single(item, template, tc, s, new_wl, new_wl->window->active); cmdq_print(item, "%s", cp); free(cp); diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 4ccc22c6..185bdc12 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -58,7 +58,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct client *c = cmd_find_client(item, NULL, 1); + struct client *tc = cmdq_get_target_client(item); struct window_pane *wp = target->wp; struct session *s = target->s; struct winlink *wl = target->wl; @@ -110,7 +110,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) /* Expand the command. */ ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); - format_defaults(ft, c, s, wl, wp); + format_defaults(ft, tc, s, wl, wp); cmd = format_expand_time(ft, args->argv[0]); format_free(ft); diff --git a/cmd-queue.c b/cmd-queue.c index 97d19f81..16c58401 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -42,6 +42,7 @@ struct cmdq_item { struct cmdq_item *next; struct client *client; + struct client *target_client; enum cmdq_type type; u_int group; @@ -145,6 +146,13 @@ cmdq_get_client(struct cmdq_item *item) return (item->client); } +/* Get item target client. */ +struct client * +cmdq_get_target_client(struct cmdq_item *item) +{ + return (item->target_client); +} + /* Get item state. */ struct cmdq_state * cmdq_get_state(struct cmdq_item *item) @@ -483,14 +491,15 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs, static enum cmd_retval cmdq_fire_command(struct cmdq_item *item) { - struct client *c = item->client; - const char *name = cmdq_name(c); + const char *name = cmdq_name(item->client); struct cmdq_state *state = item->state; struct cmd *cmd = item->cmd; + struct args *args = cmd_get_args(cmd); const struct cmd_entry *entry = cmd_get_entry(cmd); + struct client *tc, *saved = item->client; enum cmd_retval retval; struct cmd_find_state *fsp, fs; - int flags; + int flags, quiet = 0; char *tmp; if (log_get_level() > 1) { @@ -504,6 +513,25 @@ cmdq_fire_command(struct cmdq_item *item) if (item->client == NULL) item->client = cmd_find_client(item, NULL, 1); + + if (entry->flags & CMD_CLIENT_CANFAIL) + quiet = 1; + if (entry->flags & CMD_CLIENT_CFLAG) { + tc = cmd_find_client(item, args_get(args, 'c'), quiet); + if (tc == NULL && !quiet) { + retval = CMD_RETURN_ERROR; + goto out; + } + } else if (entry->flags & CMD_CLIENT_TFLAG) { + tc = cmd_find_client(item, args_get(args, 't'), quiet); + if (tc == NULL && !quiet) { + retval = CMD_RETURN_ERROR; + goto out; + } + } else + tc = cmd_find_client(item, NULL, 1); + item->target_client = tc; + retval = cmdq_find_flag(item, &item->source, &entry->source); if (retval == CMD_RETURN_ERROR) goto out; @@ -511,6 +539,7 @@ cmdq_fire_command(struct cmdq_item *item) if (retval == CMD_RETURN_ERROR) goto out; + retval = entry->exec(cmd, item); if (retval == CMD_RETURN_ERROR) goto out; @@ -528,7 +557,7 @@ cmdq_fire_command(struct cmdq_item *item) } out: - item->client = c; + item->client = saved; if (retval == CMD_RETURN_ERROR) cmdq_guard(item, "error", flags); else diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 1becaaae..5514ff73 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_refresh_client_entry = { .usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_refresh_client_exec }; @@ -46,17 +46,13 @@ static enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct client *c; - struct tty *tty; + struct client *tc = cmdq_get_target_client(item); + struct tty *tty = &tc->tty; struct window *w; const char *size, *errstr; char *copy, *next, *s; u_int x, y, adjust; - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - tty = &c->tty; - if (args_has(args, 'c') || args_has(args, 'L') || args_has(args, 'R') || @@ -74,48 +70,47 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'c')) - c->pan_window = NULL; + tc->pan_window = NULL; else { - w = c->session->curw->window; - if (c->pan_window != w) { - c->pan_window = w; - c->pan_ox = tty->oox; - c->pan_oy = tty->ooy; + w = tc->session->curw->window; + if (tc->pan_window != w) { + tc->pan_window = w; + tc->pan_ox = tty->oox; + tc->pan_oy = tty->ooy; } if (args_has(args, 'L')) { - if (c->pan_ox > adjust) - c->pan_ox -= adjust; + if (tc->pan_ox > adjust) + tc->pan_ox -= adjust; else - c->pan_ox = 0; + tc->pan_ox = 0; } else if (args_has(args, 'R')) { - c->pan_ox += adjust; - if (c->pan_ox > w->sx - tty->osx) - c->pan_ox = w->sx - tty->osx; + tc->pan_ox += adjust; + if (tc->pan_ox > w->sx - tty->osx) + tc->pan_ox = w->sx - tty->osx; } else if (args_has(args, 'U')) { - if (c->pan_oy > adjust) - c->pan_oy -= adjust; + if (tc->pan_oy > adjust) + tc->pan_oy -= adjust; else - c->pan_oy = 0; + tc->pan_oy = 0; } else if (args_has(args, 'D')) { - c->pan_oy += adjust; - if (c->pan_oy > w->sy - tty->osy) - c->pan_oy = w->sy - tty->osy; + tc->pan_oy += adjust; + if (tc->pan_oy > w->sy - tty->osy) + tc->pan_oy = w->sy - tty->osy; } } - tty_update_client_offset(c); - server_redraw_client(c); + tty_update_client_offset(tc); + server_redraw_client(tc); return (CMD_RETURN_NORMAL); } if (args_has(args, 'l')) { - if (c->session != NULL) - tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?"); + tty_putcode_ptr2(&tc->tty, TTYC_MS, "", "?"); return (CMD_RETURN_NORMAL); } if (args_has(args, 'C') || args_has(args, 'F')) { if (args_has(args, 'C')) { - if (!(c->flags & CLIENT_CONTROL)) { + if (!(tc->flags & CLIENT_CONTROL)) { cmdq_error(item, "not a control client"); return (CMD_RETURN_ERROR); } @@ -130,12 +125,12 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "size too small or too big"); return (CMD_RETURN_ERROR); } - tty_set_size(&c->tty, x, y, 0, 0); - c->flags |= CLIENT_SIZECHANGED; + tty_set_size(&tc->tty, x, y, 0, 0); + tc->flags |= CLIENT_SIZECHANGED; recalculate_sizes(); } if (args_has(args, 'F')) { - if (!(c->flags & CLIENT_CONTROL)) { + if (!(tc->flags & CLIENT_CONTROL)) { cmdq_error(item, "not a control client"); return (CMD_RETURN_ERROR); } @@ -143,7 +138,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) while ((next = strsep(&s, ",")) != NULL) { /* Unknown flags are ignored. */ if (strcmp(next, "no-output") == 0) - c->flags |= CLIENT_CONTROL_NOOUTPUT; + tc->flags |= CLIENT_CONTROL_NOOUTPUT; } free(copy); } @@ -151,11 +146,11 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'S')) { - c->flags |= CLIENT_STATUSFORCE; - server_status_client(c); + tc->flags |= CLIENT_STATUSFORCE; + server_status_client(tc); } else { - c->flags |= CLIENT_STATUSFORCE; - server_redraw_client(c); + tc->flags |= CLIENT_STATUSFORCE; + server_redraw_client(tc); } return (CMD_RETURN_NORMAL); } diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 5f003473..4b2c3d88 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -48,11 +48,10 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct client *c = cmd_find_client(item, NULL, 1); struct session *s = target->s; char *newname; - newname = format_single_from_target(item, args->argv[0], c); + newname = format_single_from_target(item, args->argv[0]); if (strcmp(newname, s->name) == 0) { free(newname); return (CMD_RETURN_NORMAL); diff --git a/cmd-rename-window.c b/cmd-rename-window.c index d3e6d883..1fb17ad9 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -47,11 +47,10 @@ cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct client *c = cmd_find_client(item, NULL, 1); struct winlink *wl = target->wl; char *newname; - newname = format_single_from_target(item, args->argv[0], c); + newname = format_single_from_target(item, args->argv[0]); window_set_name(wl->window, newname); options_set_number(wl->window->options, "automatic-rename", 0); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 9e745dda..39d19ddb 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -50,6 +50,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; + struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct winlink *wl = target->wl; char *cause = NULL; @@ -60,7 +61,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) sc.item = item; sc.s = s; sc.wl = wl; - sc.c = cmd_find_client(item, NULL, 1); + sc.tc = tc; sc.name = NULL; sc.argc = args->argc; diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 3c82ffdb..82d4a1a2 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -92,7 +92,6 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); struct cmd_run_shell_data *cdata; - struct client *c = cmd_find_client(item, NULL, 1); struct session *s = target->s; struct window_pane *wp = target->wp; const char *delay; @@ -102,7 +101,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) cdata = xcalloc(1, sizeof *cdata); if (args->argc != 0) - cdata->cmd = format_single_from_target(item, args->argv[0], c); + cdata->cmd = format_single_from_target(item, args->argv[0]); if (args_has(args, 't') && wp != NULL) cdata->wp_id = wp->id; diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 9aaa365f..a6ad188f 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -74,7 +74,6 @@ static enum cmd_retval cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct client *c = cmd_find_client(item, NULL, 1); struct paste_buffer *pb; int flags; const char *bufname = args_get(args, 'b'), *bufdata; @@ -98,7 +97,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) if (cmd_get_entry(self) == &cmd_show_buffer_entry) path = xstrdup("-"); else - path = format_single_from_target(item, args->argv[0], c); + path = format_single_from_target(item, args->argv[0]); if (args_has(args, 'a')) flags = O_APPEND; else diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 5f4a2e63..db110ff9 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -87,12 +87,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) const struct cmd_entry *entry = cmd_get_entry(self); struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); - struct client *c = cmd_find_client(item, NULL, 1); struct winlink *wl = target->wl; struct window *w = wl->window; struct session *s = target->s; struct window_pane *wp = target->wp, *lastwp, *markedwp; - char *pane_title; + char *title; const char *style; struct style *sy; struct options_entry *o; @@ -197,11 +196,10 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'T')) { - pane_title = format_single(item, args_get(args, 'T'), - c, s, wl, wp); - if (screen_set_title(&wp->base, pane_title)) + title = format_single_from_target(item, args_get(args, 'T')); + if (screen_set_title(&wp->base, title)) server_status_window(wp->window); - free(pane_title); + free(title); return (CMD_RETURN_NORMAL); } diff --git a/cmd-send-keys.c b/cmd-send-keys.c index de910904..173d3713 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -57,23 +57,23 @@ const struct cmd_entry cmd_send_prefix_entry = { }; static struct cmdq_item * -cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, - struct cmdq_item *item, key_code key) +cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, + key_code key) { struct cmd_find_state *target = cmdq_get_target(item); - struct session *s = fs->s; - struct winlink *wl = fs->wl; - struct window_pane *wp = fs->wp; + struct client *tc = cmdq_get_target_client(item); + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; struct window_mode_entry *wme; struct key_table *table; struct key_binding *bd; - wme = TAILQ_FIRST(&fs->wp->modes); + wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode->key_table == NULL) { - if (options_get_number(fs->wp->window->options, "xterm-keys")) + if (options_get_number(wp->window->options, "xterm-keys")) key |= KEYC_XTERM; - if (window_pane_key(wp, cmdq_get_client(item), s, wl, key, - NULL) != 0) + if (window_pane_key(wp, tc, s, wl, key, NULL) != 0) return (NULL); return (item); } @@ -82,18 +82,17 @@ cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, bd = key_bindings_get(table, key & ~KEYC_XTERM); if (bd != NULL) { table->references++; - item = key_bindings_dispatch(bd, item, c, NULL, target); + after = key_bindings_dispatch(bd, after, tc, NULL, target); key_bindings_unref_table(table); } - return (item); + return (after); } static struct cmdq_item * -cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, - struct cmdq_item *item, struct args *args, int i) +cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, + struct args *args, int i) { const char *s = args->argv[i]; - struct cmdq_item *new_item; struct utf8_data *ud, *uc; wchar_t wc; key_code key; @@ -105,29 +104,29 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, n = strtol(s, &endptr, 16); if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') return (item); - return (cmd_send_keys_inject_key(c, fs, item, KEYC_LITERAL|n)); + return (cmd_send_keys_inject_key(item, after, KEYC_LITERAL|n)); } literal = args_has(args, 'l'); if (!literal) { key = key_string_lookup_string(s); if (key != KEYC_NONE && key != KEYC_UNKNOWN) { - new_item = cmd_send_keys_inject_key(c, fs, item, key); - if (new_item != NULL) - return (new_item); + after = cmd_send_keys_inject_key(item, after, key); + if (after != NULL) + return (after); } literal = 1; } if (literal) { ud = utf8_fromcstr(s); for (uc = ud; uc->size != 0; uc++) { - if (utf8_combine(uc, &wc) != UTF8_DONE) + if (utf8_combine(uc, &wc) == UTF8_DONE) continue; - item = cmd_send_keys_inject_key(c, fs, item, wc); + after = cmd_send_keys_inject_key(item, after, wc); } free(ud); } - return (item); + return (after); } static enum cmd_retval @@ -135,13 +134,14 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct client *c = cmd_find_client(item, NULL, 1); - struct window_pane *wp = target->wp; + struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; struct key_event *event = cmdq_get_event(item); struct mouse_event *m = &event->m; struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct cmdq_item *after = item; int i; key_code key; u_int np = 1; @@ -170,7 +170,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) } if (!m->valid) m = NULL; - wme->mode->command(wme, c, s, wl, args, m); + wme->mode->command(wme, tc, s, wl, args, m); return (CMD_RETURN_NORMAL); } @@ -180,7 +180,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "no mouse target"); return (CMD_RETURN_ERROR); } - window_pane_key(wp, cmdq_get_client(item), s, wl, m->key, m); + window_pane_key(wp, tc, s, wl, m->key, m); return (CMD_RETURN_NORMAL); } @@ -189,7 +189,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) key = options_get_number(s->options, "prefix2"); else key = options_get_number(s->options, "prefix"); - cmd_send_keys_inject_key(c, target, item, key); + cmd_send_keys_inject_key(item, item, key); return (CMD_RETURN_NORMAL); } @@ -200,8 +200,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) for (; np != 0; np--) { for (i = 0; i < args->argc; i++) { - item = cmd_send_keys_inject_string(c, target, item, - args, i); + after = cmd_send_keys_inject_string(item, after, args, + i); } } diff --git a/cmd-split-window.c b/cmd-split-window.c index 84419a1e..c0e0e22a 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -57,7 +57,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; - struct client *c = cmd_find_client(item, NULL, 1); + struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct winlink *wl = target->wl; struct window_pane *wp = target->wp, *new_wp; @@ -174,7 +174,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = SPLIT_WINDOW_TEMPLATE; - cp = format_single(item, template, c, s, wl, new_wp); + cp = format_single(item, template, tc, s, wl, new_wp); cmdq_print(item, "%s", cp); free(cp); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index bc6a8174..82510ce6 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -40,7 +40,7 @@ const struct cmd_entry cmd_switch_client_entry = { /* -t is special */ - .flags = CMD_READONLY, + .flags = CMD_READONLY|CMD_CLIENT_CFLAG, .exec = cmd_switch_client_exec }; @@ -53,7 +53,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) const char *tflag = args_get(args, 't'); enum cmd_find_type type; int flags; - struct client *c; + struct client *tc = cmdq_get_target_client(item); struct session *s; struct winlink *wl; struct window *w; @@ -61,9 +61,6 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) const char *tablename; struct key_table *table; - if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (tflag != NULL && tflag[strcspn(tflag, ":.%")] != '\0') { type = CMD_FIND_PANE; flags = 0; @@ -78,7 +75,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) wp = target.wp; if (args_has(args, 'r')) - c->flags ^= CLIENT_READONLY; + tc->flags ^= CLIENT_READONLY; tablename = args_get(args, 'T'); if (tablename != NULL) { @@ -88,24 +85,24 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } table->references++; - key_bindings_unref_table(c->keytable); - c->keytable = table; + key_bindings_unref_table(tc->keytable); + tc->keytable = table; return (CMD_RETURN_NORMAL); } if (args_has(args, 'n')) { - if ((s = session_next_session(c->session)) == NULL) { + if ((s = session_next_session(tc->session)) == NULL) { cmdq_error(item, "can't find next session"); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'p')) { - if ((s = session_previous_session(c->session)) == NULL) { + if ((s = session_previous_session(tc->session)) == NULL) { cmdq_error(item, "can't find previous session"); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'l')) { - if (c->last_session != NULL && session_alive(c->last_session)) - s = c->last_session; + if (tc->last_session != NULL && session_alive(tc->last_session)) + s = tc->last_session; else s = NULL; if (s == NULL) { @@ -131,23 +128,23 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) } if (!args_has(args, 'E')) - environ_update(s->options, c->environ, s->environ); + environ_update(s->options, tc->environ, s->environ); - if (c->session != NULL && c->session != s) - c->last_session = c->session; - c->session = s; + if (tc->session != NULL && tc->session != s) + tc->last_session = tc->session; + tc->session = s; if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) - server_client_set_key_table(c, NULL); - tty_update_client_offset(c); - status_timer_start(c); - notify_client("client-session-changed", c); + server_client_set_key_table(tc, NULL); + tty_update_client_offset(tc); + status_timer_start(tc); + notify_client("client-session-changed", tc); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); server_check_unattached(); - server_redraw_client(c); + server_redraw_client(tc); s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = c; + s->curw->window->latest = tc; recalculate_sizes(); alerts_check_session(s); diff --git a/format.c b/format.c index 08fa38df..fd3b45b9 100644 --- a/format.c +++ b/format.c @@ -2429,12 +2429,12 @@ format_single(struct cmdq_item *item, const char *fmt, struct client *c, /* Expand a single string using target. */ char * -format_single_from_target(struct cmdq_item *item, const char *fmt, - struct client *c) +format_single_from_target(struct cmdq_item *item, const char *fmt) { struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item); - return (format_single(item, fmt, c, target->s, target->wl, target->wp)); + return (format_single(item, fmt, tc, target->s, target->wl, target->wp)); } /* Set defaults for any of arguments that are not NULL. */ diff --git a/spawn.c b/spawn.c index 49ab2a1e..91fb310a 100644 --- a/spawn.c +++ b/spawn.c @@ -153,7 +153,7 @@ spawn_window(struct spawn_context *sc, char **cause) xasprintf(cause, "couldn't add window %d", idx); return (NULL); } - default_window_size(sc->c, s, NULL, &sx, &sy, &xpixel, &ypixel, + default_window_size(sc->tc, s, NULL, &sx, &sy, &xpixel, &ypixel, -1); if ((w = window_create(sx, sy, xpixel, ypixel)) == NULL) { winlink_remove(&s->windows, sc->wl); @@ -163,7 +163,7 @@ spawn_window(struct spawn_context *sc, char **cause) if (s->curw == NULL) s->curw = sc->wl; sc->wl->session = s; - w->latest = sc->c; + w->latest = sc->tc; winlink_set_window(sc->wl, w); } else w = NULL; diff --git a/tmux.h b/tmux.h index bce2ec69..d9fc0a9a 100644 --- a/tmux.h +++ b/tmux.h @@ -1413,6 +1413,9 @@ struct cmd_entry { #define CMD_STARTSERVER 0x1 #define CMD_READONLY 0x2 #define CMD_AFTERHOOK 0x4 +#define CMD_CLIENT_CFLAG 0x8 +#define CMD_CLIENT_TFLAG 0x10 +#define CMD_CLIENT_CANFAIL 0x20 int flags; enum cmd_retval (*exec)(struct cmd *, struct cmdq_item *); @@ -1681,7 +1684,7 @@ struct spawn_context { struct session *s; struct winlink *wl; - struct client *c; + struct client *tc; struct window_pane *wp0; struct layout_cell *lc; @@ -1793,8 +1796,7 @@ char *format_expand(struct format_tree *, const char *); char *format_single(struct cmdq_item *, const char *, struct client *, struct session *, struct winlink *, struct window_pane *); -char *format_single_from_target(struct cmdq_item *, const char *, - struct client *); +char *format_single_from_target(struct cmdq_item *, const char *); void format_defaults(struct format_tree *, struct client *, struct session *, struct winlink *, struct window_pane *); void format_defaults_window(struct format_tree *, struct window *); @@ -2113,6 +2115,7 @@ struct cmdq_list *cmdq_new(void); void cmdq_free(struct cmdq_list *); const char *cmdq_get_name(struct cmdq_item *); struct client *cmdq_get_client(struct cmdq_item *); +struct client *cmdq_get_target_client(struct cmdq_item *); struct cmdq_state *cmdq_get_state(struct cmdq_item *); struct cmd_find_state *cmdq_get_target(struct cmdq_item *); struct cmd_find_state *cmdq_get_source(struct cmdq_item *); diff --git a/window-copy.c b/window-copy.c index 995dc35c..efb4a43f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -130,7 +130,8 @@ static void window_copy_rectangle_toggle(struct window_mode_entry *); static void window_copy_move_mouse(struct mouse_event *); static void window_copy_drag_update(struct client *, struct mouse_event *); static void window_copy_drag_release(struct client *, struct mouse_event *); -static struct screen* window_copy_clone_screen(struct screen *, struct screen*); +static struct screen *window_copy_clone_screen(struct screen *, + struct screen *); const struct window_mode window_copy_mode = { .name = "copy-mode", diff --git a/window.c b/window.c index 13fb3bec..b0194eaf 100644 --- a/window.c +++ b/window.c @@ -1150,7 +1150,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { - if (wme->mode->key != NULL) + if (wme->mode->key != NULL && c != NULL) wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); return (0); } From fc83517913c8280c222a6cf78ca7fb8053421b37 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Apr 2020 20:54:15 +0000 Subject: [PATCH 0159/1006] Missed a few warnings in previous. --- cmd-lock-server.c | 12 ++++-------- cmd-set-option.c | 7 +++---- cmd-show-messages.c | 10 +++------- cmd-show-options.c | 3 +-- 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 086cf639..a0df95b0 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -57,26 +57,22 @@ const struct cmd_entry cmd_lock_client_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_lock_server_exec }; static enum cmd_retval cmd_lock_server_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct client *c; + struct client *tc = cmdq_get_target_client(item); if (cmd_get_entry(self) == &cmd_lock_server_entry) server_lock(); else if (cmd_get_entry(self) == &cmd_lock_session_entry) server_lock_session(target->s); - else { - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - server_lock_client(c); - } + else + server_lock_client(tc); recalculate_sizes(); return (CMD_RETURN_NORMAL); diff --git a/cmd-set-option.c b/cmd-set-option.c index 4e81dc5e..1752d093 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -84,7 +84,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); struct cmd_find_state *target = cmdq_get_target(item); - struct client *c, *loop; + struct client *loop; struct session *s = target->s; struct window *w; struct window_pane *wp; @@ -98,8 +98,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) window = (cmd_get_entry(self) == &cmd_set_window_option_entry); /* Expand argument. */ - c = cmd_find_client(item, NULL, 1); - argument = format_single_from_target(item, args->argv[0], c); + argument = format_single_from_target(item, args->argv[0]); /* If set-hook -R, fire the hook straight away. */ if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) { @@ -122,7 +121,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) if (args->argc < 2) value = NULL; else if (args_has(args, 'F')) - value = format_single_from_target(item, args->argv[1], c); + value = format_single_from_target(item, args->argv[1]); else value = xstrdup(args->argv[1]); diff --git a/cmd-show-messages.c b/cmd-show-messages.c index cedf1093..78e5859c 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_show_messages_entry = { .args = { "JTt:", 0, 0 }, .usage = "[-JT] " CMD_TARGET_CLIENT_USAGE, - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_show_messages_exec }; @@ -70,14 +70,11 @@ static enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct client *c; + struct client *tc = cmdq_get_target_client(item); struct message_entry *msg; char *tim; int done, blank; - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - done = blank = 0; if (args_has(args, 'T')) { blank = cmd_show_messages_terminals(item, blank); @@ -90,10 +87,9 @@ cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) if (done) return (CMD_RETURN_NORMAL); - TAILQ_FOREACH(msg, &c->message_log, entry) { + TAILQ_FOREACH(msg, &tc->message_log, entry) { tim = ctime(&msg->msg_time); *strchr(tim, '\n') = '\0'; - cmdq_print(item, "%s %s", tim, msg->msg); } diff --git a/cmd-show-options.c b/cmd-show-options.c index 5b62a78e..e2aec930 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -79,7 +79,6 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct client *c = cmd_find_client(item, NULL, 1); struct options *oo; char *argument, *name = NULL, *cause; int window, idx, ambiguous, parent, scope; @@ -99,7 +98,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) } return (cmd_show_options_all(self, item, scope, oo)); } - argument = format_single_from_target(item, args->argv[0], c); + argument = format_single_from_target(item, args->argv[0]); name = options_match(argument, &idx, &ambiguous); if (name == NULL) { From 63ec7918542a36ac9afa60b2e91f75337c5a91ff Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 14 Apr 2020 06:00:52 +0000 Subject: [PATCH 0160/1006] Provide an accessor for the running queue item and use it to not let hooks recurse. --- cmd-queue.c | 36 ++++++++++++++++++++++++++---------- notify.c | 9 ++++++--- tmux.h | 1 + 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 16c58401..5df30940 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -64,7 +64,7 @@ struct cmdq_item { TAILQ_ENTRY(cmdq_item) entry; }; -TAILQ_HEAD(cmdq_list, cmdq_item); +TAILQ_HEAD(cmdq_item_list, cmdq_item); /* * Command queue state. This is the context for commands on the command queue. @@ -83,6 +83,12 @@ struct cmdq_state { struct cmd_find_state current; }; +/* Command queue. */ +struct cmdq_list { + struct cmdq_item *item; + struct cmdq_item_list list; +}; + /* Get command queue name. */ static const char * cmdq_name(struct client *c) @@ -119,7 +125,7 @@ cmdq_new(void) struct cmdq_list *queue; queue = xcalloc (1, sizeof *queue); - TAILQ_INIT (queue); + TAILQ_INIT (&queue->list); return (queue); } @@ -127,7 +133,7 @@ cmdq_new(void) void cmdq_free(struct cmdq_list *queue) { - if (!TAILQ_EMPTY(queue)) + if (!TAILQ_EMPTY(&queue->list)) fatalx("queue not empty"); free(queue); } @@ -289,12 +295,12 @@ cmdq_append(struct client *c, struct cmdq_item *item) item->client = c; item->queue = queue; - TAILQ_INSERT_TAIL(queue, item, entry); + TAILQ_INSERT_TAIL(&queue->list, item, entry); log_debug("%s %s: %s", __func__, cmdq_name(c), item->name); item = next; } while (item != NULL); - return (TAILQ_LAST(queue, cmdq_list)); + return (TAILQ_LAST(&queue->list, cmdq_item_list)); } /* Insert an item. */ @@ -315,7 +321,7 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) item->client = c; item->queue = queue; - TAILQ_INSERT_AFTER(queue, after, item, entry); + TAILQ_INSERT_AFTER(&queue->list, after, item, entry); log_debug("%s %s: %s after %s", __func__, cmdq_name(c), item->name, after->name); @@ -399,7 +405,7 @@ cmdq_remove(struct cmdq_item *item) cmd_list_free(item->cmdlist); cmdq_free_state(item->state); - TAILQ_REMOVE(item->queue, item, entry); + TAILQ_REMOVE(&item->queue->list, item, entry); free(item->name); free(item); @@ -621,18 +627,18 @@ cmdq_next(struct client *c) u_int items = 0; static u_int number; - if (TAILQ_EMPTY(queue)) { + if (TAILQ_EMPTY(&queue->list)) { log_debug("%s %s: empty", __func__, name); return (0); } - if (TAILQ_FIRST(queue)->flags & CMDQ_WAITING) { + if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) { log_debug("%s %s: waiting", __func__, name); return (0); } log_debug("%s %s: enter", __func__, name); for (;;) { - item = TAILQ_FIRST(queue); + item = queue->item = TAILQ_FIRST(&queue->list); if (item == NULL) break; log_debug("%s %s: %s (%d), flags %x", __func__, name, @@ -682,6 +688,7 @@ cmdq_next(struct client *c) } cmdq_remove(item); } + queue->item = NULL; log_debug("%s %s: exit (empty)", __func__, name); return (items); @@ -691,6 +698,15 @@ waiting: return (items); } +/* Get running item if any. */ +struct cmdq_item * +cmdq_running(struct client *c) +{ + struct cmdq_list *queue = cmdq_get(c); + + return (queue->item); +} + /* Print a guard line. */ void cmdq_guard(struct cmdq_item *item, const char *guard, int flags) diff --git a/notify.c b/notify.c index 3147c4e0..54254354 100644 --- a/notify.c +++ b/notify.c @@ -158,7 +158,11 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c, struct session *s, struct window *w, struct window_pane *wp) { struct notify_entry *ne; - struct cmdq_item *new_item; + struct cmdq_item *item; + + item = cmdq_running(NULL); + if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS)) + return; ne = xcalloc(1, sizeof *ne); ne->name = xstrdup(name); @@ -183,8 +187,7 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c, if (ne->fs.s != NULL) /* cmd_find_valid_state needs session */ session_add_ref(ne->fs.s, __func__); - new_item = cmdq_get_callback(notify_callback, ne); - cmdq_append(NULL, new_item); + cmdq_append(NULL, cmdq_get_callback(notify_callback, ne)); } void diff --git a/tmux.h b/tmux.h index d9fc0a9a..b9097e93 100644 --- a/tmux.h +++ b/tmux.h @@ -2132,6 +2132,7 @@ void cmdq_insert_hook(struct session *, struct cmdq_item *, struct cmd_find_state *, const char *, ...); void cmdq_continue(struct cmdq_item *); u_int cmdq_next(struct client *); +struct cmdq_item *cmdq_running(struct client *); void cmdq_guard(struct cmdq_item *, const char *, int); void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...); From 1ef9a69f4fcb503bc2cf1a5901a9ca6eaaae5d44 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 14 Apr 2020 13:22:05 +0000 Subject: [PATCH 0161/1006] Send keys when they are complete not before (!= vs ==). --- cmd-send-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 173d3713..afaf0a81 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -120,7 +120,7 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, if (literal) { ud = utf8_fromcstr(s); for (uc = ud; uc->size != 0; uc++) { - if (utf8_combine(uc, &wc) == UTF8_DONE) + if (utf8_combine(uc, &wc) != UTF8_DONE) continue; after = cmd_send_keys_inject_key(item, after, wc); } From e11295f42d55a387d1609dde7ff7e2c7745c2be2 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 14 Apr 2020 18:33:01 +0000 Subject: [PATCH 0162/1006] Adjust cursor and scroll positions when entering copy mode so that the cursor line is still visible even if the source and target panes are different heights. --- window-copy.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/window-copy.c b/window-copy.c index efb4a43f..90c4b45f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -376,10 +376,18 @@ window_copy_init(struct window_mode_entry *wme, u_int i; data = window_copy_common_init(wme); - data->backing = window_copy_clone_screen(&wp->base, &data->screen); - data->cx = data->backing->cx; - data->cy = data->backing->cy; + + data->cx = wp->base.cx; + if (data->cx > screen_size_x(&data->screen) - 1) + data->cx = screen_size_x(&data->screen) - 1; + + data->cy = screen_hsize(&wp->base) + wp->base.cy; + if (data->cy < screen_hsize(data->backing)) { + data->oy = screen_hsize(data->backing) - data->cy; + data->cy = 0; + } else + data->cy -= screen_hsize(data->backing); data->scroll_exit = args_has(args, 'e'); data->hide_position = args_has(args, 'H'); From b9a00cbe8ac7436008b9697de4b738ebed19ed8c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 14 Apr 2020 19:07:10 +0000 Subject: [PATCH 0163/1006] Leave the cursor above empty lines. --- window-copy.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/window-copy.c b/window-copy.c index 90c4b45f..f6f29f9a 100644 --- a/window-copy.c +++ b/window-copy.c @@ -130,8 +130,6 @@ static void window_copy_rectangle_toggle(struct window_mode_entry *); static void window_copy_move_mouse(struct mouse_event *); static void window_copy_drag_update(struct client *, struct mouse_event *); static void window_copy_drag_release(struct client *, struct mouse_event *); -static struct screen *window_copy_clone_screen(struct screen *, - struct screen *); const struct window_mode window_copy_mode = { .name = "copy-mode", @@ -322,7 +320,7 @@ window_copy_clone_screen(struct screen *src, struct screen *hint) } screen_write_start(&ctx, NULL, dst); - screen_write_cursormove(&ctx, 0, dst->grid->sy - 1, 0); + screen_write_cursormove(&ctx, 0, dst->grid->sy + sy - dy - 1, 0); screen_write_stop(&ctx); return (dst); From c2c9b77f141f3462414789b696dcee2cc2789bc4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 Apr 2020 13:04:20 +0100 Subject: [PATCH 0164/1006] Do not use the command if the kernel didn't return the full size. --- osdep-darwin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osdep-darwin.c b/osdep-darwin.c index d4a88028..6b2b1d72 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -61,7 +61,7 @@ osdep_get_name(int fd, __unused char *tty) size = sizeof kp; if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) return (NULL); - if (*kp.kp_proc.p_comm == '\0') + if (size != (sizeof kp) || *kp.kp_proc.p_comm == '\0') return (NULL); return (strdup(kp.kp_proc.p_comm)); From c7883d5c872a56c5342092f81e87e0742713b4de Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Apr 2020 12:59:20 +0000 Subject: [PATCH 0165/1006] Use grid_empty_line rather than memset when adding new lines on resize. Also remove some old test code. --- grid.c | 4 +--- screen.c | 13 ++----------- tmux.h | 1 + 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/grid.c b/grid.c index f33bc98d..0cef412b 100644 --- a/grid.c +++ b/grid.c @@ -48,8 +48,6 @@ static const struct grid_cell_entry grid_cleared_entry = { GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } } }; -static void grid_empty_line(struct grid *, u_int, u_int); - /* Store cell in entry. */ static void grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc, @@ -454,7 +452,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg) } /* Empty a line and set background colour if needed. */ -static void +void grid_empty_line(struct grid *gd, u_int py, u_int bg) { memset(&gd->linedata[py], 0, sizeof gd->linedata[py]); diff --git a/screen.c b/screen.c index 898cc860..6fa0fe11 100644 --- a/screen.c +++ b/screen.c @@ -314,7 +314,7 @@ screen_resize_y(struct screen *s, u_int sy) /* Then fill the rest in with blanks. */ for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++) - memset(grid_get_line(gd, i), 0, sizeof(struct grid_line)); + grid_empty_line(gd, i, 8); } /* Set the new size, and reset the scroll region. */ @@ -484,10 +484,7 @@ screen_select_cell(struct screen *s, struct grid_cell *dst, static void screen_reflow(struct screen *s, u_int new_x) { - u_int cx = s->cx, cy = s->grid->hsize + s->cy, wx, wy; - struct timeval start, tv; - - gettimeofday(&start, NULL); + u_int cx = s->cx, cy = s->grid->hsize + s->cy, wx, wy; grid_wrap_position(s->grid, cx, cy, &wx, &wy); log_debug("%s: cursor %u,%u is %u,%u", __func__, cx, cy, wx, wy); @@ -504,12 +501,6 @@ screen_reflow(struct screen *s, u_int new_x) s->cx = 0; s->cy = 0; } - - gettimeofday(&tv, NULL); - timersub(&tv, &start, &tv); - - log_debug("%s: reflow took %llu.%06u seconds", __func__, - (unsigned long long)tv.tv_sec, (u_int)tv.tv_usec); } /* diff --git a/tmux.h b/tmux.h index b9097e93..278919d6 100644 --- a/tmux.h +++ b/tmux.h @@ -2312,6 +2312,7 @@ int attributes_fromstring(const char *); /* grid.c */ extern const struct grid_cell grid_default_cell; +void grid_empty_line(struct grid *, u_int, u_int); int grid_cells_equal(const struct grid_cell *, const struct grid_cell *); struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); From 1e72f5ea4356a102e12c0b5325d4af824899d39c Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Apr 2020 16:11:23 +0000 Subject: [PATCH 0166/1006] Use mode-style for selected items, like choose modes. GitHub issue 2166. --- menu.c | 6 +++++- screen-write.c | 25 +++++++++++++------------ tmux.h | 3 ++- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/menu.c b/menu.c index 78369218..941d4123 100644 --- a/menu.c +++ b/menu.c @@ -149,10 +149,14 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) struct menu *menu = md->menu; struct screen_write_ctx ctx; u_int i, px = md->px, py = md->py; + struct grid_cell gc; + + memcpy(&gc, &grid_default_cell, sizeof gc); + style_apply(&gc, c->session->curw->window->options, "mode-style"); screen_write_start(&ctx, NULL, s); screen_write_clearscreen(&ctx, 8); - screen_write_menu(&ctx, menu, md->choice); + screen_write_menu(&ctx, menu, md->choice, &gc); screen_write_stop(&ctx); for (i = 0; i < screen_size_y(&md->s); i++) diff --git a/screen-write.c b/screen-write.c index 43cb42b4..b2f9e223 100644 --- a/screen-write.c +++ b/screen-write.c @@ -409,21 +409,23 @@ screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom) /* Draw a menu on screen. */ void -screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice) +screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, + int choice, const struct grid_cell *choice_gc) { struct screen *s = ctx->s; - struct grid_cell gc; + struct grid_cell default_gc; + const struct grid_cell *gc = &default_gc; u_int cx, cy, i, j; const char *name; cx = s->cx; cy = s->cy; - memcpy(&gc, &grid_default_cell, sizeof gc); + memcpy(&default_gc, &grid_default_cell, sizeof default_gc); screen_write_box(ctx, menu->width + 4, menu->count + 2); screen_write_cursormove(ctx, cx + 2, cy, 0); - format_draw(ctx, &gc, menu->width, menu->title, NULL); + format_draw(ctx, &default_gc, menu->width, menu->title, NULL); for (i = 0; i < menu->count; i++) { name = menu->items[i].name; @@ -432,20 +434,19 @@ screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice) screen_write_hline(ctx, menu->width + 4, 1, 1); } else { if (choice >= 0 && i == (u_int)choice && *name != '-') - gc.attr |= GRID_ATTR_REVERSE; + gc = choice_gc; screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); for (j = 0; j < menu->width; j++) - screen_write_putc(ctx, &gc, ' '); + screen_write_putc(ctx, gc, ' '); screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); if (*name == '-') { name++; - gc.attr |= GRID_ATTR_DIM; - format_draw(ctx, &gc, menu->width, name, NULL); - gc.attr &= ~GRID_ATTR_DIM; + default_gc.attr |= GRID_ATTR_DIM; + format_draw(ctx, gc, menu->width, name, NULL); + default_gc.attr &= ~GRID_ATTR_DIM; } else - format_draw(ctx, &gc, menu->width, name, NULL); - if (choice >= 0 && i == (u_int)choice) - gc.attr &= ~GRID_ATTR_REVERSE; + format_draw(ctx, gc, menu->width, name, NULL); + gc = &default_gc; } } diff --git a/tmux.h b/tmux.h index 278919d6..260891b2 100644 --- a/tmux.h +++ b/tmux.h @@ -2382,7 +2382,8 @@ void screen_write_fast_copy(struct screen_write_ctx *, struct screen *, u_int, u_int, u_int, u_int); void screen_write_hline(struct screen_write_ctx *, u_int, int, int); void screen_write_vline(struct screen_write_ctx *, u_int, int, int); -void screen_write_menu(struct screen_write_ctx *, struct menu *, int); +void screen_write_menu(struct screen_write_ctx *, struct menu *, int, + const struct grid_cell *); void screen_write_box(struct screen_write_ctx *, u_int, u_int); void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int, u_int); From 53a29a2ffa0d7f167499b67ec36bc7a1adf78b49 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Apr 2020 17:50:02 +0000 Subject: [PATCH 0167/1006] Instead of fixing with the cursor position when the copied screen is created, resize it and let the resize/reflow code fix it up and return it. Solves various problems with cursor position and resizing when in copy mode. With Anindya Mukherjee. --- screen.c | 82 ++++++++++++++++++++++++++++++++------------------- tmux.h | 2 ++ window-copy.c | 66 ++++++++++++++++++++++------------------- 3 files changed, 88 insertions(+), 62 deletions(-) diff --git a/screen.c b/screen.c index 6fa0fe11..ff8b1d2e 100644 --- a/screen.c +++ b/screen.c @@ -48,9 +48,8 @@ struct screen_title_entry { }; TAILQ_HEAD(screen_titles, screen_title_entry); -static void screen_resize_y(struct screen *, u_int); - -static void screen_reflow(struct screen *, u_int); +static void screen_resize_y(struct screen *, u_int, int); +static void screen_reflow(struct screen *, u_int, u_int *, u_int *); /* Free titles stack. */ static void @@ -216,10 +215,23 @@ screen_pop_title(struct screen *s) } } -/* Resize screen. */ +/* Resize screen and return cursor position. */ void -screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) +screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, + int eat_empty, u_int *cx, u_int *cy) { + u_int tcx, tcy; + + if (cx == NULL) + cx = &tcx; + *cx = s->cx; + + if (cy == NULL) + cy = т + *cy = s->grid->hsize + s->cy; + + log_debug("%s: start %u,%u (%u,%u)", __func__, s->cx, s->cy, *cx, *cy); + if (sx < 1) sx = 1; if (sy < 1) @@ -232,14 +244,30 @@ screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) reflow = 0; if (sy != screen_size_y(s)) - screen_resize_y(s, sy); + screen_resize_y(s, sy, eat_empty); if (reflow) - screen_reflow(s, sx); + screen_reflow(s, sx, cx, cy); + + if (*cy >= s->grid->hsize) { + s->cx = *cx; + s->cy = (*cy) - s->grid->hsize; + } else { + s->cx = 0; + s->cy = 0; + } + log_debug("%s: finish %u,%u (%u,%u)", __func__, s->cx, s->cy, *cx, *cy); +} + +/* Resize screen. */ +void +screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) +{ + screen_resize_cursor(s, sx, sy, reflow, 1, NULL, NULL); } static void -screen_resize_y(struct screen *s, u_int sy) +screen_resize_y(struct screen *s, u_int sy, int eat_empty) { struct grid *gd = s->grid; u_int needed, available, oldy, i; @@ -264,14 +292,16 @@ screen_resize_y(struct screen *s, u_int sy) needed = oldy - sy; /* Delete as many lines as possible from the bottom. */ - available = oldy - 1 - s->cy; - if (available > 0) { - if (available > needed) - available = needed; - grid_view_delete_lines(gd, oldy - available, available, - 8); + if (eat_empty) { + available = oldy - 1 - s->cy; + if (available > 0) { + if (available > needed) + available = needed; + grid_view_delete_lines(gd, oldy - available, + available, 8); + } + needed -= available; } - needed -= available; /* * Now just increase the history size, if possible, to take @@ -287,7 +317,6 @@ screen_resize_y(struct screen *s, u_int sy) available = needed; grid_view_delete_lines(gd, 0, available, 8); } - s->cy -= needed; } /* Resize line array. */ @@ -307,7 +336,6 @@ screen_resize_y(struct screen *s, u_int sy) available = needed; gd->hscrolled -= available; gd->hsize -= available; - s->cy += available; } else available = 0; needed -= available; @@ -482,25 +510,17 @@ screen_select_cell(struct screen *s, struct grid_cell *dst, /* Reflow wrapped lines. */ static void -screen_reflow(struct screen *s, u_int new_x) +screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy) { - u_int cx = s->cx, cy = s->grid->hsize + s->cy, wx, wy; + u_int wx, wy; - grid_wrap_position(s->grid, cx, cy, &wx, &wy); - log_debug("%s: cursor %u,%u is %u,%u", __func__, cx, cy, wx, wy); + grid_wrap_position(s->grid, *cx, *cy, &wx, &wy); + log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx, wy); grid_reflow(s->grid, new_x); - grid_unwrap_position(s->grid, &cx, &cy, wx, wy); - log_debug("%s: new cursor is %u,%u", __func__, cx, cy); - - if (cy >= s->grid->hsize) { - s->cx = cx; - s->cy = cy - s->grid->hsize; - } else { - s->cx = 0; - s->cy = 0; - } + grid_unwrap_position(s->grid, cx, cy, wx, wy); + log_debug("%s: new cursor is %u,%u", __func__, *cx,* cy); } /* diff --git a/tmux.h b/tmux.h index 260891b2..73b6b941 100644 --- a/tmux.h +++ b/tmux.h @@ -2437,6 +2437,8 @@ void screen_set_path(struct screen *, const char *); void screen_push_title(struct screen *); void screen_pop_title(struct screen *); void screen_resize(struct screen *, u_int, u_int, int); +void screen_resize_cursor(struct screen *, u_int, u_int, int, int, u_int *, + u_int *); void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int, u_int, int, struct grid_cell *); void screen_clear_selection(struct screen *); diff --git a/window-copy.c b/window-copy.c index f6f29f9a..2b42eb2a 100644 --- a/window-copy.c +++ b/window-copy.c @@ -298,30 +298,33 @@ window_copy_scroll_timer(__unused int fd, __unused short events, void *arg) } static struct screen * -window_copy_clone_screen(struct screen *src, struct screen *hint) +window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, + u_int *cy) { - struct screen *dst; - struct screen_write_ctx ctx; - u_int dy, sy; + struct screen *dst; + u_int sy; + const struct grid_line *gl; dst = xcalloc(1, sizeof *dst); sy = screen_hsize(src) + screen_size_y(src); - if (screen_size_y(hint) > sy) - dy = screen_size_y(hint); - else - dy = sy; - screen_init(dst, screen_size_x(src), dy, src->grid->hlimit); - - grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy); - if (screen_size_y(hint) < sy) { - dst->grid->sy = screen_size_y(hint); - dst->grid->hsize = sy - screen_size_y(hint); + while (sy > screen_hsize(src)) { + gl = grid_peek_line(src->grid, sy - 1); + if (gl->cellused != 0) + break; + sy--; } + screen_init(dst, screen_size_x(src), sy, screen_hlimit(src)); + grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy); - screen_write_start(&ctx, NULL, dst); - screen_write_cursormove(&ctx, 0, dst->grid->sy + sy - dy - 1, 0); - screen_write_stop(&ctx); + dst->grid->sy = sy - screen_hsize(src); + dst->grid->hsize = screen_hsize(src); + dst->grid->hscrolled = src->grid->hscrolled; + dst->cx = src->cx; + dst->cy = src->cy; + + screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1, + 0, cx, cy); return (dst); } @@ -370,22 +373,22 @@ window_copy_init(struct window_mode_entry *wme, { struct window_pane *wp = wme->swp; struct window_copy_mode_data *data; + struct screen *base = &wp->base; struct screen_write_ctx ctx; - u_int i; + u_int i, cx, cy; data = window_copy_common_init(wme); - data->backing = window_copy_clone_screen(&wp->base, &data->screen); + data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy); - data->cx = wp->base.cx; - if (data->cx > screen_size_x(&data->screen) - 1) - data->cx = screen_size_x(&data->screen) - 1; - - data->cy = screen_hsize(&wp->base) + wp->base.cy; - if (data->cy < screen_hsize(data->backing)) { - data->oy = screen_hsize(data->backing) - data->cy; + if (cy < screen_hsize(data->backing)) { + data->cx = cx; data->cy = 0; - } else - data->cy -= screen_hsize(data->backing); + data->oy = screen_hsize(data->backing) - cy; + } else { + data->cx = data->backing->cx; + data->cy = data->backing->cy; + data->oy = 0; + } data->scroll_exit = args_has(args, 'e'); data->hide_position = args_has(args, 'H'); @@ -697,8 +700,8 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) struct screen_write_ctx ctx; int search; - screen_resize(s, sx, sy, 1); - screen_resize(data->backing, sx, sy, 1); + screen_resize(s, sx, sy, 0); + screen_resize_cursor(data->backing, sx, sy, 1, 0, NULL, NULL); if (data->cy > sy - 1) data->cy = sy - 1; @@ -2024,7 +2027,8 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) screen_free(data->backing); free(data->backing); - data->backing = window_copy_clone_screen(&wp->base, &data->screen); + data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, + NULL); return (WINDOW_COPY_CMD_REDRAW); } From fc1855f514028941df1acb79a2dfd728a2197f94 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Apr 2020 19:06:49 +0000 Subject: [PATCH 0168/1006] Clear the selection and repeat the search on refresh same as resize. --- window-copy.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/window-copy.c b/window-copy.c index 2b42eb2a..a9307cb5 100644 --- a/window-copy.c +++ b/window-copy.c @@ -693,24 +693,13 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) } static void -window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) +window_copy_size_changed(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; - struct screen_write_ctx ctx; - int search; + struct screen_write_ctx ctx; + int search = (data->searchmark != NULL); - screen_resize(s, sx, sy, 0); - screen_resize_cursor(data->backing, sx, sy, 1, 0, NULL, NULL); - - if (data->cy > sy - 1) - data->cy = sy - 1; - if (data->cx > sx) - data->cx = sx; - if (data->oy > screen_hsize(data->backing)) - data->oy = screen_hsize(data->backing); - - search = (data->searchmark != NULL); window_copy_clear_selection(wme); window_copy_clear_marks(wme); @@ -723,7 +712,25 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; +} +static void +window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) +{ + struct window_copy_mode_data *data = wme->data; + struct screen *s = &data->screen; + + screen_resize(s, sx, sy, 0); + screen_resize_cursor(data->backing, sx, sy, 1, 0, NULL, NULL); + + if (data->cy > sy - 1) + data->cy = sy - 1; + if (data->cx > sx) + data->cx = sx; + if (data->oy > screen_hsize(data->backing)) + data->oy = screen_hsize(data->backing); + + window_copy_size_changed(wme); window_copy_redraw_screen(wme); } @@ -2030,6 +2037,7 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, NULL); + window_copy_size_changed(wme); return (WINDOW_COPY_CMD_REDRAW); } From b6dfca9b4d5bae8860c38bb6835c012ad0225fa3 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 05:22:08 +0000 Subject: [PATCH 0169/1006] Don't miss the last line off the screen when writing after resize, from Anindya Mukherjee. --- window-copy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index a9307cb5..8364ef11 100644 --- a/window-copy.c +++ b/window-copy.c @@ -704,7 +704,7 @@ window_copy_size_changed(struct window_mode_entry *wme) window_copy_clear_marks(wme); screen_write_start(&ctx, NULL, s); - window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1); + window_copy_write_lines(wme, &ctx, 0, screen_size_y(s)); screen_write_stop(&ctx); if (search && !data->timeout) From a2e47b527986af6140a9dfa43c9448347dc0a050 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 07:28:36 +0000 Subject: [PATCH 0170/1006] Show signal name when process exits rather than number. --- server-fn.c | 4 ++-- tmux.c | 12 ++++++++++++ tmux.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/server-fn.c b/server-fn.c index aa4a482b..da1371ae 100644 --- a/server-fn.c +++ b/server-fn.c @@ -334,8 +334,8 @@ server_destroy_pane(struct window_pane *wp, int notify) tim); } else if (WIFSIGNALED(wp->status)) { screen_write_nputs(&ctx, -1, &gc, - "Pane is dead (signal %d, %s)", - WTERMSIG(wp->status), + "Pane is dead (signal %s, %s)", + sig2name(WTERMSIG(wp->status)), tim); } diff --git a/tmux.c b/tmux.c index eac8c12f..c9678d9e 100644 --- a/tmux.c +++ b/tmux.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -167,6 +168,17 @@ setblocking(int fd, int state) } } +const char * +sig2name(int signo) +{ + static char s[11]; + + if (signo > 0 && signo < NSIG) + return (sys_signame[signo]); + xsnprintf(s, sizeof s, "%d", signo); + return (s); +} + const char * find_cwd(void) { diff --git a/tmux.h b/tmux.h index 73b6b941..98d78a63 100644 --- a/tmux.h +++ b/tmux.h @@ -1725,6 +1725,7 @@ extern int ptm_fd; extern const char *shell_command; int checkshell(const char *); void setblocking(int, int); +const char *sig2name(int); const char *find_cwd(void); const char *find_home(void); const char *getversion(void); From 1aa2845026710b3f90b56952a7feb6e88d7e5c26 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 16 Apr 2020 10:08:16 +0100 Subject: [PATCH 0171/1006] Check for sys_signame. --- configure.ac | 3 +++ tmux.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/configure.ac b/configure.ac index e29ebdc7..359d2137 100644 --- a/configure.ac +++ b/configure.ac @@ -87,6 +87,9 @@ AC_CHECK_HEADERS([ \ util.h \ ]) +# Look for sys_signame. +AC_SEARCH_LIBS(sys_signame, ) + # Look for fmod. AC_CHECK_LIB(m, fmod) diff --git a/tmux.c b/tmux.c index f5cd4343..e16642fd 100644 --- a/tmux.c +++ b/tmux.c @@ -170,8 +170,10 @@ sig2name(int signo) { static char s[11]; +#ifdef HAVE_SYS_SIGNAME if (signo > 0 && signo < NSIG) return (sys_signame[signo]); +#endif xsnprintf(s, sizeof s, "%d", signo); return (s); } From b3cadf826034ec356d0bf3dda26b7699bff4649c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 16 Apr 2020 10:15:33 +0100 Subject: [PATCH 0172/1006] Fix sys_signame check. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 359d2137..dc133c25 100644 --- a/configure.ac +++ b/configure.ac @@ -88,7 +88,7 @@ AC_CHECK_HEADERS([ \ ]) # Look for sys_signame. -AC_SEARCH_LIBS(sys_signame, ) +AC_SEARCH_LIBS(sys_signame, , AC_DEFINE(HAVE_SYS_SIGNAME)) # Look for fmod. AC_CHECK_LIB(m, fmod) From b2443aa2f98c1a1fa5d53d4e79a3e7fd221cc365 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 13:35:24 +0000 Subject: [PATCH 0173/1006] Add support for the iTerm2 sychronized updates escape sequence which drastically reduces flickering. --- screen-redraw.c | 6 ++++++ screen-write.c | 11 ++++++++++- tmux.1 | 2 ++ tmux.h | 6 ++++++ tty-keys.c | 2 +- tty-term.c | 5 +++++ tty.c | 26 ++++++++++++++++++++++++++ 7 files changed, 56 insertions(+), 2 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 211f7f79..c510fb68 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -435,6 +435,7 @@ screen_redraw_screen(struct client *c) flags = screen_redraw_update(c, c->flags); screen_redraw_set_context(c, &ctx); + tty_sync_start(&c->tty); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { if (ctx.pane_status != PANE_STATUS_OFF) @@ -448,7 +449,9 @@ screen_redraw_screen(struct client *c) screen_redraw_draw_status(&ctx); if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) c->overlay_draw(c, &ctx); + tty_reset(&c->tty); + tty_sync_end(&c->tty); } /* Redraw a single pane. */ @@ -461,9 +464,12 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) return; screen_redraw_set_context(c, &ctx); + tty_sync_start(&c->tty); screen_redraw_draw_pane(&ctx, wp); + tty_reset(&c->tty); + tty_sync_end(&c->tty); } /* Draw a border cell. */ diff --git a/screen-write.c b/screen-write.c index b2f9e223..3e7fa66b 100644 --- a/screen-write.c +++ b/screen-write.c @@ -100,7 +100,8 @@ void screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) { - u_int y; + struct tty_ctx ttyctx; + u_int y; memset(ctx, 0, sizeof *ctx); @@ -129,18 +130,26 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, screen_size_y(ctx->s)); } } + + screen_write_initctx(ctx, &ttyctx); + tty_write(tty_cmd_syncstart, &ttyctx); } /* Finish writing. */ void screen_write_stop(struct screen_write_ctx *ctx) { + struct tty_ctx ttyctx; + screen_write_collect_end(ctx); screen_write_collect_flush(ctx, 0); log_debug("%s: %u cells (%u written, %u skipped)", __func__, ctx->cells, ctx->written, ctx->skipped); + screen_write_initctx(ctx, &ttyctx); + tty_write(tty_cmd_syncend, &ttyctx); + free(ctx->item); free(ctx->list); /* flush will have emptied */ } diff --git a/tmux.1 b/tmux.1 index 81a5c613..fd022c9e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5511,6 +5511,8 @@ $ printf '\e033[4 q' If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Sync +Show that the terminal supports synchronized updates. .It Em \&Tc Indicate that the terminal supports the .Ql direct colour diff --git a/tmux.h b/tmux.h index 98d78a63..87d38ef3 100644 --- a/tmux.h +++ b/tmux.h @@ -452,6 +452,7 @@ enum tty_code_code { TTYC_SMUL, TTYC_SMXX, TTYC_SS, + TTYC_SYNC, TTYC_TC, TTYC_TSL, TTYC_U8, @@ -1181,6 +1182,7 @@ struct tty_term { #define TERM_DECSLRM 0x4 #define TERM_DECFRA 0x8 #define TERM_RGBCOLOURS 0x10 +#define TERM_SYNC 0x20 int flags; LIST_ENTRY(tty_term) entry; @@ -1949,6 +1951,8 @@ void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); void tty_draw_line(struct tty *, struct window_pane *, struct screen *, u_int, u_int, u_int, u_int, u_int); +void tty_sync_start(struct tty *); +void tty_sync_end(struct tty *); int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); @@ -1976,6 +1980,8 @@ void tty_cmd_scrolldown(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); +void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); +void tty_cmd_syncend(struct tty *, const struct tty_ctx *); /* tty-term.c */ extern struct tty_terms tty_terms; diff --git a/tty-keys.c b/tty-keys.c index 064f2172..1fcc74b8 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1117,7 +1117,7 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, /* Set terminal flags. */ if (strncmp(tmp, "ITERM2 ", 7) == 0) - flags |= (TERM_DECSLRM|TERM_256COLOURS|TERM_RGBCOLOURS); + flags |= (TERM_DECSLRM|TERM_256COLOURS|TERM_RGBCOLOURS|TERM_SYNC); if (strncmp(tmp, "TMUX ", 5) == 0) flags |= (TERM_256COLOURS|TERM_RGBCOLOURS); log_debug("%s: received DSR %.*s", c->name, (int)*size, buf); diff --git a/tty-term.c b/tty-term.c index ef7d7905..d6faa6ff 100644 --- a/tty-term.c +++ b/tty-term.c @@ -260,6 +260,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SMXX] = { TTYCODE_STRING, "smxx" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, + [TTYC_SYNC] = { TTYCODE_FLAG, "Sync" }, [TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, [TTYC_U8] = { TTYCODE_NUMBER, "U8" }, @@ -532,6 +533,10 @@ tty_term_find(char *name, int fd, char **cause) tty_term_has(term, TTYC_SETRGBB))) term->flags |= TERM_RGBCOLOURS; + /* Set flag if terminal has synchronized updates. */ + if (tty_term_flag(term, TTYC_SYNC)) + term->flags |= TERM_SYNC; + /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). diff --git a/tty.c b/tty.c index a7607cba..95c16fb8 100644 --- a/tty.c +++ b/tty.c @@ -1426,6 +1426,20 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, tty_update_mode(tty, tty->mode, s); } +void +tty_sync_start(struct tty *tty) +{ + if ((tty->term->flags|tty->term_flags) & TERM_SYNC) + tty_puts(tty, "\033P=1s\033\\"); +} + +void +tty_sync_end(struct tty *tty) +{ + if ((tty->term->flags|tty->term_flags) & TERM_SYNC) + tty_puts(tty, "\033P=2s\033\\"); +} + static int tty_client_ready(struct client *c, struct window_pane *wp) { @@ -1919,6 +1933,18 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) tty_invalidate(tty); } +void +tty_cmd_syncstart(struct tty *tty, __unused const struct tty_ctx *ctx) +{ + tty_sync_start(tty); +} + +void +tty_cmd_syncend(struct tty *tty, __unused const struct tty_ctx *ctx) +{ + tty_sync_end(tty); +} + static void tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) { From 4744aa43af47815a9c4c110cceb3959b1662e54b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 14:03:51 +0000 Subject: [PATCH 0174/1006] Add a helper function to get the terminal flags. --- tmux.h | 1 + tty.c | 44 ++++++++++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/tmux.h b/tmux.h index 87d38ef3..dbf3244f 100644 --- a/tmux.h +++ b/tmux.h @@ -1957,6 +1957,7 @@ int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); void tty_set_flags(struct tty *, int); +int tty_get_flags(struct tty *); 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 95c16fb8..a068c1a6 100644 --- a/tty.c +++ b/tty.c @@ -74,7 +74,7 @@ static void tty_default_attributes(struct tty *, struct window_pane *, u_int); #define tty_use_margin(tty) \ - ((tty->term->flags|tty->term_flags) & TERM_DECSLRM) + (tty_get_flags(tty) & TERM_DECSLRM) #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) @@ -478,6 +478,14 @@ tty_set_flags(struct tty *tty, int flags) tty_puts(tty, "\033[?69h"); /* DECLRMM */ } +int +tty_get_flags(struct tty *tty) +{ + if (tty->term != NULL) + return (tty->term->flags|tty->term_flags); + return (tty->term_flags); +} + void tty_raw(struct tty *tty, const char *s) { @@ -575,7 +583,7 @@ tty_putc(struct tty *tty, u_char ch) { const char *acs; - if ((tty->term->flags & TERM_NOXENL) && + if ((tty_get_flags(tty) & TERM_NOXENL) && ch >= 0x20 && ch != 0x7f && tty->cy == tty->sy - 1 && tty->cx + 1 >= tty->sx) @@ -601,7 +609,7 @@ tty_putc(struct tty *tty, u_char ch) * where we think it should be after a line wrap - this * means it works on sensible terminals as well. */ - if (tty->term->flags & TERM_NOXENL) + if (tty_get_flags(tty) & TERM_NOXENL) tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx); } else tty->cx++; @@ -611,7 +619,7 @@ tty_putc(struct tty *tty, u_char ch) void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { - if ((tty->term->flags & TERM_NOXENL) && + if ((tty_get_flags(tty) & TERM_NOXENL) && tty->cy == tty->sy - 1 && tty->cx + len >= tty->sx) len = tty->sx - tty->cx - 1; @@ -1167,7 +1175,7 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, * background colour isn't default (because it doesn't work * after SGR 0). */ - if (((tty->term->flags|tty->term_flags) & TERM_DECFRA) && + if ((tty_get_flags(tty) & TERM_DECFRA) && !COLOUR_DEFAULT(bg)) { xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", py + 1, px + 1, py + ny, px + nx); @@ -1429,14 +1437,14 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, void tty_sync_start(struct tty *tty) { - if ((tty->term->flags|tty->term_flags) & TERM_SYNC) + if (tty_get_flags(tty) & TERM_SYNC) tty_puts(tty, "\033P=1s\033\\"); } void tty_sync_end(struct tty *tty) { - if ((tty->term->flags|tty->term_flags) & TERM_SYNC) + if (tty_get_flags(tty) & TERM_SYNC) tty_puts(tty, "\033P=2s\033\\"); } @@ -1890,7 +1898,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || - (tty->term->flags & TERM_NOXENL) || + (tty_get_flags(tty) & TERM_NOXENL) || ctx->xoff + ctx->ocx != 0 || ctx->yoff + ctx->ocy != tty->cy + 1 || tty->cx < tty->sx || @@ -1951,7 +1959,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) const struct grid_cell *gcp; /* Skip last character if terminal is stupid. */ - if ((tty->term->flags & TERM_NOXENL) && + if ((tty_get_flags(tty) & TERM_NOXENL) && tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1) return; @@ -2110,7 +2118,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || - (tty->term->flags & TERM_NOXENL) || + (tty_get_flags(tty) & TERM_NOXENL) || ctx->xoff + cx != 0 || ctx->yoff + cy != tty->cy + 1 || tty->cx < tty->sx || @@ -2447,14 +2455,14 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Is this a 24-bit colour? */ if (gc->fg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ - if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS) + if (tty_get_flags(tty) & TERM_RGBCOLOURS) return; colour_split_rgb(gc->fg, &r, &g, &b); gc->fg = colour_find_rgb(r, g, b); } /* How many colours does this terminal have? */ - if ((tty->term->flags|tty->term_flags) & TERM_256COLOURS) + if (tty_get_flags(tty) & TERM_256COLOURS) colours = 256; else colours = tty_term_number(tty->term, TTYC_COLORS); @@ -2496,14 +2504,14 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Is this a 24-bit colour? */ if (gc->bg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ - if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS) + if (tty_get_flags(tty) & TERM_RGBCOLOURS) return; colour_split_rgb(gc->bg, &r, &g, &b); gc->bg = colour_find_rgb(r, g, b); } /* How many colours does this terminal have? */ - if ((tty->term->flags|tty->term_flags) & TERM_256COLOURS) + if (tty_get_flags(tty) & TERM_256COLOURS) colours = 256; else colours = tty_term_number(tty->term, TTYC_COLORS); @@ -2564,7 +2572,7 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) /* Is this an aixterm bright colour? */ if (gc->fg >= 90 && gc->fg <= 97) { - if (tty->term_flags & TERM_256COLOURS) { + if (tty_get_flags(tty) & TERM_256COLOURS) { xsnprintf(s, sizeof s, "\033[%dm", gc->fg); tty_puts(tty, s); } else @@ -2596,7 +2604,7 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) /* Is this an aixterm bright colour? */ if (gc->bg >= 90 && gc->bg <= 97) { - if (tty->term_flags & TERM_256COLOURS) { + if (tty_get_flags(tty) & TERM_256COLOURS) { xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10); tty_puts(tty, s); } else @@ -2652,7 +2660,7 @@ tty_try_colour(struct tty *tty, int colour, const char *type) * Also if RGB is set, setaf and setab do not support the 256 * colour palette so use the sequences directly there too. */ - if ((tty->term_flags & TERM_256COLOURS) || + if ((tty_get_flags(tty) & TERM_256COLOURS) || tty_term_has(tty->term, TTYC_RGB)) goto fallback_256; @@ -2660,7 +2668,7 @@ tty_try_colour(struct tty *tty, int colour, const char *type) * If the terminfo entry has 256 colours and setaf and setab * exist, assume that they work correctly. */ - if (tty->term->flags & TERM_256COLOURS) { + if (tty_get_flags(tty) & TERM_256COLOURS) { if (*type == '3') { if (!tty_term_has(tty->term, TTYC_SETAF)) goto fallback_256; From 5ec80bd249a37147207ec2ef420086336ccf78a8 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 14:25:35 +0000 Subject: [PATCH 0175/1006] Move the UTF-8 flag to terminal flags. --- format.c | 2 +- server-client.c | 2 +- tmux.h | 3 ++- tty-acs.c | 2 +- tty.c | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/format.c b/format.c index fd3b45b9..f2387f7a 100644 --- a/format.c +++ b/format.c @@ -2569,7 +2569,7 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_prefix", "%d", 1); format_add(ft, "client_key_table", "%s", c->keytable->name); - if (tty->flags & TTY_UTF8) + if (tty_get_flags(tty) & TERM_UTF8) format_add(ft, "client_utf8", "%d", 1); else format_add(ft, "client_utf8", "%d", 0); diff --git a/server-client.c b/server-client.c index 8042da9a..1b27ae20 100644 --- a/server-client.c +++ b/server-client.c @@ -2056,7 +2056,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->fd = -1; } else { if (c->flags & CLIENT_UTF8) - c->tty.flags |= TTY_UTF8; + c->tty.term_flags |= TERM_UTF8; if (c->flags & CLIENT_256COLOURS) c->tty.term_flags |= TERM_256COLOURS; tty_resize(&c->tty); diff --git a/tmux.h b/tmux.h index dbf3244f..58840108 100644 --- a/tmux.h +++ b/tmux.h @@ -1183,6 +1183,7 @@ struct tty_term { #define TERM_DECFRA 0x8 #define TERM_RGBCOLOURS 0x10 #define TERM_SYNC 0x20 +#define TERM_UTF8 0x40 int flags; LIST_ENTRY(tty_term) entry; @@ -1235,7 +1236,7 @@ struct tty { #define TTY_NOCURSOR 0x1 #define TTY_FREEZE 0x2 #define TTY_TIMER 0x4 -#define TTY_UTF8 0x8 +/* 0x8 unused */ #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 #define TTY_FOCUS 0x40 diff --git a/tty-acs.c b/tty-acs.c index 14634120..f5352d3e 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -99,7 +99,7 @@ tty_acs_needed(struct tty *tty) tty_term_number(tty->term, TTYC_U8) == 0) return (1); - if (tty->flags & TTY_UTF8) + if (tty_get_flags(tty) & TERM_UTF8) return (0); return (1); } diff --git a/tty.c b/tty.c index a068c1a6..239d1cde 100644 --- a/tty.c +++ b/tty.c @@ -1255,7 +1255,7 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) return (gc); /* UTF-8 terminal and a UTF-8 character - fine. */ - if (tty->flags & TTY_UTF8) + if (tty_get_flags(tty) & TERM_UTF8) return (gc); /* Replace by the right number of underscores. */ From 363d950ac0c0c24f446d9ebc3421d1c38395f872 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 15:14:25 +0000 Subject: [PATCH 0176/1006] Send secondary device attributes instead of primary which gives us a bit more useful information on some terminals. --- tmux.h | 1 + tty-keys.c | 32 ++++++++++++++++++++------------ tty.c | 5 ++--- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/tmux.h b/tmux.h index 58840108..69866153 100644 --- a/tmux.h +++ b/tmux.h @@ -1261,6 +1261,7 @@ struct tty { struct event key_timer; struct tty_key *key_tree; }; +#define tty_term_flags(tty) (tty->term->flags|tty->term_flags) /* TTY command context. */ struct tty_ctx { diff --git a/tty-keys.c b/tty-keys.c index 1fcc74b8..dfea15b2 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1007,8 +1007,8 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, } /* - * Handle device attributes input. Returns 0 for success, -1 for failure, 1 for - * partial. + * Handle secondary device attributes input. Returns 0 for success, -1 for + * failure, 1 for partial. */ static int tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, @@ -1032,7 +1032,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] != '>') return (-1); if (len == 3) return (1); @@ -1048,7 +1048,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, tmp[i] = '\0'; *size = 4 + i; - /* Convert version numbers. */ + /* Convert all arguments to numbers. */ cp = tmp; while ((next = strsep(&cp, ";")) != NULL) { p[n] = strtoul(next, &endptr, 10); @@ -1059,13 +1059,20 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, /* Set terminal flags. */ switch (p[0]) { - case 64: /* VT420 */ + case 41: /* VT420 */ flags |= (TERM_DECFRA|TERM_DECSLRM); break; + case 'M': /* mintty */ + flags |= (TERM_256COLOURS|TERM_RGBCOLOURS); + break; + case 'T': /* tmux - if newer will have the DSR as well */ + flags |= (TERM_UTF8|TERM_256COLOURS); + break; + case 'U': /* rxvt-unicode */ + flags |= (TERM_UTF8); + break; } - for (i = 1; i < n; i++) - log_debug("%s: DA feature: %d", c->name, p[i]); - log_debug("%s: received DA %.*s", c->name, (int)*size, buf); + log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); tty_set_flags(tty, flags); tty->flags |= TTY_HAVEDA; @@ -1116,10 +1123,11 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, *size = 3 + i; /* Set terminal flags. */ - if (strncmp(tmp, "ITERM2 ", 7) == 0) - flags |= (TERM_DECSLRM|TERM_256COLOURS|TERM_RGBCOLOURS|TERM_SYNC); - if (strncmp(tmp, "TMUX ", 5) == 0) - flags |= (TERM_256COLOURS|TERM_RGBCOLOURS); + if (strncmp(tmp, "ITERM2 ", 7) == 0) { + flags |= (TERM_UTF8|TERM_DECSLRM|TERM_SYNC|TERM_256COLOURS| + TERM_RGBCOLOURS); + } else if (strncmp(tmp, "TMUX ", 5) == 0) + flags |= (TERM_UTF8|TERM_256COLOURS|TERM_RGBCOLOURS); log_debug("%s: received DSR %.*s", c->name, (int)*size, buf); tty_set_flags(tty, flags); diff --git a/tty.c b/tty.c index 239d1cde..06ae6d31 100644 --- a/tty.c +++ b/tty.c @@ -364,7 +364,7 @@ tty_send_requests(struct tty *tty) if (tty_term_flag(tty->term, TTYC_XT)) { if (~tty->flags & TTY_HAVEDA) - tty_puts(tty, "\033[c"); + tty_puts(tty, "\033[>c"); if (~tty->flags & TTY_HAVEDSR) tty_puts(tty, "\033[1337n"); } else @@ -1175,8 +1175,7 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, * background colour isn't default (because it doesn't work * after SGR 0). */ - if ((tty_get_flags(tty) & TERM_DECFRA) && - !COLOUR_DEFAULT(bg)) { + if ((tty_get_flags(tty) & TERM_DECFRA) && !COLOUR_DEFAULT(bg)) { xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", py + 1, px + 1, py + ny, px + nx); tty_puts(tty, tmp); From 2e347d6a384a5cfa9e18058393043e623a10c584 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 16:13:56 +0000 Subject: [PATCH 0177/1006] Only start and stop sync for operations like clear and scroll where there is a better chance more data will be on the way. --- screen-write.c | 100 +++++++++++++++++++++++++------------------------ tmux.h | 1 + 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/screen-write.c b/screen-write.c index 3e7fa66b..7a06b67c 100644 --- a/screen-write.c +++ b/screen-write.c @@ -23,8 +23,6 @@ #include "tmux.h" -static void screen_write_initctx(struct screen_write_ctx *, - struct tty_ctx *); static void screen_write_collect_clear(struct screen_write_ctx *, u_int, u_int); static void screen_write_collect_scroll(struct screen_write_ctx *); @@ -95,13 +93,36 @@ screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy) evtimer_add(&w->offset_timer, &tv); } +/* Set up context for TTY command. */ +static void +screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, + int sync) +{ + struct screen *s = ctx->s; + + memset(ttyctx, 0, sizeof *ttyctx); + + ttyctx->wp = ctx->wp; + + ttyctx->ocx = s->cx; + ttyctx->ocy = s->cy; + + ttyctx->orlower = s->rlower; + ttyctx->orupper = s->rupper; + + if (sync && !ctx->sync && ttyctx->wp != NULL) { + log_debug("%s: starting sync", __func__); + tty_write(tty_cmd_syncstart, ttyctx); + ctx->sync = 1; + } +} + /* Initialize writing with a window. */ void screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) { - struct tty_ctx ttyctx; - u_int y; + u_int y; memset(ctx, 0, sizeof *ctx); @@ -130,9 +151,6 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, screen_size_y(ctx->s)); } } - - screen_write_initctx(ctx, &ttyctx); - tty_write(tty_cmd_syncstart, &ttyctx); } /* Finish writing. */ @@ -147,8 +165,11 @@ screen_write_stop(struct screen_write_ctx *ctx) log_debug("%s: %u cells (%u written, %u skipped)", __func__, ctx->cells, ctx->written, ctx->skipped); - screen_write_initctx(ctx, &ttyctx); - tty_write(tty_cmd_syncend, &ttyctx); + if (ctx->sync) { + screen_write_initctx(ctx, &ttyctx, 0); + tty_write(tty_cmd_syncend, &ttyctx); + log_debug("%s: ending sync", __func__); + } free(ctx->item); free(ctx->list); /* flush will have emptied */ @@ -557,23 +578,6 @@ screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx, } } -/* Set up context for TTY command. */ -static void -screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) -{ - struct screen *s = ctx->s; - - memset(ttyctx, 0, sizeof *ttyctx); - - ttyctx->wp = ctx->wp; - - ttyctx->ocx = s->cx; - ttyctx->ocy = s->cy; - - ttyctx->orlower = s->rlower; - ttyctx->orupper = s->rupper; -} - /* Set a mode. */ void screen_write_mode_set(struct screen_write_ctx *ctx, int mode) @@ -732,7 +736,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) s->rupper = 0; s->rlower = screen_size_y(s) - 1; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1); tty_write(tty_cmd_alignmenttest, &ttyctx); @@ -756,7 +760,7 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (s->cx > screen_size_x(s) - 1) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.bg = bg; grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); @@ -784,7 +788,7 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (s->cx > screen_size_x(s) - 1) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.bg = bg; grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); @@ -812,7 +816,7 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (s->cx > screen_size_x(s) - 1) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.bg = bg; grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg); @@ -839,7 +843,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; grid_view_insert_lines(gd, s->cy, ny, bg); @@ -855,7 +859,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; if (s->cy < s->rupper || s->cy > s->rlower) @@ -885,7 +889,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; grid_view_delete_lines(gd, s->cy, ny, bg); @@ -901,7 +905,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; if (s->cy < s->rupper || s->cy > s->rlower) @@ -927,7 +931,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); @@ -950,7 +954,7 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); @@ -969,7 +973,7 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) struct tty_ctx ttyctx; u_int sx = screen_size_x(s); - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; if (s->cx > sx - 1) @@ -1012,7 +1016,7 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) struct screen *s = ctx->s; struct tty_ctx ttyctx; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; if (s->cy == s->rupper) @@ -1111,7 +1115,7 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) struct tty_ctx ttyctx; u_int i; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; if (lines == 0) @@ -1143,7 +1147,7 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) struct tty_ctx ttyctx; u_int sx = screen_size_x(s), sy = screen_size_y(s); - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; /* Scroll into history if it is enabled and clearing entire screen. */ @@ -1168,7 +1172,7 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) struct tty_ctx ttyctx; u_int sx = screen_size_x(s); - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; if (s->cy > 0) @@ -1191,7 +1195,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) struct tty_ctx ttyctx; u_int sx = screen_size_x(s), sy = screen_size_y(s); - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; /* Scroll into history if it is enabled. */ @@ -1267,7 +1271,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) if (ctx->scrolled > s->rlower - s->rupper + 1) ctx->scrolled = s->rlower - s->rupper + 1; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.num = ctx->scrolled; ttyctx.bg = ctx->bg; tty_write(tty_cmd_scrollup, &ttyctx); @@ -1282,7 +1286,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) for (y = 0; y < screen_size_y(s); y++) { TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { screen_write_set_cursor(ctx, ci->x, y); - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = &ci->gc; ttyctx.wrapped = ci->wrapped; ttyctx.ptr = ci->data; @@ -1425,7 +1429,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { cx = s->cx; cy = s->cy; screen_write_set_cursor(ctx, xx, s->cy); - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); s->cx = cx; s->cy = cy; @@ -1459,7 +1463,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) /* Sanity check cursor position. */ if (s->cx > sx - width || s->cy > sy - 1) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); /* Handle overwriting of UTF-8 characters. */ gl = grid_get_line(s->grid, s->grid->hsize + s->cy); @@ -1662,7 +1666,7 @@ screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) { struct tty_ctx ttyctx; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.ptr = str; ttyctx.num = len; @@ -1675,7 +1679,7 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) { struct tty_ctx ttyctx; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.ptr = str; ttyctx.num = len; diff --git a/tmux.h b/tmux.h index 69866153..a5dead04 100644 --- a/tmux.h +++ b/tmux.h @@ -777,6 +777,7 @@ struct screen_write_collect_line; struct screen_write_ctx { struct window_pane *wp; struct screen *s; + int sync; struct screen_write_collect_item *item; struct screen_write_collect_line *list; From 9311ed049b2dba24d562e34ae17af0f5e316784e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 17:20:23 +0000 Subject: [PATCH 0178/1006] Start menu with top item selected if no mouse, GitHub issue 2169. --- menu.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/menu.c b/menu.c index 941d4123..39cb50c7 100644 --- a/menu.c +++ b/menu.c @@ -298,6 +298,8 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, void *data) { struct menu_data *md; + u_int i; + const char *name; if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) return (-1); @@ -318,7 +320,18 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, md->py = py; md->menu = menu; - md->choice = -1; + if (md->flags & MENU_NOMOUSE) { + for (i = 0; i < menu->count; i++) { + name = menu->items[i].name; + if (name != NULL && *name != '-') + break; + } + if (i != menu->count) + md->choice = i; + else + md->choice = -1; + } else + md->choice = -1; md->cb = cb; md->data = data; From c1b015f24ed2ea93f91ae082b31be3c92b463a2f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 17:24:28 +0000 Subject: [PATCH 0179/1006] Log what caused a flush for better visibility on what could be improved. --- screen-write.c | 56 +++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/screen-write.c b/screen-write.c index 7a06b67c..aa7bd76e 100644 --- a/screen-write.c +++ b/screen-write.c @@ -26,7 +26,8 @@ static void screen_write_collect_clear(struct screen_write_ctx *, u_int, u_int); static void screen_write_collect_scroll(struct screen_write_ctx *); -static void screen_write_collect_flush(struct screen_write_ctx *, int); +static void screen_write_collect_flush(struct screen_write_ctx *, int, + const char *); static int screen_write_overwrite(struct screen_write_ctx *, struct grid_cell *, u_int); @@ -160,7 +161,7 @@ screen_write_stop(struct screen_write_ctx *ctx) struct tty_ctx ttyctx; screen_write_collect_end(ctx); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); log_debug("%s: %u cells (%u written, %u skipped)", __func__, ctx->cells, ctx->written, ctx->skipped); @@ -765,7 +766,7 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = nx; tty_write(tty_cmd_insertcharacter, &ttyctx); } @@ -793,7 +794,7 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = nx; tty_write(tty_cmd_deletecharacter, &ttyctx); } @@ -821,7 +822,7 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = nx; tty_write(tty_cmd_clearcharacter, &ttyctx); } @@ -848,7 +849,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) grid_view_insert_lines(gd, s->cy, ny, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = ny; tty_write(tty_cmd_insertline, &ttyctx); return; @@ -867,7 +868,8 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) else grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); + ttyctx.num = ny; tty_write(tty_cmd_insertline, &ttyctx); } @@ -894,7 +896,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) grid_view_delete_lines(gd, s->cy, ny, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = ny; tty_write(tty_cmd_deleteline, &ttyctx); return; @@ -913,7 +915,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) else grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = ny; tty_write(tty_cmd_deleteline, &ttyctx); } @@ -937,7 +939,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); screen_write_collect_clear(ctx, s->cy, 1); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); tty_write(tty_cmd_clearline, &ttyctx); } @@ -961,7 +963,7 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) if (s->cx == 0) screen_write_collect_clear(ctx, s->cy, 1); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); tty_write(tty_cmd_clearendofline, &ttyctx); } @@ -983,7 +985,7 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) if (s->cx > sx - 1) screen_write_collect_clear(ctx, s->cy, 1); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); tty_write(tty_cmd_clearstartofline, &ttyctx); } @@ -1024,7 +1026,7 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) else if (s->cy > 0) screen_write_set_cursor(ctx, -1, s->cy - 1); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); tty_write(tty_cmd_reverseindex, &ttyctx); } @@ -1042,7 +1044,7 @@ screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, if (rupper >= rlower) /* cannot be one line */ return; - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); /* Cursor moves to top-left. */ screen_write_set_cursor(ctx, 0, 0); @@ -1069,7 +1071,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) s->rupper, s->rlower); if (bg != ctx->bg) { - screen_write_collect_flush(ctx, 1); + screen_write_collect_flush(ctx, 1, __func__); ctx->bg = bg; } @@ -1095,7 +1097,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) lines = s->rlower - s->rupper + 1; if (bg != ctx->bg) { - screen_write_collect_flush(ctx, 1); + screen_write_collect_flush(ctx, 1, __func__); ctx->bg = bg; } @@ -1126,7 +1128,7 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) for (i = 0; i < lines; i++) grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = lines; tty_write(tty_cmd_scrolldown, &ttyctx); } @@ -1160,7 +1162,7 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) } screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); tty_write(tty_cmd_clearendofscreen, &ttyctx); } @@ -1183,7 +1185,7 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); screen_write_collect_clear(ctx, 0, s->cy); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); tty_write(tty_cmd_clearstartofscreen, &ttyctx); } @@ -1257,7 +1259,8 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx) /* Flush collected lines. */ static void -screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) +screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, + const char *from) { struct screen *s = ctx->s; struct screen_write_collect_item *ci, *tmp; @@ -1302,7 +1305,8 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) } s->cx = cx; s->cy = cy; - log_debug("%s: flushed %u items (%zu bytes)", __func__, items, written); + log_debug("%s: flushed %u items (%zu bytes) (%s)", __func__, items, + written, from); ctx->written += written; } @@ -1380,7 +1384,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx, collect = 0; if (!collect) { screen_write_collect_end(ctx); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); screen_write_cell(ctx, gc); return; } @@ -1425,7 +1429,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) /* If the width is zero, combine onto the previous character. */ if (width == 0) { - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { cx = s->cx; cy = s->cy; screen_write_set_cursor(ctx, xx, s->cy); @@ -1438,7 +1442,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) } /* Flush any existing scrolling. */ - screen_write_collect_flush(ctx, 1); + screen_write_collect_flush(ctx, 1, __func__); /* If this character doesn't fit, ignore it. */ if ((~s->mode & MODE_WRAP) && @@ -1457,7 +1461,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); screen_write_linefeed(ctx, 1, 8); screen_write_set_cursor(ctx, 0, -1); - screen_write_collect_flush(ctx, 1); + screen_write_collect_flush(ctx, 1, __func__); } /* Sanity check cursor position. */ @@ -1535,7 +1539,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) /* Create space for character in insert mode. */ if (s->mode & MODE_INSERT) { - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = width; tty_write(tty_cmd_insertcharacter, &ttyctx); } From d90ca7ecd61ee2f1376801acd61252c03caef3ca Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 20:32:51 +0000 Subject: [PATCH 0180/1006] Collect up line clears like text within the available data so we don't need to flush everything. --- screen-write.c | 207 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 167 insertions(+), 40 deletions(-) diff --git a/screen-write.c b/screen-write.c index aa7bd76e..8a2a19e3 100644 --- a/screen-write.c +++ b/screen-write.c @@ -25,6 +25,10 @@ static void screen_write_collect_clear(struct screen_write_ctx *, u_int, u_int); +static int screen_write_collect_clear_end(struct screen_write_ctx *, u_int, + u_int, u_int); +static int screen_write_collect_clear_start(struct screen_write_ctx *, u_int, + u_int, u_int); static void screen_write_collect_scroll(struct screen_write_ctx *); static void screen_write_collect_flush(struct screen_write_ctx *, int, const char *); @@ -42,8 +46,12 @@ struct screen_write_collect_item { u_int x; int wrapped; + enum { TEXT, CLEAR, CLEAR_END, CLEAR_START } type; u_int used; - char data[256]; + union { + u_int bg; + char data[256]; + }; struct grid_cell gc; @@ -924,33 +932,39 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) void screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) { - struct screen *s = ctx->s; - struct grid_line *gl; - struct tty_ctx ttyctx; - u_int sx = screen_size_x(s); + struct screen *s = ctx->s; + struct grid_line *gl; + u_int sx = screen_size_x(s); + struct screen_write_collect_item *ci = ctx->item; gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) return; - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = bg; - grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); screen_write_collect_clear(ctx, s->cy, 1); - screen_write_collect_flush(ctx, 0, __func__); - tty_write(tty_cmd_clearline, &ttyctx); + ci->x = 0; + ci->type = CLEAR; + ci->bg = bg; + TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); + ctx->item = xcalloc(1, sizeof *ctx->item); } /* Clear to end of line from cursor. */ void screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) { - struct screen *s = ctx->s; - struct grid_line *gl; - struct tty_ctx ttyctx; - u_int sx = screen_size_x(s); + struct screen *s = ctx->s; + struct grid_line *gl; + struct tty_ctx ttyctx; + u_int sx = screen_size_x(s); + struct screen_write_collect_item *ci = ctx->item; + + if (s->cx == 0) { + screen_write_clearline(ctx, bg); + return; + } gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) @@ -961,19 +975,28 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); - if (s->cx == 0) - screen_write_collect_clear(ctx, s->cy, 1); - screen_write_collect_flush(ctx, 0, __func__); - tty_write(tty_cmd_clearendofline, &ttyctx); + if (!screen_write_collect_clear_end(ctx, s->cy, s->cx, bg)) { + ci->x = s->cx; + ci->type = CLEAR_END; + ci->bg = bg; + TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); + ctx->item = xcalloc(1, sizeof *ctx->item); + } } /* Clear to start of line from cursor. */ void screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) { - struct screen *s = ctx->s; - struct tty_ctx ttyctx; - u_int sx = screen_size_x(s); + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + u_int sx = screen_size_x(s); + struct screen_write_collect_item *ci = ctx->item; + + if (s->cx >= sx - 1) { + screen_write_clearline(ctx, bg); + return; + } screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; @@ -983,10 +1006,13 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) else grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); - if (s->cx > sx - 1) - screen_write_collect_clear(ctx, s->cy, 1); - screen_write_collect_flush(ctx, 0, __func__); - tty_write(tty_cmd_clearstartofline, &ttyctx); + if (!screen_write_collect_clear_start(ctx, s->cy, s->cx, bg)) { + ci->x = s->cx; + ci->type = CLEAR_START; + ci->bg = bg; + TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); + ctx->item = xcalloc(1, sizeof *ctx->item); + } } /* Move cursor to px,py. */ @@ -1018,16 +1044,17 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) struct screen *s = ctx->s; struct tty_ctx ttyctx; - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = bg; - - if (s->cy == s->rupper) + if (s->cy == s->rupper) { grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); - else if (s->cy > 0) + screen_write_collect_flush(ctx, 0, __func__); + + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.bg = bg; + + tty_write(tty_cmd_reverseindex, &ttyctx); + } else if (s->cy > 0) screen_write_set_cursor(ctx, -1, s->cy - 1); - screen_write_collect_flush(ctx, 0, __func__); - tty_write(tty_cmd_reverseindex, &ttyctx); } /* Set scroll region. */ @@ -1217,25 +1244,114 @@ screen_write_clearhistory(struct screen_write_ctx *ctx) grid_clear_history(ctx->s->grid); } -/* Clear a collected line. */ +/* Clear to start of a collected line. */ +static int +screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x, + u_int bg) +{ + struct screen_write_collect_item *ci, *tmp; + size_t size = 0; + u_int items = 0; + int redundant = 0; + + if (TAILQ_EMPTY(&ctx->list[y].items)) + return (0); + TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { + switch (ci->type) { + case CLEAR: + continue; + case CLEAR_START: + if (ci->x >= x) { + if (ci->bg == bg) + redundant = 1; + continue; + } + break; + case CLEAR_END: + if (ci->x <= x) + ci->x = x; + continue; + case TEXT: + if (ci->x > x) + continue; + break; + } + items++; + size += ci->used; + TAILQ_REMOVE(&ctx->list[y].items, ci, entry); + free(ci); + } + ctx->skipped += size; + log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items, + size, y); + return (redundant); +} + +/* Clear to end of a collected line. */ +static int +screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x, + u_int bg) +{ + struct screen_write_collect_item *ci, *tmp; + size_t size = 0; + int redundant = 0; + u_int items = 0; + + if (TAILQ_EMPTY(&ctx->list[y].items)) + return (0); + TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { + switch (ci->type) { + case CLEAR: + continue; + case CLEAR_START: + if (ci->x >= x) + ci->x = x; + continue; + case CLEAR_END: + if (ci->x <= x) { + if (ci->bg == bg) + redundant = 1; + continue; + } + break; + case TEXT: + if (ci->x < x) + continue; + break; + } + items++; + size += ci->used; + TAILQ_REMOVE(&ctx->list[y].items, ci, entry); + free(ci); + } + ctx->skipped += size; + log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items, + size, y); + return (redundant); +} + +/* Clear part of a collected line. */ static void screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) { struct screen_write_collect_item *ci, *tmp; - u_int i; + u_int i, items; size_t size; for (i = y; i < y + n; i++) { if (TAILQ_EMPTY(&ctx->list[i].items)) continue; + items = 0; size = 0; TAILQ_FOREACH_SAFE(ci, &ctx->list[i].items, entry, tmp) { + items++; size += ci->used; TAILQ_REMOVE(&ctx->list[i].items, ci, entry); free(ci); } ctx->skipped += size; - log_debug("%s: dropped %zu bytes (line %u)", __func__, size, i); + log_debug("%s: dropped %u items (%zu bytes) (line %u)", + __func__, items, size, y); } } @@ -1290,11 +1406,22 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { screen_write_set_cursor(ctx, ci->x, y); screen_write_initctx(ctx, &ttyctx, 0); - ttyctx.cell = &ci->gc; - ttyctx.wrapped = ci->wrapped; - ttyctx.ptr = ci->data; - ttyctx.num = ci->used; - tty_write(tty_cmd_cells, &ttyctx); + if (ci->type == CLEAR) { + ttyctx.bg = ci->bg; + tty_write(tty_cmd_clearline, &ttyctx); + } else if (ci->type == CLEAR_END) { + ttyctx.bg = ci->bg; + tty_write(tty_cmd_clearendofline, &ttyctx); + } else if (ci->type == CLEAR_START) { + ttyctx.bg = ci->bg; + tty_write(tty_cmd_clearstartofline, &ttyctx); + } else { + ttyctx.cell = &ci->gc; + ttyctx.wrapped = ci->wrapped; + ttyctx.ptr = ci->data; + ttyctx.num = ci->used; + tty_write(tty_cmd_cells, &ttyctx); + } items++; written += ci->used; From d8433add47635348808cfd777bf6c7d90bc97d56 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 21:16:24 +0000 Subject: [PATCH 0181/1006] Do not need to set up a tty context for clearing lines now. --- screen-write.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/screen-write.c b/screen-write.c index 8a2a19e3..bc12b7cd 100644 --- a/screen-write.c +++ b/screen-write.c @@ -957,7 +957,6 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; struct grid_line *gl; - struct tty_ctx ttyctx; u_int sx = screen_size_x(s); struct screen_write_collect_item *ci = ctx->item; @@ -970,9 +969,6 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) return; - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = bg; - grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); if (!screen_write_collect_clear_end(ctx, s->cy, s->cx, bg)) { @@ -989,7 +985,6 @@ void screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; - struct tty_ctx ttyctx; u_int sx = screen_size_x(s); struct screen_write_collect_item *ci = ctx->item; @@ -998,9 +993,6 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) return; } - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = bg; - if (s->cx > sx - 1) grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); else From 5f18844b3251451c1c9d4a983c1246a03358b886 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Apr 2020 21:46:43 +0000 Subject: [PATCH 0182/1006] Return to sending sync around clears. --- screen-write.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/screen-write.c b/screen-write.c index bc12b7cd..ff7b12de 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1397,17 +1397,20 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, for (y = 0; y < screen_size_y(s); y++) { TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { screen_write_set_cursor(ctx, ci->x, y); - screen_write_initctx(ctx, &ttyctx, 0); if (ci->type == CLEAR) { + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; tty_write(tty_cmd_clearline, &ttyctx); } else if (ci->type == CLEAR_END) { + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; tty_write(tty_cmd_clearendofline, &ttyctx); } else if (ci->type == CLEAR_START) { + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; tty_write(tty_cmd_clearstartofline, &ttyctx); } else { + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = &ci->gc; ttyctx.wrapped = ci->wrapped; ttyctx.ptr = ci->data; From 5aba26f2cb7aa9609a3c3d2bd38a3942b6a378b8 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Apr 2020 08:03:22 +0000 Subject: [PATCH 0183/1006] Add a copy-command option and change copy-pipe and friends to pipe to it if used without arguments, allows all copy key bindings to be changed to pipe with one option. --- key-bindings.c | 24 ++++++++++++------------ options-table.c | 6 ++++++ tmux.1 | 10 +++++++--- window-copy.c | 22 +++++++++++++--------- 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index 09f0e0b1..09a4eafa 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -333,10 +333,10 @@ key_bindings_init(void) "bind -n MouseDown2Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { paste -p }", /* Mouse button 1 double click on pane. */ - "bind -n DoubleClick1Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-word; run -d0.3; send -X copy-selection-and-cancel }", + "bind -n DoubleClick1Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-word; run -d0.3; send -X copy-pipe-and-cancel }", /* Mouse button 1 triple click on pane. */ - "bind -n TripleClick1Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-selection-and-cancel }", + "bind -n TripleClick1Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel }", /* Mouse button 1 drag on border. */ "bind -n MouseDrag1Border resize-pane -M", @@ -374,7 +374,7 @@ key_bindings_init(void) "bind -Tcopy-mode C-r command-prompt -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", "bind -Tcopy-mode C-s command-prompt -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'", "bind -Tcopy-mode C-v send -X page-down", - "bind -Tcopy-mode C-w send -X copy-selection-and-cancel", + "bind -Tcopy-mode C-w send -X copy-pipe-and-cancel", "bind -Tcopy-mode Escape send -X cancel", "bind -Tcopy-mode Space send -X page-down", "bind -Tcopy-mode , send -X jump-reverse", @@ -393,11 +393,11 @@ key_bindings_init(void) "bind -Tcopy-mode End send -X end-of-line", "bind -Tcopy-mode MouseDown1Pane select-pane", "bind -Tcopy-mode MouseDrag1Pane select-pane\\; send -X begin-selection", - "bind -Tcopy-mode MouseDragEnd1Pane send -X copy-selection-and-cancel", + "bind -Tcopy-mode MouseDragEnd1Pane send -X copy-pipe-and-cancel", "bind -Tcopy-mode WheelUpPane select-pane\\; send -N5 -X scroll-up", "bind -Tcopy-mode WheelDownPane select-pane\\; send -N5 -X scroll-down", - "bind -Tcopy-mode DoubleClick1Pane select-pane\\; send -X select-word\\; run -d0.3\\; send -X copy-selection-and-cancel", - "bind -Tcopy-mode TripleClick1Pane select-pane\\; send -X select-line\\; run -d0.3\\; send -X copy-selection-and-cancel", + "bind -Tcopy-mode DoubleClick1Pane select-pane\\; send -X select-word\\; run -d0.3\\; send -X copy-pipe-and-cancel", + "bind -Tcopy-mode TripleClick1Pane select-pane\\; send -X select-line\\; run -d0.3\\; send -X copy-pipe-and-cancel", "bind -Tcopy-mode NPage send -X page-down", "bind -Tcopy-mode PPage send -X page-up", "bind -Tcopy-mode Up send -X cursor-up", @@ -423,7 +423,7 @@ key_bindings_init(void) "bind -Tcopy-mode M-m send -X back-to-indentation", "bind -Tcopy-mode M-r send -X middle-line", "bind -Tcopy-mode M-v send -X page-up", - "bind -Tcopy-mode M-w send -X copy-selection-and-cancel", + "bind -Tcopy-mode M-w send -X copy-pipe-and-cancel", "bind -Tcopy-mode 'M-{' send -X previous-paragraph", "bind -Tcopy-mode 'M-}' send -X next-paragraph", "bind -Tcopy-mode M-Up send -X halfpage-up", @@ -440,8 +440,8 @@ key_bindings_init(void) "bind -Tcopy-mode-vi C-b send -X page-up", "bind -Tcopy-mode-vi C-f send -X page-down", "bind -Tcopy-mode-vi C-h send -X cursor-left", - "bind -Tcopy-mode-vi C-j send -X copy-selection-and-cancel", - "bind -Tcopy-mode-vi Enter send -X copy-selection-and-cancel", + "bind -Tcopy-mode-vi C-j send -X copy-pipe-and-cancel", + "bind -Tcopy-mode-vi Enter send -X copy-pipe-and-cancel", "bind -Tcopy-mode-vi C-u send -X halfpage-up", "bind -Tcopy-mode-vi C-v send -X rectangle-toggle", "bind -Tcopy-mode-vi C-y send -X scroll-up", @@ -499,11 +499,11 @@ key_bindings_init(void) "bind -Tcopy-mode-vi % send -X next-matching-bracket", "bind -Tcopy-mode-vi MouseDown1Pane select-pane", "bind -Tcopy-mode-vi MouseDrag1Pane select-pane\\; send -X begin-selection", - "bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel", + "bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-pipe-and-cancel", "bind -Tcopy-mode-vi WheelUpPane select-pane\\; send -N5 -X scroll-up", "bind -Tcopy-mode-vi WheelDownPane select-pane\\; send -N5 -X scroll-down", - "bind -Tcopy-mode-vi DoubleClick1Pane select-pane\\; send -X select-word\\; run -d0.3\\; send -X copy-selection-and-cancel", - "bind -Tcopy-mode-vi TripleClick1Pane select-pane\\; send -X select-line\\; run -d0.3\\; send -X copy-selection-and-cancel", + "bind -Tcopy-mode-vi DoubleClick1Pane select-pane\\; send -X select-word\\; run -d0.3\\; send -X copy-pipe-and-cancel", + "bind -Tcopy-mode-vi TripleClick1Pane select-pane\\; send -X select-line\\; run -d0.3\\; send -X copy-pipe-and-cancel", "bind -Tcopy-mode-vi BSpace send -X cursor-left", "bind -Tcopy-mode-vi NPage send -X page-down", "bind -Tcopy-mode-vi PPage send -X page-up", diff --git a/options-table.c b/options-table.c index 33ee4402..1401f05d 100644 --- a/options-table.c +++ b/options-table.c @@ -198,6 +198,12 @@ const struct options_table_entry options_table[] = { .separator = "," }, + { .name = "copy-command", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, diff --git a/tmux.1 b/tmux.1 index fd022c9e..28c8c9c3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1483,9 +1483,9 @@ The following commands are supported in copy mode: .It Li "clear-selection" Ta "Escape" Ta "C-g" .It Li "copy-end-of-line []" Ta "D" Ta "C-k" .It Li "copy-line []" Ta "" Ta "" -.It Li "copy-pipe []" Ta "" Ta "" -.It Li "copy-pipe-no-clear []" Ta "" Ta "" -.It Li "copy-pipe-and-cancel []" Ta "" Ta "" +.It Li "copy-pipe [] []" Ta "" Ta "" +.It Li "copy-pipe-no-clear [] []" Ta "" Ta "" +.It Li "copy-pipe-and-cancel [] []" Ta "" Ta "" .It Li "copy-selection []" Ta "" Ta "" .It Li "copy-selection-no-clear []" Ta "" Ta "" .It Li "copy-selection-and-cancel []" Ta "Enter" Ta "M-w" @@ -3090,6 +3090,10 @@ be set to .Ql screen , .Ql tmux or a derivative of them. +.It Ic copy-command Ar shell-command +Give the command to pipe to if the +.Ic copy-pipe +copy mode command is used without arguments. .It Ic escape-time Ar time Set the time in milliseconds for which .Nm diff --git a/window-copy.c b/window-copy.c index 8364ef11..bdf2c148 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1728,11 +1728,10 @@ window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs) if (cs->args->argc == 3) prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp); - if (s != NULL && *cs->args->argv[1] != '\0') { + if (s != NULL && cs->args->argc > 1 && *cs->args->argv[1] != '\0') command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); - window_copy_copy_pipe(wme, s, prefix, command); - free(command); - } + window_copy_copy_pipe(wme, s, prefix, command); + free(command); free(prefix); return (WINDOW_COPY_CMD_NOTHING); @@ -2066,11 +2065,11 @@ static const struct { window_copy_cmd_copy_end_of_line }, { "copy-line", 0, 1, 0, window_copy_cmd_copy_line }, - { "copy-pipe-no-clear", 1, 2, 0, + { "copy-pipe-no-clear", 0, 2, 0, window_copy_cmd_copy_pipe_no_clear }, - { "copy-pipe", 1, 2, 0, + { "copy-pipe", 0, 2, 0, window_copy_cmd_copy_pipe }, - { "copy-pipe-and-cancel", 1, 2, 0, + { "copy-pipe-and-cancel", 0, 2, 0, window_copy_cmd_copy_pipe_and_cancel }, { "copy-selection-no-clear", 0, 1, 0, window_copy_cmd_copy_selection_no_clear }, @@ -3471,8 +3470,13 @@ window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, if (buf == NULL) return; - job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT, -1, -1); - bufferevent_write(job_get_event(job), buf, len); + if (cmd == NULL || *cmd == '\0') + cmd = options_get_string(global_options, "copy-command"); + if (cmd != NULL && *cmd != '\0') { + job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT, + -1, -1); + bufferevent_write(job_get_event(job), buf, len); + } window_copy_copy_buffer(wme, prefix, buf, len); } From 7f2925a01de5da0416dd2589ee080f61650f2da9 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Apr 2020 09:06:10 +0000 Subject: [PATCH 0184/1006] Support the application escape sequence mintty (means tmux doesn't have to delay to wait for Escape). --- tty-keys.c | 5 ++++- tty.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index dfea15b2..10c9c670 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -61,6 +61,9 @@ struct tty_default_key_raw { key_code key; }; static const struct tty_default_key_raw tty_default_raw_keys[] = { + /* Application escape. */ + { "\033O[", '\033' }, + /* * Numeric keypad. Just use the vt100 escape sequences here and always * put the terminal into keypad_xmit mode. Translation of numbers @@ -1065,7 +1068,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, case 'M': /* mintty */ flags |= (TERM_256COLOURS|TERM_RGBCOLOURS); break; - case 'T': /* tmux - if newer will have the DSR as well */ + case 'T': /* tmux - new versons reply to DSR which will set RGB */ flags |= (TERM_UTF8|TERM_256COLOURS); break; case 'U': /* rxvt-unicode */ diff --git a/tty.c b/tty.c index 06ae6d31..4090a115 100644 --- a/tty.c +++ b/tty.c @@ -340,6 +340,7 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } + tty_puts(tty, "\033[?7727h"); } evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); @@ -426,6 +427,7 @@ tty_stop_tty(struct tty *tty) tty->flags &= ~TTY_FOCUS; tty_raw(tty, "\033[?1004l"); } + tty_raw(tty, "\033[?7727l"); } if (tty_use_margin(tty)) From 282a7a8d96877d4063fe16c5fbba03d95bc35008 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Apr 2020 14:06:42 +0000 Subject: [PATCH 0185/1006] Make sure the cursor position is still on screen after we have trimmed empty lines. Also improve some log messages. --- screen.c | 7 +++++-- tmux.h | 2 +- window-copy.c | 12 ++++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/screen.c b/screen.c index ff8b1d2e..d9d1aa09 100644 --- a/screen.c +++ b/screen.c @@ -230,7 +230,9 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, cy = т *cy = s->grid->hsize + s->cy; - log_debug("%s: start %u,%u (%u,%u)", __func__, s->cx, s->cy, *cx, *cy); + log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)", + __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy, + *cx, *cy); if (sx < 1) sx = 1; @@ -256,7 +258,8 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, s->cx = 0; s->cy = 0; } - log_debug("%s: finish %u,%u (%u,%u)", __func__, s->cx, s->cy, *cx, *cy); + log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx, + s->cy, *cx, *cy); } /* Resize screen. */ diff --git a/tmux.h b/tmux.h index a5dead04..b791f5af 100644 --- a/tmux.h +++ b/tmux.h @@ -79,7 +79,7 @@ struct winlink; #define NAME_INTERVAL 500000 /* Maximum size of data to hold from a pane. */ -#define READ_SIZE 4096 +#define READ_SIZE 8192 /* Default pixel cell sizes. */ #define DEFAULT_XPIXEL 16 diff --git a/window-copy.c b/window-copy.c index bdf2c148..45a47675 100644 --- a/window-copy.c +++ b/window-copy.c @@ -314,14 +314,22 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, break; sy--; } + log_debug("%s: target screen is %ux%u, source %ux%u", __func__, + screen_size_x(src), sy, screen_size_x(hint), + screen_hsize(src) + screen_size_y(src)); screen_init(dst, screen_size_x(src), sy, screen_hlimit(src)); grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy); dst->grid->sy = sy - screen_hsize(src); dst->grid->hsize = screen_hsize(src); dst->grid->hscrolled = src->grid->hscrolled; - dst->cx = src->cx; - dst->cy = src->cy; + if (src->cy > dst->grid->sy - 1) { + dst->cx = 0; + dst->cy = dst->grid->sy - 1; + } else { + dst->cx = src->cx; + dst->cy = src->cy; + } screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1, 0, cx, cy); From bbd6e899a8618229f27f04e81a95163968a7b905 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Apr 2020 15:44:58 +0000 Subject: [PATCH 0186/1006] There is no point allocating a new item and putting it on the list when the whole line is cleared line, there is never any point in doing it more than once. Instead store the background colour alone. --- screen-write.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/screen-write.c b/screen-write.c index ff7b12de..bd756ce3 100644 --- a/screen-write.c +++ b/screen-write.c @@ -46,7 +46,7 @@ struct screen_write_collect_item { u_int x; int wrapped; - enum { TEXT, CLEAR, CLEAR_END, CLEAR_START } type; + enum { TEXT, CLEAR_END, CLEAR_START } type; u_int used; union { u_int bg; @@ -58,6 +58,7 @@ struct screen_write_collect_item { TAILQ_ENTRY(screen_write_collect_item) entry; }; struct screen_write_collect_line { + u_int bg; TAILQ_HEAD(, screen_write_collect_item) items; }; @@ -932,10 +933,9 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) void screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) { - struct screen *s = ctx->s; - struct grid_line *gl; - u_int sx = screen_size_x(s); - struct screen_write_collect_item *ci = ctx->item; + struct screen *s = ctx->s; + struct grid_line *gl; + u_int sx = screen_size_x(s); gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) @@ -944,11 +944,8 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); screen_write_collect_clear(ctx, s->cy, 1); - ci->x = 0; - ci->type = CLEAR; - ci->bg = bg; - TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); - ctx->item = xcalloc(1, sizeof *ctx->item); + ctx->list[s->cy].bg = 1 + bg; + ctx->item->used = 0; } /* Clear to end of line from cursor. */ @@ -1250,8 +1247,6 @@ screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x, return (0); TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { switch (ci->type) { - case CLEAR: - continue; case CLEAR_START: if (ci->x >= x) { if (ci->bg == bg) @@ -1293,8 +1288,6 @@ screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x, return (0); TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { switch (ci->type) { - case CLEAR: - continue; case CLEAR_START: if (ci->x >= x) ci->x = x; @@ -1395,13 +1388,15 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, cx = s->cx; cy = s->cy; for (y = 0; y < screen_size_y(s); y++) { + if (ctx->list[y].bg != 0) { + screen_write_set_cursor(ctx, 0, y); + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.bg = ctx->list[y].bg - 1; + tty_write(tty_cmd_clearline, &ttyctx); + } TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { screen_write_set_cursor(ctx, ci->x, y); - if (ci->type == CLEAR) { - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = ci->bg; - tty_write(tty_cmd_clearline, &ttyctx); - } else if (ci->type == CLEAR_END) { + if (ci->type == CLEAR_END) { screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; tty_write(tty_cmd_clearendofline, &ttyctx); From a877a5d8c96f317cb8c496f0c9afa0304be926a6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Apr 2020 21:33:18 +0000 Subject: [PATCH 0187/1006] Do not move the cursor to the existing y position if it is invalid, go home instead. --- tty.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tty.c b/tty.c index 4090a115..9c03c08a 100644 --- a/tty.c +++ b/tty.c @@ -1739,7 +1739,10 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) for (i = 0; i < ctx->num; i++) tty_putc(tty, '\n'); } else { - tty_cursor(tty, 0, tty->cy); + if (tty->cy == UINT_MAX) + tty_cursor(tty, 0, 0); + else + tty_cursor(tty, 0, tty->cy); tty_putcode1(tty, TTYC_INDN, ctx->num); } } @@ -2063,8 +2066,12 @@ tty_region(struct tty *tty, u_int rupper, u_int rlower) * flag so further output causes a line feed). As a workaround, do an * explicit move to 0 first. */ - if (tty->cx >= tty->sx) - tty_cursor(tty, 0, tty->cy); + if (tty->cx >= tty->sx) { + if (tty->cy == UINT_MAX) + tty_cursor(tty, 0, 0); + else + tty_cursor(tty, 0, tty->cy); + } tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower); tty->cx = tty->cy = UINT_MAX; From a7a9460d2790161f7bb60c4047acf32d3aa93ed9 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Apr 2020 22:16:28 +0000 Subject: [PATCH 0188/1006] Set mode properly before and after redrawing, and don't bother calculating cursor position if it won't be used. --- server-client.c | 8 +++++--- tty.c | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/server-client.c b/server-client.c index 1b27ae20..8365831f 100644 --- a/server-client.c +++ b/server-client.c @@ -1681,7 +1681,7 @@ server_client_check_redraw(struct client *c) struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; - int needed, flags; + int needed, flags, mode = tty->mode; struct timeval tv = { .tv_usec = 1000 }; static struct event ev; size_t left; @@ -1732,6 +1732,7 @@ server_client_check_redraw(struct client *c) flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR); tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE)) | TTY_NOCURSOR; + tty_update_mode(tty, mode, NULL); if (~c->flags & CLIENT_REDRAWWINDOW) { /* @@ -1752,8 +1753,9 @@ server_client_check_redraw(struct client *c) screen_redraw_screen(c); } - tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags; - tty_update_mode(tty, tty->mode, NULL); + tty->flags = (tty->flags & ~TTY_NOCURSOR) | (flags & TTY_NOCURSOR); + tty_update_mode(tty, mode, NULL); + tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR)) | flags; c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_STATUSFORCE); diff --git a/tty.c b/tty.c index 9c03c08a..82436959 100644 --- a/tty.c +++ b/tty.c @@ -2151,6 +2151,9 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy) u_int thisx, thisy; int change; + if (tty->flags & TTY_BLOCK) + return; + if (cx > tty->sx - 1) cx = tty->sx - 1; From 5289d4ed13e18fa4430aba27af0d525d2f76fc30 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 06:10:15 +0000 Subject: [PATCH 0189/1006] When a redraw is deferred because the terminal hasn't finished reading the data from the last one, other panes could update while waiting, so we set the flag to redraw them all when the new redraw actually happened. But this means a lot of redrawing panes unnecessarily if they haven't changed - so instead set a flag to say "at least one pane needs to be redrawed" then look at the invidual pane flags to see which ones need it. --- screen-redraw.c | 13 ++++++++++--- server-client.c | 18 +++++++++--------- tmux.h | 5 ++++- tty.c | 16 ++++++++++++++-- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index c510fb68..c9e70590 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -438,17 +438,24 @@ screen_redraw_screen(struct client *c) tty_sync_start(&c->tty); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { + log_debug("%s: redrawing borders", c->name); if (ctx.pane_status != PANE_STATUS_OFF) screen_redraw_draw_pane_status(&ctx); screen_redraw_draw_borders(&ctx); } - if (flags & CLIENT_REDRAWWINDOW) + if (flags & CLIENT_REDRAWWINDOW) { + log_debug("%s: redrawing panes", c->name); screen_redraw_draw_panes(&ctx); + } if (ctx.statuslines != 0 && - (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) + (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) { + log_debug("%s: redrawing status", c->name); screen_redraw_draw_status(&ctx); - if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) + } + if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) { + log_debug("%s: redrawing overlay", c->name); c->overlay_draw(c, &ctx); + } tty_reset(&c->tty); tty_sync_end(&c->tty); diff --git a/server-client.c b/server-client.c index 8365831f..a045c44b 100644 --- a/server-client.c +++ b/server-client.c @@ -1681,7 +1681,7 @@ server_client_check_redraw(struct client *c) struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; - int needed, flags, mode = tty->mode; + int needed, flags, mode = tty->mode, new_flags = 0; struct timeval tv = { .tv_usec = 1000 }; static struct event ev; size_t left; @@ -1689,11 +1689,12 @@ server_client_check_redraw(struct client *c) if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; if (c->flags & CLIENT_ALLREDRAWFLAGS) { - log_debug("%s: redraw%s%s%s%s", c->name, + log_debug("%s: redraw%s%s%s%s%s", c->name, (c->flags & CLIENT_REDRAWWINDOW) ? " window" : "", (c->flags & CLIENT_REDRAWSTATUS) ? " status" : "", (c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "", - (c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : ""); + (c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "", + (c->flags & CLIENT_REDRAWPANES) ? " panes" : ""); } /* @@ -1711,6 +1712,8 @@ server_client_check_redraw(struct client *c) break; } } + if (needed) + new_flags |= CLIENT_REDRAWPANES; } if (needed && (left = EVBUFFER_LENGTH(tty->out)) != 0) { log_debug("%s: redraw deferred (%zu left)", c->name, left); @@ -1720,12 +1723,7 @@ server_client_check_redraw(struct client *c) log_debug("redraw timer started"); evtimer_add(&ev, &tv); } - - /* - * We may have got here for a single pane redraw, but force a - * full redraw next time in case other panes have been updated. - */ - c->flags |= CLIENT_ALLREDRAWFLAGS; + c->flags |= new_flags; return; } else if (needed) log_debug("%s: redraw needed", c->name); @@ -1741,10 +1739,12 @@ server_client_check_redraw(struct client *c) */ TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { if (wp->flags & PANE_REDRAW) { + log_debug("%s: redrawing pane %%%u", __func__, wp->id); tty_update_mode(tty, tty->mode, NULL); screen_redraw_pane(c, wp); } } + c->flags &= ~CLIENT_REDRAWPANES; } if (c->flags & CLIENT_ALLREDRAWFLAGS) { diff --git a/tmux.h b/tmux.h index b791f5af..d475065e 100644 --- a/tmux.h +++ b/tmux.h @@ -1244,6 +1244,7 @@ struct tty { #define TTY_BLOCK 0x80 #define TTY_HAVEDA 0x100 #define TTY_HAVEDSR 0x200 +#define TTY_SYNCING 0x400 int flags; struct tty_term *term; @@ -1538,12 +1539,14 @@ struct client { #define CLIENT_CONTROL_NOOUTPUT 0x4000000 #define CLIENT_DEFAULTSOCKET 0x8000000 #define CLIENT_STARTSERVER 0x10000000 +#define CLIENT_REDRAWPANES 0x20000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUSALWAYS| \ CLIENT_REDRAWBORDERS| \ - CLIENT_REDRAWOVERLAY) + CLIENT_REDRAWOVERLAY| \ + CLIENT_REDRAWPANES) #define CLIENT_UNATTACHEDFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ diff --git a/tty.c b/tty.c index 82436959..a50db799 100644 --- a/tty.c +++ b/tty.c @@ -1438,15 +1438,19 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, void tty_sync_start(struct tty *tty) { - if (tty_get_flags(tty) & TERM_SYNC) + if ((~tty->flags & TTY_SYNCING) && (tty_get_flags(tty) & TERM_SYNC)) { tty_puts(tty, "\033P=1s\033\\"); + tty->flags |= TTY_SYNCING; + } } void tty_sync_end(struct tty *tty) { - if (tty_get_flags(tty) & TERM_SYNC) + if (tty_get_flags(tty) & TERM_SYNC) { tty_puts(tty, "\033P=2s\033\\"); + tty->flags &= ~TTY_SYNCING; + } } static int @@ -1480,6 +1484,14 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), TAILQ_FOREACH(c, &clients, entry) { if (!tty_client_ready(c, wp)) continue; + if (c->flags & CLIENT_REDRAWPANES) { + /* + * Redraw is already deferred to redraw another pane - + * redraw this one also when that happens. + */ + wp->flags |= PANE_REDRAW; + break; + } ctx->bigger = tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); From d94bdf7420eb6d0ef88783a35db2c592a3fccec0 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 06:15:07 +0000 Subject: [PATCH 0190/1006] Revert previous, there is still a problem. --- screen-redraw.c | 13 +++---------- server-client.c | 18 +++++++++--------- tmux.h | 5 +---- tty.c | 16 ++-------------- 4 files changed, 15 insertions(+), 37 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index c9e70590..c510fb68 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -438,24 +438,17 @@ screen_redraw_screen(struct client *c) tty_sync_start(&c->tty); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { - log_debug("%s: redrawing borders", c->name); if (ctx.pane_status != PANE_STATUS_OFF) screen_redraw_draw_pane_status(&ctx); screen_redraw_draw_borders(&ctx); } - if (flags & CLIENT_REDRAWWINDOW) { - log_debug("%s: redrawing panes", c->name); + if (flags & CLIENT_REDRAWWINDOW) screen_redraw_draw_panes(&ctx); - } if (ctx.statuslines != 0 && - (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) { - log_debug("%s: redrawing status", c->name); + (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) screen_redraw_draw_status(&ctx); - } - if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) { - log_debug("%s: redrawing overlay", c->name); + if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) c->overlay_draw(c, &ctx); - } tty_reset(&c->tty); tty_sync_end(&c->tty); diff --git a/server-client.c b/server-client.c index a045c44b..8365831f 100644 --- a/server-client.c +++ b/server-client.c @@ -1681,7 +1681,7 @@ server_client_check_redraw(struct client *c) struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; - int needed, flags, mode = tty->mode, new_flags = 0; + int needed, flags, mode = tty->mode; struct timeval tv = { .tv_usec = 1000 }; static struct event ev; size_t left; @@ -1689,12 +1689,11 @@ server_client_check_redraw(struct client *c) if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; if (c->flags & CLIENT_ALLREDRAWFLAGS) { - log_debug("%s: redraw%s%s%s%s%s", c->name, + log_debug("%s: redraw%s%s%s%s", c->name, (c->flags & CLIENT_REDRAWWINDOW) ? " window" : "", (c->flags & CLIENT_REDRAWSTATUS) ? " status" : "", (c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "", - (c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "", - (c->flags & CLIENT_REDRAWPANES) ? " panes" : ""); + (c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : ""); } /* @@ -1712,8 +1711,6 @@ server_client_check_redraw(struct client *c) break; } } - if (needed) - new_flags |= CLIENT_REDRAWPANES; } if (needed && (left = EVBUFFER_LENGTH(tty->out)) != 0) { log_debug("%s: redraw deferred (%zu left)", c->name, left); @@ -1723,7 +1720,12 @@ server_client_check_redraw(struct client *c) log_debug("redraw timer started"); evtimer_add(&ev, &tv); } - c->flags |= new_flags; + + /* + * We may have got here for a single pane redraw, but force a + * full redraw next time in case other panes have been updated. + */ + c->flags |= CLIENT_ALLREDRAWFLAGS; return; } else if (needed) log_debug("%s: redraw needed", c->name); @@ -1739,12 +1741,10 @@ server_client_check_redraw(struct client *c) */ TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { if (wp->flags & PANE_REDRAW) { - log_debug("%s: redrawing pane %%%u", __func__, wp->id); tty_update_mode(tty, tty->mode, NULL); screen_redraw_pane(c, wp); } } - c->flags &= ~CLIENT_REDRAWPANES; } if (c->flags & CLIENT_ALLREDRAWFLAGS) { diff --git a/tmux.h b/tmux.h index d475065e..b791f5af 100644 --- a/tmux.h +++ b/tmux.h @@ -1244,7 +1244,6 @@ struct tty { #define TTY_BLOCK 0x80 #define TTY_HAVEDA 0x100 #define TTY_HAVEDSR 0x200 -#define TTY_SYNCING 0x400 int flags; struct tty_term *term; @@ -1539,14 +1538,12 @@ struct client { #define CLIENT_CONTROL_NOOUTPUT 0x4000000 #define CLIENT_DEFAULTSOCKET 0x8000000 #define CLIENT_STARTSERVER 0x10000000 -#define CLIENT_REDRAWPANES 0x20000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUSALWAYS| \ CLIENT_REDRAWBORDERS| \ - CLIENT_REDRAWOVERLAY| \ - CLIENT_REDRAWPANES) + CLIENT_REDRAWOVERLAY) #define CLIENT_UNATTACHEDFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ diff --git a/tty.c b/tty.c index a50db799..82436959 100644 --- a/tty.c +++ b/tty.c @@ -1438,19 +1438,15 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, void tty_sync_start(struct tty *tty) { - if ((~tty->flags & TTY_SYNCING) && (tty_get_flags(tty) & TERM_SYNC)) { + if (tty_get_flags(tty) & TERM_SYNC) tty_puts(tty, "\033P=1s\033\\"); - tty->flags |= TTY_SYNCING; - } } void tty_sync_end(struct tty *tty) { - if (tty_get_flags(tty) & TERM_SYNC) { + if (tty_get_flags(tty) & TERM_SYNC) tty_puts(tty, "\033P=2s\033\\"); - tty->flags &= ~TTY_SYNCING; - } } static int @@ -1484,14 +1480,6 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), TAILQ_FOREACH(c, &clients, entry) { if (!tty_client_ready(c, wp)) continue; - if (c->flags & CLIENT_REDRAWPANES) { - /* - * Redraw is already deferred to redraw another pane - - * redraw this one also when that happens. - */ - wp->flags |= PANE_REDRAW; - break; - } ctx->bigger = tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); From 1d2bd864f25a58ab85a6f9f0a448f3a69d8491cd Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 06:20:50 +0000 Subject: [PATCH 0191/1006] Add a flag to protect against nested syncs and add some extra logging to redrawing. --- screen-redraw.c | 13 ++++++++++--- server-client.c | 1 + tmux.h | 1 + tty.c | 8 ++++++-- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index c510fb68..c9e70590 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -438,17 +438,24 @@ screen_redraw_screen(struct client *c) tty_sync_start(&c->tty); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { + log_debug("%s: redrawing borders", c->name); if (ctx.pane_status != PANE_STATUS_OFF) screen_redraw_draw_pane_status(&ctx); screen_redraw_draw_borders(&ctx); } - if (flags & CLIENT_REDRAWWINDOW) + if (flags & CLIENT_REDRAWWINDOW) { + log_debug("%s: redrawing panes", c->name); screen_redraw_draw_panes(&ctx); + } if (ctx.statuslines != 0 && - (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) + (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) { + log_debug("%s: redrawing status", c->name); screen_redraw_draw_status(&ctx); - if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) + } + if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) { + log_debug("%s: redrawing overlay", c->name); c->overlay_draw(c, &ctx); + } tty_reset(&c->tty); tty_sync_end(&c->tty); diff --git a/server-client.c b/server-client.c index 8365831f..c74e4dd5 100644 --- a/server-client.c +++ b/server-client.c @@ -1741,6 +1741,7 @@ server_client_check_redraw(struct client *c) */ TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { if (wp->flags & PANE_REDRAW) { + log_debug("%s: redrawing pane %%%u", __func__, wp->id); tty_update_mode(tty, tty->mode, NULL); screen_redraw_pane(c, wp); } diff --git a/tmux.h b/tmux.h index b791f5af..16cf414b 100644 --- a/tmux.h +++ b/tmux.h @@ -1244,6 +1244,7 @@ struct tty { #define TTY_BLOCK 0x80 #define TTY_HAVEDA 0x100 #define TTY_HAVEDSR 0x200 +#define TTY_SYNCING 0x400 int flags; struct tty_term *term; diff --git a/tty.c b/tty.c index 82436959..92827f5d 100644 --- a/tty.c +++ b/tty.c @@ -1438,15 +1438,19 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, void tty_sync_start(struct tty *tty) { - if (tty_get_flags(tty) & TERM_SYNC) + if ((~tty->flags & TTY_SYNCING) && (tty_get_flags(tty) & TERM_SYNC)) { tty_puts(tty, "\033P=1s\033\\"); + tty->flags |= TTY_SYNCING; + } } void tty_sync_end(struct tty *tty) { - if (tty_get_flags(tty) & TERM_SYNC) + if (tty_get_flags(tty) & TERM_SYNC) { tty_puts(tty, "\033P=2s\033\\"); + tty->flags &= ~TTY_SYNCING; + } } static int From baf1fca27348a2950b7d1bb26cdf4d34a752b962 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 06:52:36 +0000 Subject: [PATCH 0192/1006] Only update mode when actually going to redraw something. --- server-client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server-client.c b/server-client.c index c74e4dd5..cd301bbd 100644 --- a/server-client.c +++ b/server-client.c @@ -1732,7 +1732,6 @@ server_client_check_redraw(struct client *c) flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR); tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE)) | TTY_NOCURSOR; - tty_update_mode(tty, mode, NULL); if (~c->flags & CLIENT_REDRAWWINDOW) { /* @@ -1742,13 +1741,14 @@ server_client_check_redraw(struct client *c) TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { if (wp->flags & PANE_REDRAW) { log_debug("%s: redrawing pane %%%u", __func__, wp->id); - tty_update_mode(tty, tty->mode, NULL); + tty_update_mode(tty, mode, NULL); screen_redraw_pane(c, wp); } } } if (c->flags & CLIENT_ALLREDRAWFLAGS) { + tty_update_mode(tty, mode, NULL); if (options_get_number(s->options, "set-titles")) server_client_set_title(c); screen_redraw_screen(c); From e153b928ff5139fdc39b45db493a1c81d2175d21 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 07:19:28 +0000 Subject: [PATCH 0193/1006] Add formats for pane written/skipped bytes for debugging. --- format.c | 3 +++ screen-write.c | 4 ++++ tmux.1 | 2 ++ tmux.h | 3 +++ 4 files changed, 12 insertions(+) diff --git a/format.c b/format.c index f2387f7a..b410f46f 100644 --- a/format.c +++ b/format.c @@ -2690,6 +2690,9 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "history_limit", "%u", gd->hlimit); format_add_cb(ft, "history_bytes", format_cb_history_bytes); + format_add(ft, "pane_written", "%zu", wp->written); + format_add(ft, "pane_skipped", "%zu", wp->skipped); + if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); format_add(ft, "pane_index", "%u", idx); diff --git a/screen-write.c b/screen-write.c index bd756ce3..34895c7b 100644 --- a/screen-write.c +++ b/screen-write.c @@ -174,6 +174,10 @@ screen_write_stop(struct screen_write_ctx *ctx) log_debug("%s: %u cells (%u written, %u skipped)", __func__, ctx->cells, ctx->written, ctx->skipped); + if (ctx->wp != NULL) { + ctx->wp->written += ctx->written; + ctx->wp->skipped += ctx->skipped; + } if (ctx->sync) { screen_write_initctx(ctx, &ttyctx, 0); diff --git a/tmux.1 b/tmux.1 index 28c8c9c3..325ccd98 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4450,6 +4450,7 @@ The following variables are available, where appropriate: .It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" .It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" +.It Li "pane_skipped" Ta "" Ta "Bytes skipped as not visible in pane" .It Li "pane_start_command" Ta "" Ta "Command pane started with" .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" @@ -4457,6 +4458,7 @@ The following variables are available, where appropriate: .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" +.It Li "pane_written" Ta "" Ta "Bytes written by pane (aside from redrawing)" .It Li "pid" Ta "" Ta "Server PID" .It Li "popup_key" Ta "" Ta "Key pressed in popup" .It Li "popup_mouse_x" Ta "" Ta "Mouse X position in popup" diff --git a/tmux.h b/tmux.h index 16cf414b..e5d7be90 100644 --- a/tmux.h +++ b/tmux.h @@ -934,6 +934,9 @@ struct window_pane { char *searchstr; int searchregex; + u_int written; + u_int skipped; + TAILQ_ENTRY(window_pane) entry; RB_ENTRY(window_pane) tree_entry; }; From b0a37e7514f2e08a9a8315cc68add4f0a53ed2af Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 07:32:53 +0000 Subject: [PATCH 0194/1006] Bring back previons fix to only redraw panes that need it after a redraw is deferred, but clear the pane flags when they are actually redrawn rather than every time. --- screen-redraw.c | 2 ++ server-client.c | 18 ++++++++---------- tmux.h | 4 +++- tty.c | 10 ++++++++++ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index c9e70590..4c017706 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -474,6 +474,7 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) tty_sync_start(&c->tty); screen_redraw_draw_pane(&ctx, wp); + wp->flags &= ~PANE_REDRAW; tty_reset(&c->tty); tty_sync_end(&c->tty); @@ -563,6 +564,7 @@ screen_redraw_draw_panes(struct screen_redraw_ctx *ctx) TAILQ_FOREACH(wp, &w->panes, entry) { if (window_pane_visible(wp)) screen_redraw_draw_pane(ctx, wp); + wp->flags &= ~PANE_REDRAW; } } diff --git a/server-client.c b/server-client.c index cd301bbd..14691fdc 100644 --- a/server-client.c +++ b/server-client.c @@ -1370,7 +1370,6 @@ server_client_loop(void) if (resize) server_client_check_resize(wp); } - wp->flags &= ~PANE_REDRAW; } check_window_name(w); } @@ -1681,7 +1680,7 @@ server_client_check_redraw(struct client *c) struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; - int needed, flags, mode = tty->mode; + int needed, flags, mode = tty->mode, new_flags = 0; struct timeval tv = { .tv_usec = 1000 }; static struct event ev; size_t left; @@ -1689,11 +1688,12 @@ server_client_check_redraw(struct client *c) if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; if (c->flags & CLIENT_ALLREDRAWFLAGS) { - log_debug("%s: redraw%s%s%s%s", c->name, + log_debug("%s: redraw%s%s%s%s%s", c->name, (c->flags & CLIENT_REDRAWWINDOW) ? " window" : "", (c->flags & CLIENT_REDRAWSTATUS) ? " status" : "", (c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "", - (c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : ""); + (c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "", + (c->flags & CLIENT_REDRAWPANES) ? " panes" : ""); } /* @@ -1711,6 +1711,8 @@ server_client_check_redraw(struct client *c) break; } } + if (needed) + new_flags |= CLIENT_REDRAWPANES; } if (needed && (left = EVBUFFER_LENGTH(tty->out)) != 0) { log_debug("%s: redraw deferred (%zu left)", c->name, left); @@ -1720,12 +1722,7 @@ server_client_check_redraw(struct client *c) log_debug("redraw timer started"); evtimer_add(&ev, &tv); } - - /* - * We may have got here for a single pane redraw, but force a - * full redraw next time in case other panes have been updated. - */ - c->flags |= CLIENT_ALLREDRAWFLAGS; + c->flags |= new_flags; return; } else if (needed) log_debug("%s: redraw needed", c->name); @@ -1745,6 +1742,7 @@ server_client_check_redraw(struct client *c) screen_redraw_pane(c, wp); } } + c->flags &= ~CLIENT_REDRAWPANES; } if (c->flags & CLIENT_ALLREDRAWFLAGS) { diff --git a/tmux.h b/tmux.h index e5d7be90..64b09abf 100644 --- a/tmux.h +++ b/tmux.h @@ -1542,12 +1542,14 @@ struct client { #define CLIENT_CONTROL_NOOUTPUT 0x4000000 #define CLIENT_DEFAULTSOCKET 0x8000000 #define CLIENT_STARTSERVER 0x10000000 +#define CLIENT_REDRAWPANES 0x20000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUSALWAYS| \ CLIENT_REDRAWBORDERS| \ - CLIENT_REDRAWOVERLAY) + CLIENT_REDRAWOVERLAY| \ + CLIENT_REDRAWPANES) #define CLIENT_UNATTACHEDFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ diff --git a/tty.c b/tty.c index 92827f5d..cdfedf6c 100644 --- a/tty.c +++ b/tty.c @@ -944,6 +944,7 @@ tty_fake_bce(const struct tty *tty, struct window_pane *wp, u_int bg) static void tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) { + struct client *c = tty->client; struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; u_int i; @@ -953,6 +954,7 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) * likely to be followed by some more scrolling. */ if (tty_large_region(tty, ctx)) { + log_debug("%s: %s, large redraw of %%%u", __func__, c->name, wp->id); wp->flags |= PANE_REDRAW; return; } @@ -1484,6 +1486,14 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), TAILQ_FOREACH(c, &clients, entry) { if (!tty_client_ready(c, wp)) continue; + if (c->flags & CLIENT_REDRAWPANES) { + /* + * Redraw is already deferred to redraw another pane - + * redraw this one also when that happens. + */ + wp->flags |= PANE_REDRAW; + break; + } ctx->bigger = tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); From c87595326c69b3a82175268136f901199ff3cead Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 09:00:31 +0000 Subject: [PATCH 0195/1006] Use peek line function instead of hoking in the array directly. --- format.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index b410f46f..53ef1604 100644 --- a/format.c +++ b/format.c @@ -977,7 +977,7 @@ format_grid_word(struct grid *gd, u_int x, u_int y) if (x == 0) { if (y == 0) break; - gl = &gd->linedata[y - 1]; + gl = grid_peek_line(gd, y - 1); if (~gl->flags & GRID_LINE_WRAPPED) break; y--; @@ -993,7 +993,7 @@ format_grid_word(struct grid *gd, u_int x, u_int y) if (end == 0 || x == end - 1) { if (y == gd->hsize + gd->sy - 1) break; - gl = &gd->linedata[y]; + gl = grid_peek_line(gd, y); if (~gl->flags & GRID_LINE_WRAPPED) break; y++; From 94c90385d2e728a4d37a25ce78b55b2ffeb429f9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 18 Apr 2020 11:45:49 +0100 Subject: [PATCH 0196/1006] Apple appear to have fixed kqueue in some OS X version (will wonder never cease!) so use it since it appears to be faster. --- osdep-darwin.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osdep-darwin.c b/osdep-darwin.c index 6b2b1d72..6d1bfb72 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -93,15 +93,19 @@ osdep_event_init(void) { struct event_base *base; +#ifndef __MAC_10_7 /* * On OS X, kqueue and poll are both completely broken and don't * work on anything except socket file descriptors (yes, really). */ setenv("EVENT_NOKQUEUE", "1", 1); setenv("EVENT_NOPOLL", "1", 1); +#endif base = event_init(); +#ifndef __MAC_10_7 unsetenv("EVENT_NOKQUEUE"); unsetenv("EVENT_NOPOLL"); +#endif return (base); } From 4a93294152efb921bb5bada71164c47a57518e31 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 14:21:39 +0000 Subject: [PATCH 0197/1006] Use size_t not u_int for the bytes counters and fix a const missing. --- format.c | 2 +- tmux.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/format.c b/format.c index 53ef1604..df610a50 100644 --- a/format.c +++ b/format.c @@ -954,7 +954,7 @@ format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe) char * format_grid_word(struct grid *gd, u_int x, u_int y) { - struct grid_line *gl; + const struct grid_line *gl; struct grid_cell gc; const char *ws; struct utf8_data *ud = NULL; diff --git a/tmux.h b/tmux.h index 64b09abf..edc2f377 100644 --- a/tmux.h +++ b/tmux.h @@ -934,8 +934,8 @@ struct window_pane { char *searchstr; int searchregex; - u_int written; - u_int skipped; + size_t written; + size_t skipped; TAILQ_ENTRY(window_pane) entry; RB_ENTRY(window_pane) tree_entry; From ea5fdd5331d74d305cbb61b8f47a2054fc2052fb Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 15:12:28 +0000 Subject: [PATCH 0198/1006] There is no point in keeping a bunch of different text buffers for each line when writing, we only need one as big as the line width - there can't be any more text than that since newer will overwrite older. --- screen-write.c | 64 +++++++++++++++++++++++++++++++++----------------- screen.c | 10 ++++++++ tmux.h | 4 ++++ 3 files changed, 57 insertions(+), 21 deletions(-) diff --git a/screen-write.c b/screen-write.c index 34895c7b..bd861a42 100644 --- a/screen-write.c +++ b/screen-write.c @@ -48,18 +48,16 @@ struct screen_write_collect_item { enum { TEXT, CLEAR_END, CLEAR_START } type; u_int used; - union { - u_int bg; - char data[256]; - }; + u_int bg; struct grid_cell gc; TAILQ_ENTRY(screen_write_collect_item) entry; }; struct screen_write_collect_line { - u_int bg; - TAILQ_HEAD(, screen_write_collect_item) items; + u_int bg; + char *data; + TAILQ_HEAD(, screen_write_collect_item) items; }; static void @@ -127,13 +125,33 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, } } +/* Make write list. */ +void +screen_write_make_list(struct screen *s) +{ + u_int y; + + s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list); + for (y = 0; y < screen_size_y(s); y++) + TAILQ_INIT(&s->write_list[y].items); +} + +/* Free write list. */ +void +screen_write_free_list(struct screen *s) +{ + u_int y; + + for (y = 0; y < screen_size_y(s); y++) + free(s->write_list[y].data); + free(s->write_list); +} + /* Initialize writing with a window. */ void screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) { - u_int y; - memset(ctx, 0, sizeof *ctx); ctx->wp = wp; @@ -142,9 +160,9 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, else ctx->s = s; - ctx->list = xcalloc(screen_size_y(ctx->s), sizeof *ctx->list); - for (y = 0; y < screen_size_y(ctx->s); y++) - TAILQ_INIT(&ctx->list[y].items); + if (ctx->s->write_list == NULL) + screen_write_make_list(ctx->s); + ctx->list = ctx->s->write_list; ctx->item = xcalloc(1, sizeof *ctx->item); ctx->scrolled = 0; @@ -186,7 +204,6 @@ screen_write_stop(struct screen_write_ctx *ctx) } free(ctx->item); - free(ctx->list); /* flush will have emptied */ } /* Reset screen state. */ @@ -1319,7 +1336,7 @@ screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x, return (redundant); } -/* Clear part of a collected line. */ +/* Clear collected lines. */ static void screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) { @@ -1351,15 +1368,19 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx) struct screen *s = ctx->s; struct screen_write_collect_line *cl; u_int y; + char *saved; log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, s->rupper, s->rlower); screen_write_collect_clear(ctx, s->rupper, 1); + saved = ctx->list[s->rupper].data; for (y = s->rupper; y < s->rlower; y++) { cl = &ctx->list[y + 1]; TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); + ctx->list[y].data = cl->data; } + ctx->list[s->rlower].data = saved; } /* Flush collected lines. */ @@ -1412,7 +1433,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = &ci->gc; ttyctx.wrapped = ci->wrapped; - ttyctx.ptr = ci->data; + ttyctx.ptr = ctx->list[y].data + ci->x; ttyctx.num = ci->used; tty_write(tty_cmd_cells, &ttyctx); } @@ -1423,6 +1444,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, TAILQ_REMOVE(&ctx->list[y].items, ci, entry); free(ci); } + ctx->list[y].bg = 0; } s->cx = cx; s->cy = cy; @@ -1442,14 +1464,13 @@ screen_write_collect_end(struct screen_write_ctx *ctx) if (ci->used == 0) return; - ci->data[ci->used] = '\0'; ci->x = s->cx; TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); ctx->item = xcalloc(1, sizeof *ctx->item); - log_debug("%s: %u %s (at %u,%u)", __func__, ci->used, ci->data, s->cx, - s->cy); + log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used, + (int)ci->used, ctx->list[s->cy].data + ci->x, s->cx, s->cy); if (s->cx != 0) { for (xx = s->cx; xx > 0; xx--) { @@ -1465,7 +1486,8 @@ screen_write_collect_end(struct screen_write_ctx *ctx) } } - grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, ci->data, ci->used); + grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, + ctx->list[s->cy].data + ci->x, ci->used); screen_write_set_cursor(ctx, s->cx + ci->used, -1); for (xx = s->cx; xx < screen_size_x(s); xx++) { @@ -1524,9 +1546,9 @@ screen_write_collect_add(struct screen_write_ctx *ctx, if (ci->used == 0) memcpy(&ci->gc, gc, sizeof ci->gc); - ci->data[ci->used++] = gc->data.data[0]; - if (ci->used == (sizeof ci->data) - 1) - screen_write_collect_end(ctx); + if (ctx->list[s->cy].data == NULL) + ctx->list[s->cy].data = xmalloc(screen_size_x(ctx->s)); + ctx->list[s->cy].data[s->cx + ci->used++] = gc->data.data[0]; } /* Write cell data. */ diff --git a/screen.c b/screen.c index d9d1aa09..5f6a39d3 100644 --- a/screen.c +++ b/screen.c @@ -85,6 +85,8 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->tabs = NULL; s->sel = NULL; + s->write_list = NULL; + screen_reinit(s); } @@ -122,6 +124,9 @@ screen_free(struct screen *s) free(s->title); free(s->ccolour); + if (s->write_list != NULL) + screen_write_free_list(s); + if (s->saved_grid != NULL) grid_destroy(s->saved_grid); grid_destroy(s->grid); @@ -222,6 +227,11 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, { u_int tcx, tcy; + if (s->write_list != NULL) { + screen_write_free_list(s); + s->write_list = NULL; + } + if (cx == NULL) cx = &tcx; *cx = s->cx; diff --git a/tmux.h b/tmux.h index edc2f377..b8bcfe5b 100644 --- a/tmux.h +++ b/tmux.h @@ -769,6 +769,8 @@ struct screen { bitstr_t *tabs; struct screen_sel *sel; + + struct screen_write_collect_line *write_list; }; /* Screen write context. */ @@ -2380,6 +2382,8 @@ void grid_view_delete_cells(struct grid *, u_int, u_int, u_int, u_int); char *grid_view_string_cells(struct grid *, u_int, u_int, u_int); /* screen-write.c */ +void screen_write_make_list(struct screen *); +void screen_write_free_list(struct screen *); void screen_write_start(struct screen_write_ctx *, struct window_pane *, struct screen *); void screen_write_stop(struct screen_write_ctx *); From deffef6f1378db4986941dd9d83ba61f11142cd8 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 15:22:05 +0000 Subject: [PATCH 0199/1006] Reset background colour on scrolled line. --- screen-write.c | 1 + 1 file changed, 1 insertion(+) diff --git a/screen-write.c b/screen-write.c index bd861a42..81d07cbb 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1381,6 +1381,7 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx) ctx->list[y].data = cl->data; } ctx->list[s->rlower].data = saved; + ctx->list[s->rlower].bg = 1 + 8; } /* Flush collected lines. */ From 100db552d16164648e269aeb45fd87f7a39d9c11 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 17:20:25 +0000 Subject: [PATCH 0200/1006] A resize can realloc so cannot cache the value of the list pointer. --- screen-write.c | 68 ++++++++++++++++++++++++++------------------------ screen.c | 7 +++--- tmux.h | 1 - 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/screen-write.c b/screen-write.c index 81d07cbb..55d399a7 100644 --- a/screen-write.c +++ b/screen-write.c @@ -162,7 +162,6 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, if (ctx->s->write_list == NULL) screen_write_make_list(ctx->s); - ctx->list = ctx->s->write_list; ctx->item = xcalloc(1, sizeof *ctx->item); ctx->scrolled = 0; @@ -965,7 +964,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); screen_write_collect_clear(ctx, s->cy, 1); - ctx->list[s->cy].bg = 1 + bg; + ctx->s->write_list[s->cy].bg = 1 + bg; ctx->item->used = 0; } @@ -993,7 +992,7 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) ci->x = s->cx; ci->type = CLEAR_END; ci->bg = bg; - TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); + TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); ctx->item = xcalloc(1, sizeof *ctx->item); } } @@ -1020,7 +1019,7 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) ci->x = s->cx; ci->type = CLEAR_START; ci->bg = bg; - TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); + TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); ctx->item = xcalloc(1, sizeof *ctx->item); } } @@ -1264,9 +1263,9 @@ screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x, u_int items = 0; int redundant = 0; - if (TAILQ_EMPTY(&ctx->list[y].items)) + if (TAILQ_EMPTY(&ctx->s->write_list[y].items)) return (0); - TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { + TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) { switch (ci->type) { case CLEAR_START: if (ci->x >= x) { @@ -1286,7 +1285,7 @@ screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x, } items++; size += ci->used; - TAILQ_REMOVE(&ctx->list[y].items, ci, entry); + TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry); free(ci); } ctx->skipped += size; @@ -1305,9 +1304,9 @@ screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x, int redundant = 0; u_int items = 0; - if (TAILQ_EMPTY(&ctx->list[y].items)) + if (TAILQ_EMPTY(&ctx->s->write_list[y].items)) return (0); - TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { + TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) { switch (ci->type) { case CLEAR_START: if (ci->x >= x) @@ -1327,7 +1326,7 @@ screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x, } items++; size += ci->used; - TAILQ_REMOVE(&ctx->list[y].items, ci, entry); + TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry); free(ci); } ctx->skipped += size; @@ -1341,18 +1340,20 @@ static void screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) { struct screen_write_collect_item *ci, *tmp; + struct screen_write_collect_line *cl; u_int i, items; size_t size; for (i = y; i < y + n; i++) { - if (TAILQ_EMPTY(&ctx->list[i].items)) + if (TAILQ_EMPTY(&ctx->s->write_list[i].items)) continue; items = 0; size = 0; - TAILQ_FOREACH_SAFE(ci, &ctx->list[i].items, entry, tmp) { + cl = &ctx->s->write_list[i]; + TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { items++; size += ci->used; - TAILQ_REMOVE(&ctx->list[i].items, ci, entry); + TAILQ_REMOVE(&cl->items, ci, entry); free(ci); } ctx->skipped += size; @@ -1374,14 +1375,14 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx) s->rupper, s->rlower); screen_write_collect_clear(ctx, s->rupper, 1); - saved = ctx->list[s->rupper].data; + saved = ctx->s->write_list[s->rupper].data; for (y = s->rupper; y < s->rlower; y++) { - cl = &ctx->list[y + 1]; - TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); - ctx->list[y].data = cl->data; + cl = &ctx->s->write_list[y + 1]; + TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry); + ctx->s->write_list[y].data = cl->data; } - ctx->list[s->rlower].data = saved; - ctx->list[s->rlower].bg = 1 + 8; + ctx->s->write_list[s->rlower].data = saved; + ctx->s->write_list[s->rlower].bg = 1 + 8; } /* Flush collected lines. */ @@ -1391,6 +1392,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, { struct screen *s = ctx->s; struct screen_write_collect_item *ci, *tmp; + struct screen_write_collect_line *cl; u_int y, cx, cy, items = 0; struct tty_ctx ttyctx; size_t written = 0; @@ -1414,13 +1416,14 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, cx = s->cx; cy = s->cy; for (y = 0; y < screen_size_y(s); y++) { - if (ctx->list[y].bg != 0) { + cl = &ctx->s->write_list[y]; + if (cl->bg != 0) { screen_write_set_cursor(ctx, 0, y); screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = ctx->list[y].bg - 1; + ttyctx.bg = cl->bg - 1; tty_write(tty_cmd_clearline, &ttyctx); } - TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { + TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { screen_write_set_cursor(ctx, ci->x, y); if (ci->type == CLEAR_END) { screen_write_initctx(ctx, &ttyctx, 1); @@ -1434,7 +1437,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = &ci->gc; ttyctx.wrapped = ci->wrapped; - ttyctx.ptr = ctx->list[y].data + ci->x; + ttyctx.ptr = cl->data + ci->x; ttyctx.num = ci->used; tty_write(tty_cmd_cells, &ttyctx); } @@ -1442,10 +1445,10 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, items++; written += ci->used; - TAILQ_REMOVE(&ctx->list[y].items, ci, entry); + TAILQ_REMOVE(&cl->items, ci, entry); free(ci); } - ctx->list[y].bg = 0; + cl->bg = 0; } s->cx = cx; s->cy = cy; @@ -1460,6 +1463,7 @@ screen_write_collect_end(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct screen_write_collect_item *ci = ctx->item; + struct screen_write_collect_line *cl = &s->write_list[s->cy]; struct grid_cell gc; u_int xx; @@ -1467,11 +1471,11 @@ screen_write_collect_end(struct screen_write_ctx *ctx) return; ci->x = s->cx; - TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); + TAILQ_INSERT_TAIL(&cl->items, ci, entry); ctx->item = xcalloc(1, sizeof *ctx->item); log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used, - (int)ci->used, ctx->list[s->cy].data + ci->x, s->cx, s->cy); + (int)ci->used, cl->data + ci->x, s->cx, s->cy); if (s->cx != 0) { for (xx = s->cx; xx > 0; xx--) { @@ -1487,8 +1491,8 @@ screen_write_collect_end(struct screen_write_ctx *ctx) } } - grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, - ctx->list[s->cy].data + ci->x, ci->used); + grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x, + ci->used); screen_write_set_cursor(ctx, s->cx + ci->used, -1); for (xx = s->cx; xx < screen_size_x(s); xx++) { @@ -1547,9 +1551,9 @@ screen_write_collect_add(struct screen_write_ctx *ctx, if (ci->used == 0) memcpy(&ci->gc, gc, sizeof ci->gc); - if (ctx->list[s->cy].data == NULL) - ctx->list[s->cy].data = xmalloc(screen_size_x(ctx->s)); - ctx->list[s->cy].data[s->cx + ci->used++] = gc->data.data[0]; + if (ctx->s->write_list[s->cy].data == NULL) + ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s)); + ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0]; } /* Write cell data. */ diff --git a/screen.c b/screen.c index 5f6a39d3..129cfc2d 100644 --- a/screen.c +++ b/screen.c @@ -227,10 +227,8 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, { u_int tcx, tcy; - if (s->write_list != NULL) { + if (s->write_list != NULL) screen_write_free_list(s); - s->write_list = NULL; - } if (cx == NULL) cx = &tcx; @@ -270,6 +268,9 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, } log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx, s->cy, *cx, *cy); + + if (s->write_list != NULL) + screen_write_make_list(s); } /* Resize screen. */ diff --git a/tmux.h b/tmux.h index b8bcfe5b..1580e4c1 100644 --- a/tmux.h +++ b/tmux.h @@ -782,7 +782,6 @@ struct screen_write_ctx { int sync; struct screen_write_collect_item *item; - struct screen_write_collect_line *list; u_int scrolled; u_int bg; From 62ff5e4b010a9db695c79593d20ac92b0aed9558 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 18 Apr 2020 21:35:32 +0000 Subject: [PATCH 0201/1006] The PANE_REDRAW flag bit might be needed by other panes so we can't clear it on the first redraw, and it can't be set when we are finished or they would be redrawn again, so if the redraw is deferred for a client, copy the redraw flag into a separate set of bits just for that client. --- screen-redraw.c | 2 -- server-client.c | 45 +++++++++++++++++++++++++++++++++++---------- tmux.h | 2 ++ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 4c017706..c9e70590 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -474,7 +474,6 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) tty_sync_start(&c->tty); screen_redraw_draw_pane(&ctx, wp); - wp->flags &= ~PANE_REDRAW; tty_reset(&c->tty); tty_sync_end(&c->tty); @@ -564,7 +563,6 @@ screen_redraw_draw_panes(struct screen_redraw_ctx *ctx) TAILQ_FOREACH(wp, &w->panes, entry) { if (window_pane_visible(wp)) screen_redraw_draw_pane(ctx, wp); - wp->flags &= ~PANE_REDRAW; } } diff --git a/server-client.c b/server-client.c index 14691fdc..6eda2b45 100644 --- a/server-client.c +++ b/server-client.c @@ -1370,6 +1370,7 @@ server_client_loop(void) if (resize) server_client_check_resize(wp); } + wp->flags &= ~PANE_REDRAW; } check_window_name(w); } @@ -1679,8 +1680,11 @@ server_client_check_redraw(struct client *c) { struct session *s = c->session; struct tty *tty = &c->tty; + struct window *w = c->session->curw->window; struct window_pane *wp; int needed, flags, mode = tty->mode, new_flags = 0; + int redraw; + u_int bit = 0; struct timeval tv = { .tv_usec = 1000 }; static struct event ev; size_t left; @@ -1705,7 +1709,7 @@ server_client_check_redraw(struct client *c) if (c->flags & CLIENT_ALLREDRAWFLAGS) needed = 1; else { - TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { + TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->flags & PANE_REDRAW) { needed = 1; break; @@ -1722,25 +1726,46 @@ server_client_check_redraw(struct client *c) log_debug("redraw timer started"); evtimer_add(&ev, &tv); } + if (new_flags & CLIENT_REDRAWPANES) { + c->redraw_panes = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->flags & PANE_REDRAW) + c->redraw_panes |= (1 << bit); + if (++bit == 64) { + /* + * If more that 64 panes, give up and + * just redraw the window. + */ + new_flags &= CLIENT_REDRAWPANES; + new_flags |= CLIENT_REDRAWWINDOW; + break; + } + } + } c->flags |= new_flags; return; } else if (needed) log_debug("%s: redraw needed", c->name); flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR); - tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE)) | TTY_NOCURSOR; + tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE))|TTY_NOCURSOR; if (~c->flags & CLIENT_REDRAWWINDOW) { /* * If not redrawing the entire window, check whether each pane * needs to be redrawn. */ - TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { - if (wp->flags & PANE_REDRAW) { - log_debug("%s: redrawing pane %%%u", __func__, wp->id); - tty_update_mode(tty, mode, NULL); - screen_redraw_pane(c, wp); - } + TAILQ_FOREACH(wp, &w->panes, entry) { + redraw = 0; + if (wp->flags & PANE_REDRAW) + redraw = 1; + else if (c->flags & CLIENT_REDRAWPANES) + redraw = !!(c->redraw_panes & (1 << bit)); + if (!redraw) + continue; + log_debug("%s: redrawing pane %%%u", __func__, wp->id); + tty_update_mode(tty, mode, NULL); + screen_redraw_pane(c, wp); } c->flags &= ~CLIENT_REDRAWPANES; } @@ -1752,9 +1777,9 @@ server_client_check_redraw(struct client *c) screen_redraw_screen(c); } - tty->flags = (tty->flags & ~TTY_NOCURSOR) | (flags & TTY_NOCURSOR); + tty->flags = (tty->flags & ~TTY_NOCURSOR)|(flags & TTY_NOCURSOR); tty_update_mode(tty, mode, NULL); - tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR)) | flags; + tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR))|flags; c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_STATUSFORCE); diff --git a/tmux.h b/tmux.h index 1580e4c1..bbac756b 100644 --- a/tmux.h +++ b/tmux.h @@ -1562,6 +1562,8 @@ struct client { int flags; struct key_table *keytable; + uint64_t redraw_panes; + char *message_string; struct event message_timer; u_int message_next; From 4bc0a83d51624911195fb4b033f51aa7bca43e54 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2020 06:07:39 +0000 Subject: [PATCH 0202/1006] Need to check for pane redrawing even if just the window flag is set (the pane flag may not have been previously set to avoid looping the windows). --- server-client.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server-client.c b/server-client.c index 6eda2b45..18f44ff9 100644 --- a/server-client.c +++ b/server-client.c @@ -1726,11 +1726,15 @@ server_client_check_redraw(struct client *c) log_debug("redraw timer started"); evtimer_add(&ev, &tv); } - if (new_flags & CLIENT_REDRAWPANES) { + + if (~c->flags & CLIENT_REDRAWWINDOW) { c->redraw_panes = 0; TAILQ_FOREACH(wp, &w->panes, entry) { - if (wp->flags & PANE_REDRAW) + if (wp->flags & PANE_REDRAW) { + log_debug("%s: pane %%%u needs redraw", + c->name, wp->id); c->redraw_panes |= (1 << bit); + } if (++bit == 64) { /* * If more that 64 panes, give up and @@ -1741,6 +1745,8 @@ server_client_check_redraw(struct client *c) break; } } + if (c->redraw_panes != 0) + c->flags |= CLIENT_REDRAWPANES; } c->flags |= new_flags; return; From b846ec266571be41e84ebda4480484db73416c81 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2020 06:08:37 +0000 Subject: [PATCH 0203/1006] Only trim blank lines when the source pane is not the target pane, otherwise the cursor moves which is a bit strange. --- window-copy.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/window-copy.c b/window-copy.c index 45a47675..2bda5d56 100644 --- a/window-copy.c +++ b/window-copy.c @@ -299,7 +299,7 @@ window_copy_scroll_timer(__unused int fd, __unused short events, void *arg) static struct screen * window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, - u_int *cy) + u_int *cy, int trim) { struct screen *dst; u_int sy; @@ -308,11 +308,13 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, dst = xcalloc(1, sizeof *dst); sy = screen_hsize(src) + screen_size_y(src); - while (sy > screen_hsize(src)) { - gl = grid_peek_line(src->grid, sy - 1); - if (gl->cellused != 0) - break; - sy--; + if (trim) { + while (sy > screen_hsize(src)) { + gl = grid_peek_line(src->grid, sy - 1); + if (gl->cellused != 0) + break; + sy--; + } } log_debug("%s: target screen is %ux%u, source %ux%u", __func__, screen_size_x(src), sy, screen_size_x(hint), @@ -386,7 +388,8 @@ window_copy_init(struct window_mode_entry *wme, u_int i, cx, cy; data = window_copy_common_init(wme); - data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy); + data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy, + wme->swp != wme->wp); if (cy < screen_hsize(data->backing)) { data->cx = cx; @@ -2042,7 +2045,7 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) screen_free(data->backing); free(data->backing); data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, - NULL); + NULL, wme->swp != wme->wp); window_copy_size_changed(wme); return (WINDOW_COPY_CMD_REDRAW); From 86862c976af7d16524b675ea1049edce07a1aafa Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2020 09:07:55 +0000 Subject: [PATCH 0204/1006] Also redraw panes which aren't pane 0. Problem reported by tb@. --- server-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-client.c b/server-client.c index 18f44ff9..1df1a77f 100644 --- a/server-client.c +++ b/server-client.c @@ -1767,6 +1767,7 @@ server_client_check_redraw(struct client *c) redraw = 1; else if (c->flags & CLIENT_REDRAWPANES) redraw = !!(c->redraw_panes & (1 << bit)); + bit++; if (!redraw) continue; log_debug("%s: redrawing pane %%%u", __func__, wp->id); From c91b4b2e142b5b3fc9ca88afec6bfa9b2711e01b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2020 13:25:36 +0000 Subject: [PATCH 0205/1006] Tidy up the terminal detection and feature code and add named sets of terminal features, each of which are defined in one place and map to a builtin set of terminfo(5) capabilities. Features can be specified based on TERM with a new terminal-features option or with the -T flag when running tmux. tmux will also detect a few common terminals from the DA and DSR responses. This is intended to make it easier to configure tmux's use of terminfo(5) even in the presence of outdated ncurses(3) or terminfo(5) databases or for features which do not yet have a terminfo(5) entry. Instead of having to grok terminfo(5) capability names and what they should be set to in the terminal-overrides option, the user can hopefully just give tmux a feature name and let it do the right thing. The terminal-overrides option remains both for backwards compatibility and to allow tweaks of individual capabilities. tmux already did much of this already, this makes it tidier and simpler to configure. --- Makefile | 1 + client.c | 9 +- cmd-show-messages.c | 14 ++- format.c | 7 +- options-table.c | 13 ++- server-client.c | 24 ++-- tmux.1 | 92 +++++++++++---- tmux.c | 20 ++-- tmux.h | 30 ++--- tty-acs.c | 2 +- tty-features.c | 264 ++++++++++++++++++++++++++++++++++++++++++++ tty-keys.c | 62 ++++++++--- tty-term.c | 104 +++++++++-------- tty.c | 121 ++++++-------------- 14 files changed, 540 insertions(+), 223 deletions(-) create mode 100644 tty-features.c diff --git a/Makefile b/Makefile index 24117bf1..0f1c94c4 100644 --- a/Makefile +++ b/Makefile @@ -111,6 +111,7 @@ SRCS= alerts.c \ style.c \ tmux.c \ tty-acs.c \ + tty-features.c \ tty-keys.c \ tty-term.c \ tty.c \ diff --git a/client.c b/client.c index c1d6e1b5..98565c5d 100644 --- a/client.c +++ b/client.c @@ -57,7 +57,7 @@ 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 void client_send_identify(const char *, const char *); +static void client_send_identify(const char *, const char *, int); static void client_signal(int); static void client_dispatch(struct imsg *, void *); static void client_dispatch_attached(struct imsg *); @@ -233,7 +233,7 @@ client_exit(void) /* Client main loop. */ int -client_main(struct event_base *base, int argc, char **argv, int flags) +client_main(struct event_base *base, int argc, char **argv, int flags, int feat) { struct cmd_parse_result *pr; struct msg_command *data; @@ -340,7 +340,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Send identify messages. */ - client_send_identify(ttynam, cwd); + client_send_identify(ttynam, cwd, feat); /* Send first command. */ if (msg == MSG_COMMAND) { @@ -406,7 +406,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Send identify messages to server. */ static void -client_send_identify(const char *ttynam, const char *cwd) +client_send_identify(const char *ttynam, const char *cwd, int feat) { const char *s; char **ss; @@ -419,6 +419,7 @@ client_send_identify(const char *ttynam, const char *cwd) if ((s = getenv("TERM")) == NULL) s = ""; proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); + proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat); proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 78e5859c..d734ca65 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -43,22 +43,24 @@ const struct cmd_entry cmd_show_messages_entry = { .exec = cmd_show_messages_exec }; -static int cmd_show_messages_terminals(struct cmdq_item *, int); - static int -cmd_show_messages_terminals(struct cmdq_item *item, int blank) +cmd_show_messages_terminals(struct cmd *self, struct cmdq_item *item, int blank) { + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); struct tty_term *term; u_int i, n; n = 0; LIST_FOREACH(term, &tty_terms, entry) { + if (args_has(args, 't') && term != tc->tty.term) + continue; if (blank) { cmdq_print(item, "%s", ""); blank = 0; } - cmdq_print(item, "Terminal %u: %s [references=%u, flags=0x%x]:", - n, term->name, term->references, term->flags); + cmdq_print(item, "Terminal %u: %s for %s, flags=0x%x:", n, + term->name, term->tty->client->name, term->flags); n++; for (i = 0; i < tty_term_ncodes(); i++) cmdq_print(item, "%s", tty_term_describe(term, i)); @@ -77,7 +79,7 @@ cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) done = blank = 0; if (args_has(args, 'T')) { - blank = cmd_show_messages_terminals(item, blank); + blank = cmd_show_messages_terminals(self, item, blank); done = 1; } if (args_has(args, 'J')) { diff --git a/format.c b/format.c index df610a50..f1f52895 100644 --- a/format.c +++ b/format.c @@ -2553,8 +2553,9 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_control_mode", "%d", !!(c->flags & CLIENT_CONTROL)); - if (tty->term_name != NULL) - format_add(ft, "client_termname", "%s", tty->term_name); + format_add(ft, "client_termname", "%s", c->term_name); + format_add(ft, "client_termfeatures", "%s", + tty_get_features(c->term_features)); format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); @@ -2569,7 +2570,7 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_prefix", "%d", 1); format_add(ft, "client_key_table", "%s", c->keytable->name); - if (tty_get_flags(tty) & TERM_UTF8) + if (c->flags & CLIENT_UTF8) format_add(ft, "client_utf8", "%d", 1); else format_add(ft, "client_utf8", "%d", 0); diff --git a/options-table.c b/options-table.c index 1401f05d..722429f7 100644 --- a/options-table.c +++ b/options-table.c @@ -261,9 +261,16 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, - .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" - ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" - ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT", + .default_str = "tmux*:XT,screen*:XT", + .separator = "," + }, + + { .name = "terminal-features", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .flags = OPTIONS_TABLE_IS_ARRAY, + .default_str = "xterm*:clipboard:ccolour:cstyle:title," + "screen*:title", .separator = "," }, diff --git a/server-client.c b/server-client.c index 1df1a77f..cd850593 100644 --- a/server-client.c +++ b/server-client.c @@ -296,7 +296,7 @@ server_client_lost(struct client *c) if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); - free(c->term); + free(c->term_name); status_free(c); @@ -1845,6 +1845,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { + case MSG_IDENTIFY_FEATURES: case MSG_IDENTIFY_FLAGS: case MSG_IDENTIFY_TERM: case MSG_IDENTIFY_TTYNAME: @@ -2003,7 +2004,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) { const char *data, *home; size_t datalen; - int flags; + int flags, feat; char *name; if (c->flags & CLIENT_IDENTIFIED) @@ -2013,6 +2014,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { + case MSG_IDENTIFY_FEATURES: + if (datalen != sizeof feat) + fatalx("bad MSG_IDENTIFY_FEATURES size"); + memcpy(&feat, data, sizeof feat); + c->term_features |= feat; + log_debug("client %p IDENTIFY_FEATURES %s", c, + tty_get_features(feat)); + break; case MSG_IDENTIFY_FLAGS: if (datalen != sizeof flags) fatalx("bad MSG_IDENTIFY_FLAGS size"); @@ -2023,7 +2032,10 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) case MSG_IDENTIFY_TERM: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TERM string"); - c->term = xstrdup(data); + if (*data == '\0') + c->term_name = xstrdup("unknown"); + else + c->term_name = xstrdup(data); log_debug("client %p IDENTIFY_TERM %s", c, data); break; case MSG_IDENTIFY_TTYNAME: @@ -2084,14 +2096,10 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) control_start(c); c->tty.fd = -1; } else if (c->fd != -1) { - if (tty_init(&c->tty, c, c->fd, c->term) != 0) { + if (tty_init(&c->tty, c, c->fd) != 0) { close(c->fd); c->fd = -1; } else { - if (c->flags & CLIENT_UTF8) - c->tty.term_flags |= TERM_UTF8; - if (c->flags & CLIENT_256COLOURS) - c->tty.term_flags |= TERM_256COLOURS; tty_resize(&c->tty); c->flags |= CLIENT_TERMINAL; } diff --git a/tmux.1 b/tmux.1 index 325ccd98..ca4e30e0 100644 --- a/tmux.1 +++ b/tmux.1 @@ -28,6 +28,7 @@ .Op Fl f Ar file .Op Fl L Ar socket-name .Op Fl S Ar socket-path +.Op Fl T Ar features .Op Ar command Op Ar flags .Ek .Sh DESCRIPTION @@ -98,6 +99,8 @@ The options are as follows: Force .Nm to assume the terminal supports 256 colours. +This is equivalent to +.Fl T Ar 256 . .It Fl C Start in control mode (see the .Sx CONTROL MODE @@ -186,6 +189,14 @@ that is set does not contain .Qq UTF-8 or .Qq UTF8 . +This is equivalent to +.Fl T Ar UTF-8 . +.It Fl T Ar features +Set terminal features for the client. +This is a comma-separated list of features. +See the +.Ic terminal-features +option. .It Fl v Request verbose logging. Log messages will be saved into @@ -3166,6 +3177,63 @@ disallowedWindowOps: 20,21,SetXprop Or changing this property from the .Xr xterm 1 interactive menu when required. +.It Ic terminal-features[] Ar string +Set terminal features for terminal types read from +.Xr terminfo 5 . +.Nm +has a set of named terminal features. +Each will apply appropriate changes to the +.Xr terminfo 5 +entry in use. +.Pp +.Nm +can detect features for a few common terminals; this option can be used to +easily tell tmux about features supported by terminals it cannot detect. +The +.Ic terminal-overrides +option allows individual +.Xr terminfo 5 +capabilities to be set instead, +.Ic terminal-features +is intended for classes of functionality supported in a standard way but not +reported by +.Xr terminfo 5 . +Care must be taken only to configure this with features the terminal actually +support. +.Pp +This is an array option where each entry is a colon-separated string made up +of a terminal type pattern (matched using +.Xr fnmatch 3 ) +followed by a list of terminal features. +The available features are: +.Bl -tag -width Ds +.It 256 +Supports 256 colours with the SGR escape sequences. +.It clipboard +Allows setting the system clipboard. +.It ccolour +Allows setting the cursor colour. +.It cstyle +Allows setting the cursor style. +.It margins +Supports DECSLRM margins. +.It overline +Supports the overline SGR attribute. +.It rectfill +Supports the DECFRA rectangle fill escape sequence. +.It RGB +Supports RGB colour with the SGR escape sequences. +.It sync +Supports synchronized updates. +.It title +Supports +.Xr xterm 1 +title setting. +.It usstyle +Allows underscore style and colour to be set. +.It UTF-8 +Is able to handle UTF-8 output. +.El .It Ic terminal-overrides[] Ar string Allow terminal descriptions read using .Xr terminfo 5 @@ -4383,6 +4451,7 @@ The following variables are available, where appropriate: .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_termname" Ta "" Ta "Terminal name of client" +.It Li "client_termfeatures" Ta "" Ta "Terminal features of client" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" @@ -5465,7 +5534,10 @@ The server crashed or otherwise exited without telling the client the reason. .Sh TERMINFO EXTENSIONS .Nm understands some unofficial extensions to -.Xr terminfo 5 : +.Xr terminfo 5. +It is not normally necessary to set these manually, instead the +.Ic terminal-features +option should be used. .Bl -tag -width Ds .It Em \&Cs , Cr Set the cursor colour. @@ -5479,33 +5551,15 @@ $ printf '\e033]12;red\e033\e\e' .Ed .It Em \&Smol Enable the overline attribute. -The capability is usually SGR 53 and can be added to -.Ic terminal-overrides -as: -.Bd -literal -offset indent -Smol=\eE[53m -.Ed .It Em \&Smulx 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. -The capability can typically be added to -.Ic terminal-overrides -as: -.Bd -literal -offset indent -Smulx=\eE[4::%p1%dm -.Ed .It Em \&Setulc Set the underscore colour. The argument is (red * 65536) + (green * 256) + blue where each is between 0 and 255. -The capability can typically be added to -.Ic terminal-overrides -as: -.Bd -literal -offset indent -Setulc=\eE[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m -.Ed .It Em \&Ss , Se Set or reset the cursor style. If set, a sequence such as this may be used diff --git a/tmux.c b/tmux.c index c9678d9e..515f1543 100644 --- a/tmux.c +++ b/tmux.c @@ -58,7 +58,7 @@ usage(void) { fprintf(stderr, "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" - " [-S socket-path] [command [flags]]\n", + " [-S socket-path] [-T features] [command [flags]]\n", getprogname()); exit(1); } @@ -242,9 +242,11 @@ getversion(void) int main(int argc, char **argv) { - char *path, *label, *cause, **var; + char *path = NULL, *label = NULL; + char *cause, **var; const char *s, *shell, *cwd; - int opt, flags, keys; + int opt, flags = 0, keys; + int feat = 0; const struct options_table_entry *oe; if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && @@ -261,14 +263,11 @@ main(int argc, char **argv) if (**argv == '-') flags = CLIENT_LOGIN; - else - flags = 0; - label = path = NULL; - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUvV")) != -1) { + while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:T:uUvV")) != -1) { switch (opt) { case '2': - flags |= CLIENT_256COLOURS; + tty_add_features(&feat, "256", ":,"); break; case 'c': shell_command = optarg; @@ -298,6 +297,9 @@ main(int argc, char **argv) free(path); path = xstrdup(optarg); break; + case 'T': + tty_add_features(&feat, optarg, ":,"); + break; case 'u': flags |= CLIENT_UTF8; break; @@ -405,5 +407,5 @@ main(int argc, char **argv) free(label); /* Pass control to the client. */ - exit(client_main(event_init(), argc, argv, flags)); + exit(client_main(event_init(), argc, argv, flags, feat)); } diff --git a/tmux.h b/tmux.h index bbac756b..3853b09f 100644 --- a/tmux.h +++ b/tmux.h @@ -474,6 +474,7 @@ enum msgtype { MSG_IDENTIFY_DONE, MSG_IDENTIFY_CLIENTPID, MSG_IDENTIFY_CWD, + MSG_IDENTIFY_FEATURES, MSG_COMMAND = 200, MSG_DETACH, @@ -1176,7 +1177,8 @@ struct tty_key { struct tty_code; struct tty_term { char *name; - u_int references; + struct tty *tty; + int features; char acs[UCHAR_MAX + 1][2]; @@ -1187,8 +1189,6 @@ struct tty_term { #define TERM_DECSLRM 0x4 #define TERM_DECFRA 0x8 #define TERM_RGBCOLOURS 0x10 -#define TERM_SYNC 0x20 -#define TERM_UTF8 0x40 int flags; LIST_ENTRY(tty_term) entry; @@ -1252,8 +1252,6 @@ struct tty { int flags; struct tty_term *term; - char *term_name; - int term_flags; u_int mouse_last_x; u_int mouse_last_y; @@ -1267,7 +1265,6 @@ struct tty { struct event key_timer; struct tty_key *key_tree; }; -#define tty_term_flags(tty) (tty->term->flags|tty->term_flags) /* TTY command context. */ struct tty_ctx { @@ -1498,7 +1495,9 @@ struct client { char *title; const char *cwd; - char *term; + char *term_name; + int term_features; + char *ttyname; struct tty tty; @@ -1531,7 +1530,7 @@ struct client { #define CLIENT_CONTROLCONTROL 0x4000 #define CLIENT_FOCUSED 0x8000 #define CLIENT_UTF8 0x10000 -#define CLIENT_256COLOURS 0x20000 +/* 0x20000 unused */ #define CLIENT_IDENTIFIED 0x40000 #define CLIENT_STATUSFORCE 0x80000 #define CLIENT_DOUBLECLICK 0x100000 @@ -1953,7 +1952,7 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); -int tty_init(struct tty *, struct client *, int, char *); +int tty_init(struct tty *, struct client *, int); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); void tty_start_tty(struct tty *); @@ -1968,8 +1967,7 @@ void tty_sync_end(struct tty *); int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); -void tty_set_flags(struct tty *, int); -int tty_get_flags(struct tty *); +void tty_update_features(struct tty *); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); @@ -1999,7 +1997,8 @@ void tty_cmd_syncend(struct tty *, const struct tty_ctx *); /* tty-term.c */ extern struct tty_terms tty_terms; u_int tty_term_ncodes(void); -struct tty_term *tty_term_find(char *, int, char **); +void tty_term_apply(struct tty_term *, const char *, int); +struct tty_term *tty_term_create(struct tty *, char *, int *, int, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); @@ -2016,6 +2015,11 @@ int tty_term_number(struct tty_term *, enum tty_code_code); int tty_term_flag(struct tty_term *, enum tty_code_code); const char *tty_term_describe(struct tty_term *, enum tty_code_code); +/* tty-features.c */ +void tty_add_features(int *, const char *, const char *); +const char *tty_get_features(int); +void tty_apply_features(struct tty_term *, int); + /* tty-acs.c */ int tty_acs_needed(struct tty *); const char *tty_acs_get(struct tty *, u_char); @@ -2161,7 +2165,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 client_main(struct event_base *, int, char **, int, int); /* key-bindings.c */ struct key_table *key_bindings_get_table(const char *, int); diff --git a/tty-acs.c b/tty-acs.c index f5352d3e..3e811103 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -99,7 +99,7 @@ tty_acs_needed(struct tty *tty) tty_term_number(tty->term, TTYC_U8) == 0) return (1); - if (tty_get_flags(tty) & TERM_UTF8) + if (tty->client->flags & CLIENT_UTF8) return (0); return (1); } diff --git a/tty-features.c b/tty-features.c new file mode 100644 index 00000000..58cbae7b --- /dev/null +++ b/tty-features.c @@ -0,0 +1,264 @@ +/* $OpenBSD$ */ + +/* + * 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 + +#include "tmux.h" + +/* + * Still hardcoded: + * - bracket paste (sent if application asks for it); + * - mouse (under kmous capability); + * - focus events (under focus-events option); + * - default colours (under AX or op capabilities); + * - AIX colours (under colors >= 16); + * - alternate escape (under XT). + * + * Also: + * - XT is used to decide whether to send DA and DSR, + * - DECSLRM and DECFRA use a flag instead of capabilities. + * - Sync is a flag rather than a string capability. + * - UTF-8 is a separate flag on the client; needed for unattached clients. + */ + +/* A named terminal feature. */ +struct tty_feature { + const char *name; + const char **capabilities; + int flags; +}; + +/* Terminal has xterm(1) title setting. */ +static const char *tty_feature_title_capabilities[] = { + "tsl=\\E]0;", /* should be using TS really */ + "fsl=\\a", + NULL +}; +static struct tty_feature tty_feature_title = { + "title", + tty_feature_title_capabilities, + 0 +}; + +/* Terminal can set the clipboard with OSC 52. */ +static const char *tty_feature_clipboard_capabilities[] = { + "Ms=\\E]52;%p1%s;%p2%s\\a", + NULL +}; +static struct tty_feature tty_feature_clipboard = { + "clipboard", + tty_feature_clipboard_capabilities, + 0 +}; + +/* + * Terminal supports RGB colour. This replaces setab and setaf also since + * terminals with RGB have versions that do not allow setting colours from the + * 256 palette. + */ +static const char *tty_feature_rgb_capabilities[] = { + "setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm", + "setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm", + "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + NULL +}; +static struct tty_feature tty_feature_rgb = { + "RGB", + tty_feature_rgb_capabilities, + (TERM_256COLOURS|TERM_RGBCOLOURS) +}; + +/* Terminal supports 256 colours. */ +static const char *tty_feature_256_capabilities[] = { + "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + NULL +}; +static struct tty_feature tty_feature_256 = { + "256", + tty_feature_256_capabilities, + TERM_256COLOURS +}; + +/* Terminal supports overline. */ +static const char *tty_feature_overline_capabilities[] = { + "Smol=\\E[53m", + NULL +}; +static struct tty_feature tty_feature_overline = { + "overline", + tty_feature_overline_capabilities, + 0 +}; + +/* Terminal supports underscore styles. */ +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", + NULL +}; +static struct tty_feature tty_feature_usstyle = { + "usstyle", + tty_feature_usstyle_capabilities, + 0 +}; + +/* Terminal supports cursor styles. */ +static const char *tty_feature_cstyle_capabilities[] = { + "Ss=\\E[%p1%d q", + "Se=\\E[2 q", + NULL +}; +static struct tty_feature tty_feature_cstyle = { + "cstyle", + tty_feature_cstyle_capabilities, + 0 +}; + +/* Terminal supports cursor colours. */ +static const char *tty_feature_ccolour_capabilities[] = { + "Cs=\\E]12;%p1%s\\a", + "Cr=\\E]112\\a", + NULL +}; +static struct tty_feature tty_feature_ccolour = { + "ccolour", + tty_feature_ccolour_capabilities, + 0 +}; + +/* Terminal supports synchronized updates. */ +static const char *tty_feature_sync_capabilities[] = { + "Sync", + NULL +}; +static struct tty_feature tty_feature_sync = { + "sync", + tty_feature_sync_capabilities, + 0 +}; + +/* Terminal supports DECSLRM margins. */ +static struct tty_feature tty_feature_margins = { + "margins", + NULL, + TERM_DECSLRM +}; + +/* Terminal supports DECFRA rectangle fill. */ +static struct tty_feature tty_feature_rectfill = { + "rectfill", + NULL, + TERM_DECFRA +}; + +/* Available terminal features. */ +static const struct tty_feature *tty_features[] = { + &tty_feature_256, + &tty_feature_clipboard, + &tty_feature_ccolour, + &tty_feature_cstyle, + &tty_feature_margins, + &tty_feature_overline, + &tty_feature_rectfill, + &tty_feature_rgb, + &tty_feature_sync, + &tty_feature_title, + &tty_feature_usstyle +}; + +void +tty_add_features(int *feat, const char *s, const char *separators) +{ + const struct tty_feature *tf; + char *next, *loop, *copy; + u_int i; + + loop = copy = xstrdup(s); + while ((next = strsep(&loop, separators)) != NULL) { + for (i = 0; i < nitems(tty_features); i++) { + tf = tty_features[i]; + if (strcasecmp(tf->name, next) == 0) + break; + } + if (i == nitems(tty_features)) { + log_debug("unknown terminal feature: %s", next); + break; + } + if (~(*feat) & (1 << i)) { + log_debug("adding terminal feature: %s", tf->name); + (*feat) |= (1 << i); + } + } + free(copy); +} + +const char * +tty_get_features(int feat) +{ + const struct tty_feature *tf; + static char s[512]; + u_int i; + + *s = '\0'; + for (i = 0; i < nitems(tty_features); i++) { + if (~feat & (1 << i)) + continue; + tf = tty_features[i]; + + strlcat(s, tf->name, sizeof s); + strlcat(s, ",", sizeof s); + } + if (*s != '\0') + s[strlen(s) - 1] = '\0'; + return (s); +} + +void +tty_apply_features(struct tty_term *term, int feat) +{ + const struct tty_feature *tf; + const char **capability; + u_int i; + + if (feat == 0) + return; + log_debug("applying terminal features: %s", tty_get_features(feat)); + + for (i = 0; i < nitems(tty_features); i++) { + if ((term->features & (1 << i)) || (~feat & (1 << i))) + continue; + tf = tty_features[i]; + + log_debug("applying terminal feature: %s", tf->name); + if (tf->capabilities != NULL) { + capability = tf->capabilities; + while (*capability != NULL) { + log_debug("adding capability: %s", *capability); + tty_term_apply(term, *capability, 1); + capability++; + } + } + term->flags |= tf->flags; + } + term->features |= feat; +} diff --git a/tty-keys.c b/tty-keys.c index 10c9c670..14952c05 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1020,7 +1020,6 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, struct client *c = tty->client; u_int i, n = 0; char tmp[64], *endptr, p[32] = { 0 }, *cp, *next; - int flags = 0; *size = 0; if (tty->flags & TTY_HAVEDA) @@ -1060,24 +1059,42 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, n++; } - /* Set terminal flags. */ + /* Add terminal features. */ switch (p[0]) { case 41: /* VT420 */ - flags |= (TERM_DECFRA|TERM_DECSLRM); + tty_add_features(&c->term_features, + "margins," + "rectfill", + ","); break; case 'M': /* mintty */ - flags |= (TERM_256COLOURS|TERM_RGBCOLOURS); + tty_add_features(&c->term_features, + "256," + "RGB," + "title", + ","); break; - case 'T': /* tmux - new versons reply to DSR which will set RGB */ - flags |= (TERM_UTF8|TERM_256COLOURS); + case 'T': /* tmux */ + tty_add_features(&c->term_features, + "256," + "RGB," + "ccolour," + "cstyle," + "overline," + "title," + "usstyle", + ","); break; case 'U': /* rxvt-unicode */ - flags |= (TERM_UTF8); + tty_add_features(&c->term_features, + "256," + "title", + ","); break; } log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); - tty_set_flags(tty, flags); + tty_update_features(tty); tty->flags |= TTY_HAVEDA; return (0); @@ -1094,7 +1111,6 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, struct client *c = tty->client; u_int i; char tmp[64]; - int flags = 0; *size = 0; if (tty->flags & TTY_HAVEDSR) @@ -1125,15 +1141,31 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, tmp[i] = '\0'; *size = 3 + i; - /* Set terminal flags. */ + /* Add terminal features. */ if (strncmp(tmp, "ITERM2 ", 7) == 0) { - flags |= (TERM_UTF8|TERM_DECSLRM|TERM_SYNC|TERM_256COLOURS| - TERM_RGBCOLOURS); - } else if (strncmp(tmp, "TMUX ", 5) == 0) - flags |= (TERM_UTF8|TERM_256COLOURS|TERM_RGBCOLOURS); + tty_add_features(&c->term_features, + "256," + "RGB," + "clipboard," + "cstyle," + "margins," + "sync," + "title,", + ","); + } else if (strncmp(tmp, "TMUX ", 5) == 0) { + tty_add_features(&c->term_features, + "256," + "RGB," + "ccolour," + "cstyle," + "overline," + "title," + "usstyle", + ","); + } log_debug("%s: received DSR %.*s", c->name, (int)*size, buf); - tty_set_flags(tty, flags); + tty_update_features(tty); tty->flags |= TTY_HAVEDSR; return (0); diff --git a/tty-term.c b/tty-term.c index d6faa6ff..1c87ac15 100644 --- a/tty-term.c +++ b/tty-term.c @@ -27,7 +27,6 @@ #include "tmux.h" -static void tty_term_override(struct tty_term *, const char *); static char *tty_term_strip(const char *); struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms); @@ -335,22 +334,18 @@ tty_term_override_next(const char *s, size_t *offset) return (value); } -static void -tty_term_override(struct tty_term *term, const char *override) +void +tty_term_apply(struct tty_term *term, const char *capabilities, int quiet) { const struct tty_term_code_entry *ent; struct tty_code *code; size_t offset = 0; char *cp, *value, *s; - const char *errstr; + const char *errstr, *name = term->name; u_int i; int n, remove; - s = tty_term_override_next(override, &offset); - if (s == NULL || fnmatch(s, term->name, 0) != 0) - return; - - while ((s = tty_term_override_next(override, &offset)) != NULL) { + while ((s = tty_term_override_next(capabilities, &offset)) != NULL) { if (*s == '\0') continue; value = NULL; @@ -369,12 +364,14 @@ tty_term_override(struct tty_term *term, const char *override) } else value = xstrdup(""); - if (remove) - log_debug("%s override: %s@", term->name, s); - else if (*value == '\0') - log_debug("%s override: %s", term->name, s); - else - log_debug("%s override: %s=%s", term->name, s, value); + if (!quiet) { + if (remove) + log_debug("%s override: %s@", name, s); + else if (*value == '\0') + log_debug("%s override: %s", name, s); + else + log_debug("%s override: %s=%s", name, s, value); + } for (i = 0; i < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; @@ -414,7 +411,7 @@ tty_term_override(struct tty_term *term, const char *override) } struct tty_term * -tty_term_find(char *name, int fd, char **cause) +tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) { struct tty_term *term; const struct tty_term_code_entry *ent; @@ -425,19 +422,14 @@ tty_term_find(char *name, int fd, char **cause) u_int i; int n, error; const char *s, *acs; + size_t offset; + char *first; - LIST_FOREACH(term, &tty_terms, entry) { - if (strcmp(term->name, name) == 0) { - term->references++; - return (term); - } - } - log_debug("new term: %s", name); + log_debug("adding term %s", name); - term = xmalloc(sizeof *term); + term = xcalloc(1, sizeof *term); + term->tty = tty; term->name = xstrdup(name); - term->references = 1; - term->flags = 0; term->codes = xcalloc(tty_term_ncodes(), sizeof *term->codes); LIST_INSERT_HEAD(&tty_terms, term, entry); @@ -495,12 +487,31 @@ tty_term_find(char *name, int fd, char **cause) } } + /* Apply terminal features. */ + o = options_get_only(global_options, "terminal-features"); + a = options_array_first(o); + while (a != NULL) { + ov = options_array_item_value(a); + s = ov->string; + + offset = 0; + first = tty_term_override_next(s, &offset); + if (first != NULL && fnmatch(first, term->name, 0) == 0) + tty_add_features(feat, s + offset, ":"); + a = options_array_next(a); + } + /* Apply terminal overrides. */ o = options_get_only(global_options, "terminal-overrides"); a = options_array_first(o); while (a != NULL) { ov = options_array_item_value(a); - tty_term_override(term, ov->string); + s = ov->string; + + offset = 0; + first = tty_term_override_next(s, &offset); + if (first != NULL && fnmatch(first, term->name, 0) == 0) + tty_term_apply(term, s + offset, 0); a = options_array_next(a); } @@ -523,19 +534,18 @@ tty_term_find(char *name, int fd, char **cause) goto error; } - /* Set flag if terminal has 256 colours. */ - if (tty_term_number(term, TTYC_COLORS) >= 256) - term->flags |= TERM_256COLOURS; + /* Add RGB feature if terminal has RGB colours. */ + if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) && + (!tty_term_has(term, TTYC_SETRGBF) || + !tty_term_has(term, TTYC_SETRGBB))) + tty_add_features(feat, "RGB", ":,"); - /* Set flag if terminal has RGB colours. */ - if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) || - (tty_term_has(term, TTYC_SETRGBF) && - tty_term_has(term, TTYC_SETRGBB))) - term->flags |= TERM_RGBCOLOURS; + /* Add feature if terminal has XT. */ + if (tty_term_flag(term, TTYC_XT)) + tty_add_features(feat, "title", ":,"); - /* Set flag if terminal has synchronized updates. */ - if (tty_term_flag(term, TTYC_SYNC)) - term->flags |= TERM_SYNC; + /* Apply the features. */ + tty_apply_features(term, *feat); /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 @@ -559,18 +569,6 @@ tty_term_find(char *name, int fd, char **cause) for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) term->acs[(u_char) acs[0]][0] = acs[1]; - /* On terminals with xterm titles (XT), fill in tsl and fsl. */ - if (tty_term_flag(term, TTYC_XT) && - !tty_term_has(term, TTYC_TSL) && - !tty_term_has(term, TTYC_FSL)) { - code = &term->codes[TTYC_TSL]; - code->value.string = xstrdup("\033]0;"); - code->type = TTYCODE_STRING; - code = &term->codes[TTYC_FSL]; - code->value.string = xstrdup("\007"); - code->type = TTYCODE_STRING; - } - /* Log the capabilities. */ for (i = 0; i < tty_term_ncodes(); i++) log_debug("%s%s", name, tty_term_describe(term, i)); @@ -587,10 +585,7 @@ tty_term_free(struct tty_term *term) { u_int i; - if (--term->references != 0) - return; - - LIST_REMOVE(term, entry); + log_debug("removing term %s", term->name); for (i = 0; i < tty_term_ncodes(); i++) { if (term->codes[i].type == TTYCODE_STRING) @@ -598,6 +593,7 @@ tty_term_free(struct tty_term *term) } free(term->codes); + LIST_REMOVE(term, entry); free(term->name); free(term); } diff --git a/tty.c b/tty.c index cdfedf6c..dfc35873 100644 --- a/tty.c +++ b/tty.c @@ -74,7 +74,7 @@ static void tty_default_attributes(struct tty *, struct window_pane *, u_int); #define tty_use_margin(tty) \ - (tty_get_flags(tty) & TERM_DECSLRM) + (tty->term->flags & TERM_DECSLRM) #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) @@ -96,27 +96,19 @@ tty_create_log(void) } int -tty_init(struct tty *tty, struct client *c, int fd, char *term) +tty_init(struct tty *tty, struct client *c, int fd) { if (!isatty(fd)) return (-1); memset(tty, 0, sizeof *tty); - if (term == NULL || *term == '\0') - tty->term_name = xstrdup("unknown"); - else - tty->term_name = xstrdup(term); - tty->fd = fd; tty->client = c; tty->cstyle = 0; tty->ccolour = xstrdup(""); - tty->flags = 0; - tty->term_flags = 0; - return (0); } @@ -256,7 +248,10 @@ tty_write_callback(__unused int fd, __unused short events, void *data) int tty_open(struct tty *tty, char **cause) { - tty->term = tty_term_find(tty->term_name, tty->fd, cause); + struct client *c = tty->client; + + tty->term = tty_term_create(tty, c->term_name, &c->term_features, + tty->fd, cause); if (tty->term == NULL) { tty_close(tty); return (-1); @@ -466,28 +461,20 @@ void tty_free(struct tty *tty) { tty_close(tty); - free(tty->ccolour); - free(tty->term_name); } void -tty_set_flags(struct tty *tty, int flags) +tty_update_features(struct tty *tty) { - tty->term_flags |= flags; + struct client *c = tty->client; + + tty_apply_features(tty->term, c->term_features); if (tty_use_margin(tty)) tty_puts(tty, "\033[?69h"); /* DECLRMM */ } -int -tty_get_flags(struct tty *tty) -{ - if (tty->term != NULL) - return (tty->term->flags|tty->term_flags); - return (tty->term_flags); -} - void tty_raw(struct tty *tty, const char *s) { @@ -585,7 +572,7 @@ tty_putc(struct tty *tty, u_char ch) { const char *acs; - if ((tty_get_flags(tty) & TERM_NOXENL) && + if ((tty->term->flags & TERM_NOXENL) && ch >= 0x20 && ch != 0x7f && tty->cy == tty->sy - 1 && tty->cx + 1 >= tty->sx) @@ -611,7 +598,7 @@ tty_putc(struct tty *tty, u_char ch) * where we think it should be after a line wrap - this * means it works on sensible terminals as well. */ - if (tty_get_flags(tty) & TERM_NOXENL) + if (tty->term->flags & TERM_NOXENL) tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx); } else tty->cx++; @@ -621,7 +608,7 @@ tty_putc(struct tty *tty, u_char ch) void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { - if ((tty_get_flags(tty) & TERM_NOXENL) && + if ((tty->term->flags & TERM_NOXENL) && tty->cy == tty->sy - 1 && tty->cx + len >= tty->sx) len = tty->sx - tty->cx - 1; @@ -1179,7 +1166,7 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, * background colour isn't default (because it doesn't work * after SGR 0). */ - if ((tty_get_flags(tty) & TERM_DECFRA) && !COLOUR_DEFAULT(bg)) { + if ((tty->term->flags & TERM_DECFRA) && !COLOUR_DEFAULT(bg)) { xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", py + 1, px + 1, py + ny, px + nx); tty_puts(tty, tmp); @@ -1258,7 +1245,7 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) return (gc); /* UTF-8 terminal and a UTF-8 character - fine. */ - if (tty_get_flags(tty) & TERM_UTF8) + if (tty->client->flags & CLIENT_UTF8) return (gc); /* Replace by the right number of underscores. */ @@ -1440,7 +1427,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, void tty_sync_start(struct tty *tty) { - if ((~tty->flags & TTY_SYNCING) && (tty_get_flags(tty) & TERM_SYNC)) { + if ((~tty->flags & TTY_SYNCING) && tty_term_has(tty->term, TTYC_SYNC)) { tty_puts(tty, "\033P=1s\033\\"); tty->flags |= TTY_SYNCING; } @@ -1449,7 +1436,7 @@ tty_sync_start(struct tty *tty) void tty_sync_end(struct tty *tty) { - if (tty_get_flags(tty) & TERM_SYNC) { + if ((tty->flags & TTY_SYNCING) && tty_term_has(tty->term, TTYC_SYNC)) { tty_puts(tty, "\033P=2s\033\\"); tty->flags &= ~TTY_SYNCING; } @@ -1916,7 +1903,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || - (tty_get_flags(tty) & TERM_NOXENL) || + (tty->term->flags & TERM_NOXENL) || ctx->xoff + ctx->ocx != 0 || ctx->yoff + ctx->ocy != tty->cy + 1 || tty->cx < tty->sx || @@ -1977,7 +1964,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) const struct grid_cell *gcp; /* Skip last character if terminal is stupid. */ - if ((tty_get_flags(tty) & TERM_NOXENL) && + if ((tty->term->flags & TERM_NOXENL) && tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1) return; @@ -2140,7 +2127,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || - (tty_get_flags(tty) & TERM_NOXENL) || + (tty->term->flags & TERM_NOXENL) || ctx->xoff + cx != 0 || ctx->yoff + cy != tty->cy + 1 || tty->cx < tty->sx || @@ -2480,14 +2467,14 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Is this a 24-bit colour? */ if (gc->fg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ - if (tty_get_flags(tty) & TERM_RGBCOLOURS) + if (tty->term->flags & TERM_RGBCOLOURS) return; colour_split_rgb(gc->fg, &r, &g, &b); gc->fg = colour_find_rgb(r, g, b); } /* How many colours does this terminal have? */ - if (tty_get_flags(tty) & TERM_256COLOURS) + if (tty->term->flags & TERM_256COLOURS) colours = 256; else colours = tty_term_number(tty->term, TTYC_COLORS); @@ -2529,14 +2516,14 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Is this a 24-bit colour? */ if (gc->bg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ - if (tty_get_flags(tty) & TERM_RGBCOLOURS) + if (tty->term->flags & TERM_RGBCOLOURS) return; colour_split_rgb(gc->bg, &r, &g, &b); gc->bg = colour_find_rgb(r, g, b); } /* How many colours does this terminal have? */ - if (tty_get_flags(tty) & TERM_256COLOURS) + if (tty->term->flags & TERM_256COLOURS) colours = 256; else colours = tty_term_number(tty->term, TTYC_COLORS); @@ -2597,7 +2584,7 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) /* Is this an aixterm bright colour? */ if (gc->fg >= 90 && gc->fg <= 97) { - if (tty_get_flags(tty) & TERM_256COLOURS) { + if (tty->term->flags & TERM_256COLOURS) { xsnprintf(s, sizeof s, "\033[%dm", gc->fg); tty_puts(tty, s); } else @@ -2629,7 +2616,7 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) /* Is this an aixterm bright colour? */ if (gc->bg >= 90 && gc->bg <= 97) { - if (tty_get_flags(tty) & TERM_256COLOURS) { + if (tty->term->flags & TERM_256COLOURS) { xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10); tty_puts(tty, s); } else @@ -2674,67 +2661,25 @@ static int tty_try_colour(struct tty *tty, int colour, const char *type) { u_char r, g, b; - char s[32]; if (colour & COLOUR_FLAG_256) { - /* - * If the user has specified -2 to the client (meaning - * TERM_256COLOURS is set), setaf and setab may not work (or - * they may not want to use them), so send the usual sequence. - * - * Also if RGB is set, setaf and setab do not support the 256 - * colour palette so use the sequences directly there too. - */ - if ((tty_get_flags(tty) & TERM_256COLOURS) || - tty_term_has(tty->term, TTYC_RGB)) - goto fallback_256; - - /* - * If the terminfo entry has 256 colours and setaf and setab - * exist, assume that they work correctly. - */ - if (tty_get_flags(tty) & TERM_256COLOURS) { - if (*type == '3') { - if (!tty_term_has(tty->term, TTYC_SETAF)) - goto fallback_256; - tty_putcode1(tty, TTYC_SETAF, colour & 0xff); - } else { - if (!tty_term_has(tty->term, TTYC_SETAB)) - goto fallback_256; - tty_putcode1(tty, TTYC_SETAB, colour & 0xff); - } - return (0); - } - goto fallback_256; + if (*type == '3' && tty_term_has(tty->term, TTYC_SETAF)) + tty_putcode1(tty, TTYC_SETAF, colour & 0xff); + else if (tty_term_has(tty->term, TTYC_SETAB)) + tty_putcode1(tty, TTYC_SETAB, colour & 0xff); + return (0); } if (colour & COLOUR_FLAG_RGB) { colour_split_rgb(colour & 0xffffff, &r, &g, &b); - if (*type == '3') { - if (!tty_term_has(tty->term, TTYC_SETRGBF)) - goto fallback_rgb; + if (*type == '3' && tty_term_has(tty->term, TTYC_SETRGBF)) tty_putcode3(tty, TTYC_SETRGBF, r, g, b); - } else { - if (!tty_term_has(tty->term, TTYC_SETRGBB)) - goto fallback_rgb; + else if (tty_term_has(tty->term, TTYC_SETRGBB)) tty_putcode3(tty, TTYC_SETRGBB, r, g, b); - } return (0); } return (-1); - -fallback_256: - xsnprintf(s, sizeof s, "\033[%s;5;%dm", type, colour & 0xff); - log_debug("%s: 256 colour fallback: %s", tty->client->name, s); - tty_puts(tty, s); - return (0); - -fallback_rgb: - xsnprintf(s, sizeof s, "\033[%s;2;%d;%d;%dm", type, r, g, b); - log_debug("%s: RGB colour fallback: %s", tty->client->name, s); - tty_puts(tty, s); - return (0); } static void From 135bb1edeeab3faae8001100aa7c173be9aa91e1 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2020 13:38:48 +0000 Subject: [PATCH 0206/1006] Change the Sync capability to be a string instead of a flag. --- tmux.1 | 2 +- tty-features.c | 3 +-- tty-term.c | 2 +- tty.c | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tmux.1 b/tmux.1 index ca4e30e0..648aed4d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5572,7 +5572,7 @@ If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. .It Em \&Sync -Show that the terminal supports synchronized updates. +Start (parameter is 1) or end (parameter is 2) a synchronized update. .It Em \&Tc Indicate that the terminal supports the .Ql direct colour diff --git a/tty-features.c b/tty-features.c index 58cbae7b..b6934673 100644 --- a/tty-features.c +++ b/tty-features.c @@ -35,7 +35,6 @@ * Also: * - XT is used to decide whether to send DA and DSR, * - DECSLRM and DECFRA use a flag instead of capabilities. - * - Sync is a flag rather than a string capability. * - UTF-8 is a separate flag on the client; needed for unattached clients. */ @@ -148,7 +147,7 @@ static struct tty_feature tty_feature_ccolour = { /* Terminal supports synchronized updates. */ static const char *tty_feature_sync_capabilities[] = { - "Sync", + "Sync=\\EP=%p1%ds\\E\\\\", NULL }; static struct tty_feature tty_feature_sync = { diff --git a/tty-term.c b/tty-term.c index 1c87ac15..e1b026ea 100644 --- a/tty-term.c +++ b/tty-term.c @@ -259,7 +259,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SMXX] = { TTYCODE_STRING, "smxx" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, - [TTYC_SYNC] = { TTYCODE_FLAG, "Sync" }, + [TTYC_SYNC] = { TTYCODE_STRING, "Sync" }, [TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, [TTYC_U8] = { TTYCODE_NUMBER, "U8" }, diff --git a/tty.c b/tty.c index dfc35873..e1c629ec 100644 --- a/tty.c +++ b/tty.c @@ -1428,7 +1428,7 @@ void tty_sync_start(struct tty *tty) { if ((~tty->flags & TTY_SYNCING) && tty_term_has(tty->term, TTYC_SYNC)) { - tty_puts(tty, "\033P=1s\033\\"); + tty_putcode1(tty, TTYC_SYNC, 1); tty->flags |= TTY_SYNCING; } } @@ -1437,7 +1437,7 @@ void tty_sync_end(struct tty *tty) { if ((tty->flags & TTY_SYNCING) && tty_term_has(tty->term, TTYC_SYNC)) { - tty_puts(tty, "\033P=2s\033\\"); + tty_putcode1(tty, TTYC_SYNC, 2); tty->flags &= ~TTY_SYNCING; } } From 2083a6ea2050fb211eab3da0df0ff5a40b4973b4 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2020 14:59:31 +0000 Subject: [PATCH 0207/1006] Change how sync works to always send the end sequence after all output is done when we are returning to the event loop (since we always move the cursor at that point). Also a man fix from jmc. --- screen-redraw.c | 2 -- screen-write.c | 9 --------- server-client.c | 12 +++++++++++- tmux.1 | 2 +- tmux.h | 1 - tty-keys.c | 2 +- tty.c | 26 ++++++++++++++++---------- 7 files changed, 29 insertions(+), 25 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index c9e70590..8e74fe97 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -458,7 +458,6 @@ screen_redraw_screen(struct client *c) } tty_reset(&c->tty); - tty_sync_end(&c->tty); } /* Redraw a single pane. */ @@ -476,7 +475,6 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) screen_redraw_draw_pane(&ctx, wp); tty_reset(&c->tty); - tty_sync_end(&c->tty); } /* Draw a border cell. */ diff --git a/screen-write.c b/screen-write.c index 55d399a7..042f2fa8 100644 --- a/screen-write.c +++ b/screen-write.c @@ -119,7 +119,6 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, ttyctx->orupper = s->rupper; if (sync && !ctx->sync && ttyctx->wp != NULL) { - log_debug("%s: starting sync", __func__); tty_write(tty_cmd_syncstart, ttyctx); ctx->sync = 1; } @@ -184,8 +183,6 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, void screen_write_stop(struct screen_write_ctx *ctx) { - struct tty_ctx ttyctx; - screen_write_collect_end(ctx); screen_write_collect_flush(ctx, 0, __func__); @@ -196,12 +193,6 @@ screen_write_stop(struct screen_write_ctx *ctx) ctx->wp->skipped += ctx->skipped; } - if (ctx->sync) { - screen_write_initctx(ctx, &ttyctx, 0); - tty_write(tty_cmd_syncend, &ttyctx); - log_debug("%s: ending sync", __func__); - } - free(ctx->item); } diff --git a/server-client.c b/server-client.c index cd850593..8da55ac0 100644 --- a/server-client.c +++ b/server-client.c @@ -1541,7 +1541,7 @@ server_client_reset_state(struct client *c) struct window_pane *wp = w->active, *loop; struct screen *s; struct options *oo = c->session->options; - int mode, cursor; + int mode, cursor, flags; u_int cx = 0, cy = 0, ox, oy, sx, sy; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) @@ -1606,6 +1606,16 @@ server_client_reset_state(struct client *c) /* Set the terminal mode and reset attributes. */ tty_update_mode(&c->tty, mode, s); tty_reset(&c->tty); + + /* + * All writing must be done, send a sync end (if it was started). It + * may have been started by redrawing so needs to go out even if the + * block flag is set. + */ + flags = (c->tty.flags & TTY_BLOCK); + c->tty.flags &= ~TTY_BLOCK; + tty_sync_end(&c->tty); + c->tty.flags |= flags; } /* Repeat time callback. */ diff --git a/tmux.1 b/tmux.1 index 648aed4d..e54817d5 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5534,7 +5534,7 @@ The server crashed or otherwise exited without telling the client the reason. .Sh TERMINFO EXTENSIONS .Nm understands some unofficial extensions to -.Xr terminfo 5. +.Xr terminfo 5 . It is not normally necessary to set these manually, instead the .Ic terminal-features option should be used. diff --git a/tmux.h b/tmux.h index 3853b09f..5d7dbba7 100644 --- a/tmux.h +++ b/tmux.h @@ -1992,7 +1992,6 @@ void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); -void tty_cmd_syncend(struct tty *, const struct tty_ctx *); /* tty-term.c */ extern struct tty_terms tty_terms; diff --git a/tty-keys.c b/tty-keys.c index 14952c05..aa775d69 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1150,7 +1150,7 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, "cstyle," "margins," "sync," - "title,", + "title", ","); } else if (strncmp(tmp, "TMUX ", 5) == 0) { tty_add_features(&c->term_features, diff --git a/tty.c b/tty.c index e1c629ec..0214524c 100644 --- a/tty.c +++ b/tty.c @@ -1427,18 +1427,30 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, void tty_sync_start(struct tty *tty) { - if ((~tty->flags & TTY_SYNCING) && tty_term_has(tty->term, TTYC_SYNC)) { + if (tty->flags & TTY_BLOCK) + return; + if (tty->flags & TTY_SYNCING) + return; + tty->flags |= TTY_SYNCING; + + if (tty_term_has(tty->term, TTYC_SYNC)) { + log_debug("%s sync start", tty->client->name); tty_putcode1(tty, TTYC_SYNC, 1); - tty->flags |= TTY_SYNCING; } } void tty_sync_end(struct tty *tty) { - if ((tty->flags & TTY_SYNCING) && tty_term_has(tty->term, TTYC_SYNC)) { + if (tty->flags & TTY_BLOCK) + return; + if (~tty->flags & TTY_SYNCING) + return; + tty->flags &= ~TTY_SYNCING; + + if (tty_term_has(tty->term, TTYC_SYNC)) { + log_debug("%s sync end", tty->client->name); tty_putcode1(tty, TTYC_SYNC, 2); - tty->flags &= ~TTY_SYNCING; } } @@ -1952,12 +1964,6 @@ tty_cmd_syncstart(struct tty *tty, __unused const struct tty_ctx *ctx) tty_sync_start(tty); } -void -tty_cmd_syncend(struct tty *tty, __unused const struct tty_ctx *ctx) -{ - tty_sync_end(tty); -} - static void tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) { From 117ec1b2e603c2692ab564947b099ec79a20150f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2020 15:37:32 +0000 Subject: [PATCH 0208/1006] Apply terminal-overrides after terminal detection, it always takes precedence. --- tmux.h | 3 ++- tty-features.c | 7 +++++-- tty-term.c | 41 ++++++++++++++++++++++++++--------------- tty.c | 3 ++- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/tmux.h b/tmux.h index 5d7dbba7..3fe32b50 100644 --- a/tmux.h +++ b/tmux.h @@ -1997,6 +1997,7 @@ void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); extern struct tty_terms tty_terms; u_int tty_term_ncodes(void); void tty_term_apply(struct tty_term *, const char *, int); +void tty_term_apply_overrides(struct tty_term *); struct tty_term *tty_term_create(struct tty *, char *, int *, int, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); @@ -2017,7 +2018,7 @@ const char *tty_term_describe(struct tty_term *, enum tty_code_code); /* tty-features.c */ void tty_add_features(int *, const char *, const char *); const char *tty_get_features(int); -void tty_apply_features(struct tty_term *, int); +int tty_apply_features(struct tty_term *, int); /* tty-acs.c */ int tty_acs_needed(struct tty *); diff --git a/tty-features.c b/tty-features.c index b6934673..3b12b068 100644 --- a/tty-features.c +++ b/tty-features.c @@ -232,7 +232,7 @@ tty_get_features(int feat) return (s); } -void +int tty_apply_features(struct tty_term *term, int feat) { const struct tty_feature *tf; @@ -240,7 +240,7 @@ tty_apply_features(struct tty_term *term, int feat) u_int i; if (feat == 0) - return; + return (0); log_debug("applying terminal features: %s", tty_get_features(feat)); for (i = 0; i < nitems(tty_features); i++) { @@ -259,5 +259,8 @@ tty_apply_features(struct tty_term *term, int feat) } term->flags |= tf->flags; } + if ((term->features | feat) == term->features) + return (0); term->features |= feat; + return (1); } diff --git a/tty-term.c b/tty-term.c index e1b026ea..f4a18b63 100644 --- a/tty-term.c +++ b/tty-term.c @@ -410,6 +410,30 @@ tty_term_apply(struct tty_term *term, const char *capabilities, int quiet) } } +void +tty_term_apply_overrides(struct tty_term *term) +{ + struct options_entry *o; + struct options_array_item *a; + union options_value *ov; + const char *s; + size_t offset; + char *first; + + o = options_get_only(global_options, "terminal-overrides"); + a = options_array_first(o); + while (a != NULL) { + ov = options_array_item_value(a); + s = ov->string; + + offset = 0; + first = tty_term_override_next(s, &offset); + if (first != NULL && fnmatch(first, term->name, 0) == 0) + tty_term_apply(term, s + offset, 0); + a = options_array_next(a); + } +} + struct tty_term * tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) { @@ -501,20 +525,6 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) a = options_array_next(a); } - /* Apply terminal overrides. */ - o = options_get_only(global_options, "terminal-overrides"); - a = options_array_first(o); - while (a != NULL) { - ov = options_array_item_value(a); - s = ov->string; - - offset = 0; - first = tty_term_override_next(s, &offset); - if (first != NULL && fnmatch(first, term->name, 0) == 0) - tty_term_apply(term, s + offset, 0); - a = options_array_next(a); - } - /* Delete curses data. */ del_curterm(cur_term); @@ -544,8 +554,9 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) if (tty_term_flag(term, TTYC_XT)) tty_add_features(feat, "title", ":,"); - /* Apply the features. */ + /* Apply the features and overrides. */ tty_apply_features(term, *feat); + tty_term_apply_overrides(term); /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 diff --git a/tty.c b/tty.c index 0214524c..556931da 100644 --- a/tty.c +++ b/tty.c @@ -469,7 +469,8 @@ tty_update_features(struct tty *tty) { struct client *c = tty->client; - tty_apply_features(tty->term, c->term_features); + if (tty_apply_features(tty->term, c->term_features)) + tty_term_apply_overrides(tty->term); if (tty_use_margin(tty)) tty_puts(tty, "\033[?69h"); /* DECLRMM */ From 4a5182e6658907f876581fbcf4c774bf86d0d953 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2020 15:49:05 +0000 Subject: [PATCH 0209/1006] Always start sync for output in panes that are not the active pane. --- screen-write.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/screen-write.c b/screen-write.c index 042f2fa8..98a4a701 100644 --- a/screen-write.c +++ b/screen-write.c @@ -118,7 +118,9 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, ttyctx->orlower = s->rlower; ttyctx->orupper = s->rupper; - if (sync && !ctx->sync && ttyctx->wp != NULL) { + if (ctx->wp != NULL && + !ctx->sync && + (sync || ctx->wp != ctx->wp->window->active)) { tty_write(tty_cmd_syncstart, ttyctx); ctx->sync = 1; } From 1cabccbb2b4ebad012f2109d07f5f5b224d5a45e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2020 05:26:13 +0000 Subject: [PATCH 0210/1006] xterm* needs XT also. --- options-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index 722429f7..6537e2c9 100644 --- a/options-table.c +++ b/options-table.c @@ -261,7 +261,7 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, - .default_str = "tmux*:XT,screen*:XT", + .default_str = "tmux*:XT,screen*:XT,xterm*:XT", .separator = "," }, From d0b8f5340eb17e76769699caafdb0bd87bcf816b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2020 06:32:40 +0000 Subject: [PATCH 0211/1006] Do not clear client pane redraw flags until the redraw actually happens. --- tty.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tty.c b/tty.c index 556931da..98a557b5 100644 --- a/tty.c +++ b/tty.c @@ -1491,6 +1491,7 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), * Redraw is already deferred to redraw another pane - * redraw this one also when that happens. */ + log_debug("adding %%%u to deferred redraw", wp->id); wp->flags |= PANE_REDRAW; break; } From d524cb64e7f8ed0b2f7046daf0d93bcd2ec87d0b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2020 06:32:40 +0000 Subject: [PATCH 0212/1006] Do not clear client pane redraw flags until the redraw actually happens. From 57bd6e0447b0e2b99ddc1c020dbb12ad7f92685e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2020 06:34:13 +0000 Subject: [PATCH 0213/1006] Turn off the block flag to reset the state or the cursor will not be moved back to the right place. --- server-client.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/server-client.c b/server-client.c index 8da55ac0..eb29aebb 100644 --- a/server-client.c +++ b/server-client.c @@ -1537,6 +1537,7 @@ focused: static void server_client_reset_state(struct client *c) { + struct tty *tty = &c->tty; struct window *w = c->session->curw->window; struct window_pane *wp = w->active, *loop; struct screen *s; @@ -1547,6 +1548,10 @@ server_client_reset_state(struct client *c) if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; + /* Disable the block flag. */ + flags = (tty->flags & TTY_BLOCK); + tty->flags &= ~TTY_BLOCK; + /* Get mode from overlay if any, else from screen. */ if (c->overlay_draw != NULL) { s = NULL; @@ -1561,13 +1566,13 @@ server_client_reset_state(struct client *c) log_debug("%s: client %s mode %x", __func__, c->name, mode); /* Reset region and margin. */ - tty_region_off(&c->tty); - tty_margin_off(&c->tty); + tty_region_off(tty); + tty_margin_off(tty); /* Move cursor to pane cursor and offset. */ if (c->overlay_draw == NULL) { cursor = 0; - tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); + tty_window_offset(tty, &ox, &oy, &sx, &sy); if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx && wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) { cursor = 1; @@ -1581,7 +1586,8 @@ server_client_reset_state(struct client *c) if (!cursor) mode &= ~MODE_CURSOR; } - tty_cursor(&c->tty, cx, cy); + log_debug("%s: cursor to %u,%u", __func__, cx, cy); + tty_cursor(tty, cx, cy); /* * Set mouse mode if requested. To support dragging, always use button @@ -1604,18 +1610,12 @@ server_client_reset_state(struct client *c) mode &= ~MODE_BRACKETPASTE; /* Set the terminal mode and reset attributes. */ - tty_update_mode(&c->tty, mode, s); - tty_reset(&c->tty); + tty_update_mode(tty, mode, s); + tty_reset(tty); - /* - * All writing must be done, send a sync end (if it was started). It - * may have been started by redrawing so needs to go out even if the - * block flag is set. - */ - flags = (c->tty.flags & TTY_BLOCK); - c->tty.flags &= ~TTY_BLOCK; - tty_sync_end(&c->tty); - c->tty.flags |= flags; + /* All writing must be done, send a sync end (if it was started). */ + tty_sync_end(tty); + tty->flags |= flags; } /* Repeat time callback. */ @@ -1738,7 +1738,6 @@ server_client_check_redraw(struct client *c) } if (~c->flags & CLIENT_REDRAWWINDOW) { - c->redraw_panes = 0; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->flags & PANE_REDRAW) { log_debug("%s: pane %%%u needs redraw", @@ -1784,6 +1783,7 @@ server_client_check_redraw(struct client *c) tty_update_mode(tty, mode, NULL); screen_redraw_pane(c, wp); } + c->redraw_panes = 0; c->flags &= ~CLIENT_REDRAWPANES; } From 18671a27b6f46269bc29804a0f172ce0ad3ff4e2 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2020 06:34:13 +0000 Subject: [PATCH 0214/1006] Turn off the block flag to reset the state or the cursor will not be moved back to the right place. From bd91015b1398bd986644657618aef38794c5dc1f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2020 10:37:11 +0000 Subject: [PATCH 0215/1006] 256 and RGB features can imply AX (for aixterm colours). --- tty-features.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tty-features.c b/tty-features.c index 3b12b068..ae0869e1 100644 --- a/tty-features.c +++ b/tty-features.c @@ -74,6 +74,7 @@ static struct tty_feature tty_feature_clipboard = { * 256 palette. */ static const char *tty_feature_rgb_capabilities[] = { + "AX", "setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm", "setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm", "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", @@ -88,6 +89,7 @@ static struct tty_feature tty_feature_rgb = { /* Terminal supports 256 colours. */ static const char *tty_feature_256_capabilities[] = { + "AX", "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", NULL From 9a60d41db4da149275aecadee24d089af1db9ecb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2020 10:37:11 +0000 Subject: [PATCH 0216/1006] 256 and RGB features can imply AX (for aixterm colours). From 445dfa8512b3b5552a444dd03958cdec3f968f83 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2020 13:48:56 +0000 Subject: [PATCH 0217/1006] Move the background colour to clear with (if any) up as well as the data when scrolling, redraw problem reported by sthen@. --- screen-write.c | 3 ++- tty-features.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/screen-write.c b/screen-write.c index 98a4a701..afa1e96a 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1372,10 +1372,11 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx) for (y = s->rupper; y < s->rlower; y++) { cl = &ctx->s->write_list[y + 1]; TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry); + ctx->s->write_list[y].bg = cl->bg; ctx->s->write_list[y].data = cl->data; } - ctx->s->write_list[s->rlower].data = saved; ctx->s->write_list[s->rlower].bg = 1 + 8; + ctx->s->write_list[s->rlower].data = saved; } /* Flush collected lines. */ diff --git a/tty-features.c b/tty-features.c index ae0869e1..1996c750 100644 --- a/tty-features.c +++ b/tty-features.c @@ -33,8 +33,8 @@ * - alternate escape (under XT). * * Also: - * - XT is used to decide whether to send DA and DSR, - * - DECSLRM and DECFRA use a flag instead of capabilities. + * - XT is used to decide whether to send DA and DSR; + * - DECSLRM and DECFRA use a flag instead of capabilities; * - UTF-8 is a separate flag on the client; needed for unattached clients. */ From de5163a634374d64f98a79afafe0c2a526c21a04 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2020 06:57:13 +0000 Subject: [PATCH 0218/1006] Change so main-pane-width and height can be given as a percentage. --- arguments.c | 27 +++++++++++++++++---------- layout-set.c | 43 +++++++++++++++++++++++++++++++++---------- options-table.c | 24 ++++++++---------------- tmux.1 | 6 ++++++ tmux.h | 2 ++ 5 files changed, 66 insertions(+), 36 deletions(-) diff --git a/arguments.c b/arguments.c index 5b882ea1..547cec00 100644 --- a/arguments.c +++ b/arguments.c @@ -352,22 +352,29 @@ long long args_percentage(struct args *args, u_char ch, long long minval, long long maxval, long long curval, char **cause) { - const char *errstr; - long long ll; + const char *value; struct args_entry *entry; - struct args_value *value; - size_t valuelen; - char *copy; if ((entry = args_find(args, ch)) == NULL) { *cause = xstrdup("missing"); return (0); } - value = TAILQ_LAST(&entry->values, args_values); - valuelen = strlen(value->value); + value = TAILQ_LAST(&entry->values, args_values)->value; + return (args_string_percentage(value, minval, maxval, curval, cause)); +} - if (value->value[valuelen - 1] == '%') { - copy = xstrdup(value->value); +/* Convert a string to a number which may be a percentage. */ +long long +args_string_percentage(const char *value, long long minval, long long maxval, + long long curval, char **cause) +{ + const char *errstr; + long long ll; + size_t valuelen = strlen(value); + char *copy; + + if (value[valuelen - 1] == '%') { + copy = xstrdup(value); copy[valuelen - 1] = '\0'; ll = strtonum(copy, 0, 100, &errstr); @@ -386,7 +393,7 @@ args_percentage(struct args *args, u_char ch, long long minval, return (0); } } else { - ll = strtonum(value->value, minval, maxval, &errstr); + ll = strtonum(value, minval, maxval, &errstr); if (errstr != NULL) { *cause = xstrdup(errstr); return (0); diff --git a/layout-set.c b/layout-set.c index f712b059..9ef28416 100644 --- a/layout-set.c +++ b/layout-set.c @@ -18,6 +18,7 @@ #include +#include #include #include "tmux.h" @@ -186,6 +187,8 @@ layout_set_main_h(struct window *w) struct window_pane *wp; struct layout_cell *lc, *lcmain, *lcother, *lcchild; u_int n, mainh, otherh, sx, sy; + char *cause; + const char *s; layout_print_cell(w->layout_root, __func__, 1); @@ -198,8 +201,15 @@ layout_set_main_h(struct window *w) /* Find available height - take off one line for the border. */ sy = w->sy - 1; - /* Get the main pane height and work out the other pane height. */ - mainh = options_get_number(w->options, "main-pane-height"); + /* Get the main pane height. */ + s = options_get_string(w->options, "main-pane-height"); + mainh = args_string_percentage(s, 0, sy, sy, &cause); + if (cause != NULL) { + mainh = 24; + free(cause); + } + + /* Work out the other pane height. */ if (mainh + PANE_MINIMUM >= sy) { if (sy <= PANE_MINIMUM + PANE_MINIMUM) mainh = PANE_MINIMUM; @@ -207,10 +217,12 @@ layout_set_main_h(struct window *w) mainh = sy - PANE_MINIMUM; otherh = PANE_MINIMUM; } else { - otherh = options_get_number(w->options, "other-pane-height"); - if (otherh == 0) + s = options_get_string(w->options, "other-pane-height"); + otherh = args_string_percentage(s, 0, sy, sy, &cause); + if (cause != NULL || otherh == 0) { otherh = sy - mainh; - else if (otherh > sy || sy - otherh < mainh) + free(cause); + } else if (otherh > sy || sy - otherh < mainh) otherh = sy - mainh; else mainh = sy - otherh; @@ -273,6 +285,8 @@ layout_set_main_v(struct window *w) struct window_pane *wp; struct layout_cell *lc, *lcmain, *lcother, *lcchild; u_int n, mainw, otherw, sx, sy; + char *cause; + const char *s; layout_print_cell(w->layout_root, __func__, 1); @@ -285,8 +299,15 @@ layout_set_main_v(struct window *w) /* Find available width - take off one line for the border. */ sx = w->sx - 1; - /* Get the main pane width and work out the other pane width. */ - mainw = options_get_number(w->options, "main-pane-width"); + /* Get the main pane width. */ + s = options_get_string(w->options, "main-pane-width"); + mainw = args_string_percentage(s, 0, sx, sx, &cause); + if (cause != NULL) { + mainw = 80; + free(cause); + } + + /* Work out the other pane width. */ if (mainw + PANE_MINIMUM >= sx) { if (sx <= PANE_MINIMUM + PANE_MINIMUM) mainw = PANE_MINIMUM; @@ -294,10 +315,12 @@ layout_set_main_v(struct window *w) mainw = sx - PANE_MINIMUM; otherw = PANE_MINIMUM; } else { - otherw = options_get_number(w->options, "other-pane-width"); - if (otherw == 0) + s = options_get_string(w->options, "other-pane-width"); + otherw = args_string_percentage(s, 0, sx, sx, &cause); + if (cause != NULL || otherw == 0) { otherw = sx - mainw; - else if (otherw > sx || sx - otherw < mainw) + free(cause); + } else if (otherw > sx || sx - otherw < mainw) otherw = sx - mainw; else mainw = sx - otherw; diff --git a/options-table.c b/options-table.c index 6537e2c9..23e5af6f 100644 --- a/options-table.c +++ b/options-table.c @@ -647,19 +647,15 @@ const struct options_table_entry options_table[] = { }, { .name = "main-pane-height", - .type = OPTIONS_TABLE_NUMBER, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .minimum = 1, - .maximum = INT_MAX, - .default_num = 24 + .default_str = "24" }, { .name = "main-pane-width", - .type = OPTIONS_TABLE_NUMBER, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .minimum = 1, - .maximum = INT_MAX, - .default_num = 80 + .default_str = "80" }, { .name = "mode-keys", @@ -696,19 +692,15 @@ const struct options_table_entry options_table[] = { }, { .name = "other-pane-height", - .type = OPTIONS_TABLE_NUMBER, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 + .default_str = "0" }, { .name = "other-pane-width", - .type = OPTIONS_TABLE_NUMBER, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 + .default_str = "0" }, { .name = "pane-active-border-style", diff --git a/tmux.1 b/tmux.1 index e54817d5..b99fcb62 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3711,6 +3711,9 @@ Set the width or height of the main (left or top) pane in the or .Ic main-vertical layouts. +If suffixed by +.Ql % , +this is a percentage of the window size. .Pp .It Xo Ic mode-keys .Op Ic vi | emacs @@ -3764,6 +3767,9 @@ and .Ic other-pane-height options are set, the main pane will grow taller to make the other panes the specified height, but will never shrink to do so. +If suffixed by +.Ql % , +this is a percentage of the window size. .Pp .It Ic other-pane-width Ar width Like diff --git a/tmux.h b/tmux.h index 3fe32b50..8561f11d 100644 --- a/tmux.h +++ b/tmux.h @@ -2043,6 +2043,8 @@ long long args_strtonum(struct args *, u_char, long long, long long, char **); long long args_percentage(struct args *, u_char, long long, long long, long long, char **); +long long args_string_percentage(const char *, long long, long long, + long long, char **); /* cmd-find.c */ int cmd_find_target(struct cmd_find_state *, struct cmdq_item *, From 59351001817ec03ae0e22e292e271cb9fcf3916b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2020 06:57:13 +0000 Subject: [PATCH 0219/1006] Change so main-pane-width and height can be given as a percentage. From b72498c4ff0566d377a4757d1393817627c281ec Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2020 08:48:44 +0000 Subject: [PATCH 0220/1006] Update the cursor position when deleting lines from screens without history, GitHub issue 2173. --- screen.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/screen.c b/screen.c index 129cfc2d..f416ac37 100644 --- a/screen.c +++ b/screen.c @@ -48,7 +48,7 @@ struct screen_title_entry { }; TAILQ_HEAD(screen_titles, screen_title_entry); -static void screen_resize_y(struct screen *, u_int, int); +static void screen_resize_y(struct screen *, u_int, int, u_int *); static void screen_reflow(struct screen *, u_int, u_int *, u_int *); /* Free titles stack. */ @@ -254,7 +254,7 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, reflow = 0; if (sy != screen_size_y(s)) - screen_resize_y(s, sy, eat_empty); + screen_resize_y(s, sy, eat_empty, cy); if (reflow) screen_reflow(s, sx, cx, cy); @@ -281,7 +281,7 @@ screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) } static void -screen_resize_y(struct screen *s, u_int sy, int eat_empty) +screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy) { struct grid *gd = s->grid; u_int needed, available, oldy, i; @@ -330,6 +330,7 @@ screen_resize_y(struct screen *s, u_int sy, int eat_empty) if (available > needed) available = needed; grid_view_delete_lines(gd, 0, available, 8); + (*cy) -= available; } } From 4b5a16567a7ceb1990bdaa821c12061ed80144f5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2020 08:48:44 +0000 Subject: [PATCH 0221/1006] Update the cursor position when deleting lines from screens without history, GitHub issue 2173. From df1bce40f0863b024cbd6d8ad56fd3a8914e68ed Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 22 Apr 2020 12:09:04 +0100 Subject: [PATCH 0222/1006] Call the event_init wrapper again. --- tmux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.c b/tmux.c index 40872eca..32b04bd1 100644 --- a/tmux.c +++ b/tmux.c @@ -398,5 +398,5 @@ main(int argc, char **argv) free(label); /* Pass control to the client. */ - exit(client_main(event_init(), argc, argv, flags, feat)); + exit(client_main(osdep_event_init(), argc, argv, flags, feat)); } From d4826aa1aae9b5a3efe6764f87d9c9b897f4723e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 22 Apr 2020 12:18:11 +0100 Subject: [PATCH 0223/1006] Nope, OS X kqueue is still broken... This reverts commit 94c90385d2e728a4d37a25ce78b55b2ffeb429f9. --- osdep-darwin.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osdep-darwin.c b/osdep-darwin.c index 6d1bfb72..6b2b1d72 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -93,19 +93,15 @@ osdep_event_init(void) { struct event_base *base; -#ifndef __MAC_10_7 /* * On OS X, kqueue and poll are both completely broken and don't * work on anything except socket file descriptors (yes, really). */ setenv("EVENT_NOKQUEUE", "1", 1); setenv("EVENT_NOPOLL", "1", 1); -#endif base = event_init(); -#ifndef __MAC_10_7 unsetenv("EVENT_NOKQUEUE"); unsetenv("EVENT_NOPOLL"); -#endif return (base); } From ecb6db6b6ab1b54fa2ca564e96c5602de9692484 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 22 Apr 2020 12:59:10 +0100 Subject: [PATCH 0224/1006] Update CHANGES. --- CHANGES | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/CHANGES b/CHANGES index ff8a055b..9c02ca15 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,83 @@ CHANGES FROM 3.1 TO 3.2 +* Add a -s flag to copy-mode to specify a different pane for the source + content. This means it is possible to view two places in a pane's history at + the same time in different panes, or view the history while still using the + pane. Pressing r refreshes the content from the source pane. + +* Add an argument to list-commands to show only a single command. + +* Change copy mode to make copy of the pane history so it does not need to + freeze the pane. + +* Restore pane_current_path format from portable tmux on OpenBSD. + +* Wait until the initial command sequence is done before sending a device + attributes request and other bits that prompt a reply from the terminal. This + means that stray relies are not left on the terminal if the command has + attached and then immediately detached and tmux will not be around to receive + them. + +* Add a -f filter argument to the list commands like choose-tree. + +* Move specific hooks for panes to pane options and windows for window options + rather than all hooks being session options. These hooks are now window options: + + window-layout-changed + window-linked + window-pane-changed + window-renamed + window-unlinked + + And these now pane options: + + pane-died + pane-exited + pane-focus-in + pane-focus-out + pane-mode-changed + pane-set-clipboard + + Any existing configurations using these hooks on a session rather than + globally (that is, set-hook or set-option without -g) may need to be changed. + +* Show signal names when a process exits with remain-on-exit on platforms which + have a way to get them. + +* Start menu with top item selected if no mouse and use mode-style for the + selected item. + +* Add a copy-command option and change copy-pipe and friends to pipe to it if + used without arguments, allows all the default copy key bindings to be + changed to pipe with one option rather than needing to change each key + binding individually. + +* Tidy up the terminal detection and feature code and add named sets of + terminal features, each of which are defined in one place and map to a + builtin set of terminfo(5) capabilities. Features can be specified based on + TERM with a new terminal-features option or with the -T flag when running + tmux. tmux will also detect a few common terminals from the DA and DSR + responses. + + This is intended to make it easier to configure tmux's use of terminfo(5) + even in the presence of outdated ncurses(3) or terminfo(5) databases or for + features which do not yet have a terminfo(5) entry. Instead of having to grok + terminfo(5) capability names and what they should be set to in the + terminal-overrides option, the user can hopefully just give tmux a feature + name and let it do the right thing. + + The terminal-overrides option remains both for backwards compatibility and to + allow tweaks of individual capabilities. + +* Support mintty's application escape sequence (means tmux doesn't have to + delay to wait for Escape, so no need to reduce escape-time when using + mintty). + +* Change so main-pane-width and height can be given as a percentage. + +* Support for the iTerm2 synchronized updates feature (allows the terminal to + avoid unnecessary drawing while output is still in progress). + * Make the mouse_word and mouse_line formats work in copy mode and enable the default pane menu in copy mode. @@ -41,6 +119,11 @@ CHANGES FROM 3.1 TO 3.2 CHANGES FROM 3.0a TO 3.1 +* Only search the visible part of the history when marking (highlighting) + search terms. This is much faster than searching the whole history and solves + problems with large histories. The count of matches shown is now the visible + matches rather than all matches. + * Search using regular expressions in copy mode. search-forward and search-backward use regular expressions by default; the incremental versions do not. From ccd7368cc504fd8c40fdcc9bbade1e6be4503491 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 22 Apr 2020 12:59:50 +0100 Subject: [PATCH 0225/1006] Update CHANGES. --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 62b697bc..ec373fa3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ CHANGES FROM 3.0a TO 3.1 +* Only search the visible part of the history when marking (highlighting) + search terms. This is much faster than searching the whole history and solves + problems with large histories. The count of matches shown is now the visible + matches rather than all matches. + * Search using regular expressions in copy mode. search-forward and search-backward use regular expressions by default; the incremental versions do not. From 662728d6c7e1518e420ef430e85a44c249f8795f Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2020 20:47:00 +0000 Subject: [PATCH 0226/1006] Add a session_marked format like window_marked. --- format.c | 5 +++++ tmux.1 | 1 + 2 files changed, 6 insertions(+) diff --git a/format.c b/format.c index f1f52895..f0a5a3b1 100644 --- a/format.c +++ b/format.c @@ -2529,6 +2529,11 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add_cb(ft, "session_alerts", format_cb_session_alerts); format_add_cb(ft, "session_stack", format_cb_session_stack); + + if (server_check_marked() && marked_pane.s == s) + format_add(ft, "session_marked", "1"); + else + format_add(ft, "session_marked", "0"); } /* Set default format keys for a client. */ diff --git a/tmux.1 b/tmux.1 index b99fcb62..960d31d9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4564,6 +4564,7 @@ The following variables are available, where appropriate: .It Li "session_id" Ta "" Ta "Unique session ID" .It Li "session_last_attached" Ta "" Ta "Time session last attached" .It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" +.It Li "session_marked" Ta "" Ta "1 if this session contains the marked pane" .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_path" Ta "" Ta "Working directory of session" .It Li "session_stack" Ta "" Ta "Window indexes in most recent order" From 899b3d2436ffc49e264cc869ddf35e8a9d01522b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2020 21:01:28 +0000 Subject: [PATCH 0227/1006] Indicate the marked pane in choose mode in reverse and add key to set and clear it (m and M) and a key to jump to the starting pane (H). --- mode-tree.c | 38 ++++++++++++++++++++++++++++++++------ tmux.h | 3 ++- window-tree.c | 22 ++++++++++++++++++++-- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/mode-tree.c b/mode-tree.c index 0177d618..783ffcfa 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -256,8 +256,8 @@ mode_tree_expand_current(struct mode_tree_data *mtd) } } -void -mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag) +static int +mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found) { u_int i; @@ -266,15 +266,41 @@ mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag) break; } if (i != mtd->line_size) { - mtd->current = i; + *found = i; + return (1); + } + return (0); +} + +void +mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag) +{ + u_int found; + + if (!mode_tree_get_tag(mtd, tag, &found)) + return; + if (!mtd->line_list[found].item->expanded) { + mtd->line_list[found].item->expanded = 1; + mode_tree_build(mtd); + } +} + +int +mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag) +{ + u_int found; + + if (mode_tree_get_tag(mtd, tag, &found)) { + mtd->current = found; if (mtd->current > mtd->height - 1) mtd->offset = mtd->current - mtd->height + 1; else mtd->offset = 0; - } else { - mtd->current = 0; - mtd->offset = 0; + return (1); } + mtd->current = 0; + mtd->offset = 0; + return (0); } u_int diff --git a/tmux.h b/tmux.h index 8561f11d..67dc8631 100644 --- a/tmux.h +++ b/tmux.h @@ -2621,7 +2621,8 @@ typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code); u_int mode_tree_count_tagged(struct mode_tree_data *); void *mode_tree_get_current(struct mode_tree_data *); void mode_tree_expand_current(struct mode_tree_data *); -void mode_tree_set_current(struct mode_tree_data *, uint64_t); +void mode_tree_expand(struct mode_tree_data *, uint64_t); +int mode_tree_set_current(struct mode_tree_data *, uint64_t); void mode_tree_each_tagged(struct mode_tree_data *, mode_tree_each_cb, struct client *, key_code, int); void mode_tree_down(struct mode_tree_data *, int); diff --git a/window-tree.c b/window-tree.c index d32358a5..156aafd9 100644 --- a/window-tree.c +++ b/window-tree.c @@ -37,9 +37,11 @@ static void window_tree_key(struct window_mode_entry *, #define WINDOW_TREE_DEFAULT_FORMAT \ "#{?pane_format," \ - "#{pane_current_command} \"#{pane_title}\"" \ + "#{?pane_marked,#[reverse],}" \ + "#{pane_current_command}#{?pane_active,*,}#{?pane_marked,M,} \"#{pane_title}\"" \ "," \ "#{?window_format," \ + "#{?window_marked_flag,#[reverse],}" \ "#{window_name}#{window_flags} " \ "(#{window_panes} panes)" \ "#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \ @@ -56,6 +58,7 @@ static void window_tree_key(struct window_mode_entry *, static const struct menu_item window_tree_menu_items[] = { { "Select", '\r', NULL }, { "Expand", KEYC_RIGHT, NULL }, + { "Mark", 'm', NULL }, { "", KEYC_NONE, NULL }, { "Tag", 't', NULL }, { "Tag All", '\024', NULL }, @@ -1170,7 +1173,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, struct window_tree_modedata *data = wme->data; struct window_tree_itemdata *item, *new_item; char *name, *prompt = NULL; - struct cmd_find_state fs; + struct cmd_find_state fs, *fsp = &data->fs; int finished; u_int tagged, x, y, idx; struct session *ns; @@ -1192,6 +1195,21 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, case '>': data->offset++; break; + case 'H': + mode_tree_expand(data->data, (uint64_t)fsp->s); + mode_tree_expand(data->data, (uint64_t)fsp->wl); + if (!mode_tree_set_current(data->data, (uint64_t)wme->wp)) + mode_tree_set_current(data->data, (uint64_t)fsp->wl); + break; + case 'm': + window_tree_pull_item(item, &ns, &nwl, &nwp); + server_set_marked(ns, nwl, nwp); + mode_tree_build(data->data); + break; + case 'M': + server_clear_marked(); + mode_tree_build(data->data); + break; case 'x': window_tree_pull_item(item, &ns, &nwl, &nwp); switch (item->type) { From 4b21fd2ed1f706dfeac0f9c884ec2f8e74e7487e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2020 21:01:28 +0000 Subject: [PATCH 0228/1006] Indicate the marked pane in choose mode in reverse and add key to set and clear it (m and M) and a key to jump to the starting pane (H). From 950af3363678de5b88cb6713f4837f1001e46d47 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2020 21:15:33 +0000 Subject: [PATCH 0229/1006] Improve join-pane, move-pane and break-pane: - There is no need for join-pane and move-pane to be different. - break-pane can just behave like move-window if the source has only one pane, instead of failing. - Add -a to break-pane like move-window. Also add missing man page bits for previous window-tree.c changes. GitHub issue 2176. --- cmd-break-pane.c | 34 ++++++++++++++++++++++++---------- cmd-join-pane.c | 17 ++++------------- cmd-move-window.c | 14 ++++++++------ tmux.1 | 20 +++++++++++--------- 4 files changed, 47 insertions(+), 38 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 880ee7f5..87892d73 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_break_pane_entry = { .name = "break-pane", .alias = "breakp", - .args = { "dPF:n:s:t:", 0, 0 }, - .usage = "[-dP] [-F format] [-n window-name] [-s src-pane] " + .args = { "adPF:n:s:t:", 0, 0 }, + .usage = "[-adP] [-F format] [-n window-name] [-s src-pane] " "[-t dst-window]", .source = { 's', CMD_FIND_PANE, 0 }, @@ -63,17 +63,31 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) const char *template; char *cp; - if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { - cmdq_error(item, "index %d already in use", idx); - return (CMD_RETURN_ERROR); - } - - if (window_count_panes(w) == 1) { - cmdq_error(item, "can't break with only one pane"); - return (CMD_RETURN_ERROR); + if (args_has(args, 'a')) { + if (target->wl != NULL) + idx = winlink_shuffle_up(dst_s, target->wl); + else + idx = winlink_shuffle_up(dst_s, dst_s->curw); + if (idx == -1) + return (CMD_RETURN_ERROR); } server_unzoom_window(w); + if (window_count_panes(w) == 1) { + if (server_link_window(src_s, wl, dst_s, idx, 0, + !args_has(args, 'd'), &cause) != 0) { + cmdq_error(item, "%s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + server_unlink_window(src_s, wl); + return (CMD_RETURN_NORMAL); + } + if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { + cmdq_error(item, "index in use: %d", idx); + return (CMD_RETURN_ERROR); + } + TAILQ_REMOVE(&w->panes, wp, entry); window_lost_pane(w, wp); layout_close_pane(wp); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 45a56fc4..3efe769b 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -50,8 +50,8 @@ const struct cmd_entry cmd_move_pane_entry = { .name = "move-pane", .alias = "movep", - .args = { "bdhvp:l:s:t:", 0, 0 }, - .usage = "[-bdhv] [-l size] " CMD_SRCDST_PANE_USAGE, + .args = { "bdfhvp:l:s:t:", 0, 0 }, + .usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .target = { 't', CMD_FIND_PANE, 0 }, @@ -72,16 +72,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) struct window *src_w, *dst_w; struct window_pane *src_wp, *dst_wp; char *cause = NULL; - int size, percentage, dst_idx, not_same_window; + int size, percentage, dst_idx; int flags; enum layout_type type; struct layout_cell *lc; - if (cmd_get_entry(self) == &cmd_join_pane_entry) - not_same_window = 1; - else - not_same_window = 0; - dst_s = target->s; dst_wl = target->wl; dst_wp = target->wp; @@ -94,11 +89,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) src_w = src_wl->window; server_unzoom_window(src_w); - if (not_same_window && src_w == dst_w) { - cmdq_error(item, "can't join a pane to its own window"); - return (CMD_RETURN_ERROR); - } - if (!not_same_window && src_wp == dst_wp) { + if (src_wp == dst_wp) { cmdq_error(item, "source and target panes must be different"); return (CMD_RETURN_ERROR); } diff --git a/cmd-move-window.c b/cmd-move-window.c index eb6f4f1a..94b6c950 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -63,9 +63,9 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *source = cmdq_get_source(item); struct cmd_find_state target; const char *tflag = args_get(args, 't'); - struct session *src; + struct session *src = source->s; struct session *dst; - struct winlink *wl; + struct winlink *wl = source->wl; char *cause; int idx, kflag, dflag, sflag; @@ -83,9 +83,7 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) if (cmd_find_target(&target, item, tflag, CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX) != 0) return (CMD_RETURN_ERROR); - src = source->s; dst = target.s; - wl = source->wl; idx = target.idx; kflag = args_has(args, 'k'); @@ -93,12 +91,16 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) sflag = args_has(args, 's'); if (args_has(args, 'a')) { - if ((idx = winlink_shuffle_up(dst, dst->curw)) == -1) + if (target.wl != NULL) + idx = winlink_shuffle_up(dst, target.wl); + else + idx = winlink_shuffle_up(dst, dst->curw); + if (idx == -1) return (CMD_RETURN_ERROR); } if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { - cmdq_error(item, "can't link window: %s", cause); + cmdq_error(item, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } diff --git a/tmux.1 b/tmux.1 index 960d31d9..1eed6c78 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1714,7 +1714,7 @@ from which the layout was originally defined. Commands related to windows and panes are as follows: .Bl -tag -width Ds .It Xo Ic break-pane -.Op Fl dP +.Op Fl adP .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar src-pane @@ -1725,6 +1725,10 @@ Break .Ar src-pane off from its containing window to make it the only pane in .Ar dst-window . +With +.Fl a , +the window is moved to the next index up (following windows +are moved if necessary). If .Fl d is given, the new window does not become the current window. @@ -1873,12 +1877,15 @@ The following keys may be used in tree mode: .It Li "<" Ta "Scroll list of previews left" .It Li ">" Ta "Scroll list of previews right" .It Li "C-s" Ta "Search by name" +.It Li "m" Ta "Set the marked pane" +.It Li "M" Ta "Clear the marked pane" .It Li "n" Ta "Repeat last search" .It Li "t" Ta "Toggle if item is tagged" .It Li "T" Ta "Tag no items" .It Li "C-t" Ta "Tag all items" .It Li "\&:" Ta "Run a command for each tagged item" .It Li "f" Ta "Enter a format to filter items" +.It Li "H" Ta "Jump to the starting pane" .It Li "O" Ta "Change sort field" .It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" @@ -2125,19 +2132,14 @@ See the .Sx FORMATS section. .It Xo Ic move-pane -.Op Fl bdhv +.Op Fl bdfhv .Op Fl l Ar size .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc .D1 (alias: Ic movep ) -Like -.Ic join-pane , -but -.Ar src-pane -and -.Ar dst-pane -may belong to the same window. +Does the same as +.Ic join-pane . .It Xo Ic move-window .Op Fl ardk .Op Fl s Ar src-window From e46cf86d307546e2f31e54e1af59d8e275a60269 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2020 21:15:33 +0000 Subject: [PATCH 0230/1006] Improve join-pane, move-pane and break-pane: - There is no need for join-pane and move-pane to be different. - break-pane can just behave like move-window if the source has only one pane, instead of failing. - Add -a to break-pane like move-window. Also add missing man page bits for previous window-tree.c changes. GitHub issue 2176. From 106e5d07beaacaa977372f8b6a0bcac5f981545b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 23 Apr 2020 06:30:15 +0100 Subject: [PATCH 0231/1006] Tweak the default choose modes formats: - Only show pane title if it is not default and not empty. - Add a prettier time format and use that instead of long ctime(). - Remove clutter and change the order. --- format.c | 68 ++++++++++++++++++++++++++++++++++++++++++++----- tmux.1 | 4 +++ window-buffer.c | 2 +- window-client.c | 3 +-- window-tree.c | 8 +++--- 5 files changed, 72 insertions(+), 13 deletions(-) diff --git a/format.c b/format.c index c20751b1..88378a27 100644 --- a/format.c +++ b/format.c @@ -100,6 +100,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_SESSIONS 0x80 #define FORMAT_WINDOWS 0x100 #define FORMAT_PANES 0x200 +#define FORMAT_PRETTY 0x400 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 @@ -1322,6 +1323,53 @@ format_quote(const char *s) return (out); } +/* Make a prettier time. */ +static char * +format_pretty_time(time_t t) +{ + struct tm now_tm, tm; + time_t now; + char s[6]; + int y, m, d; + + time(&now); + if (now < t) + now = t; + localtime_r(&now, &now_tm); + localtime_r(&t, &tm); + + y = now_tm.tm_year - 1; + if (tm.tm_year < y || + (tm.tm_year == y && + (tm.tm_mon <= now_tm.tm_mon || tm.tm_mday <= now_tm.tm_mday))) { + /* Last year. */ + strftime(s, sizeof s, "%h%y", &tm); + return (xstrdup(s)); + } + if (now_tm.tm_mon == 0) + m = 11; + else + m = now_tm.tm_mon - 1; + if (tm.tm_mon < m || (tm.tm_mon == m && tm.tm_mday < now_tm.tm_mday)) { + /* Last month. */ + strftime(s, sizeof s, "%d%b", &tm); + return (xstrdup(s)); + } + if (now_tm.tm_mday == 0) + d = 31; + else + d = now_tm.tm_mday - 1; + if (tm.tm_mday < d || + (tm.tm_mday == d && tm.tm_mday < now_tm.tm_mday)) { + /* This day. */ + strftime(s, sizeof s, "%a%d", &tm); + return (xstrdup(s)); + } + /* Today. */ + strftime(s, sizeof s, "%H:%M", &tm); + return (xstrdup(s)); +} + /* Find a format entry. */ static char * format_find(struct format_tree *ft, const char *key, int modifiers) @@ -1358,9 +1406,13 @@ format_find(struct format_tree *ft, const char *key, int modifiers) if (modifiers & FORMAT_TIMESTRING) { if (fe->t == 0) return (NULL); - ctime_r(&fe->t, s); - s[strcspn(s, "\n")] = '\0'; - found = xstrdup(s); + if (modifiers & FORMAT_PRETTY) + found = format_pretty_time(fe->t); + else { + ctime_r(&fe->t, s); + s[strcspn(s, "\n")] = '\0'; + found = xstrdup(s); + } goto found; } if (fe->t != 0) { @@ -1532,7 +1584,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lbdtqETSWP<>", cp[0]) != NULL && + if (strchr("lbdqETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -1553,7 +1605,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) } /* Now try single character with arguments. */ - if (strchr("mCs=pe", cp[0]) == NULL) + if (strchr("mCst=pe", cp[0]) == NULL) break; c = cp[0]; @@ -1978,7 +2030,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, case 'e': if (fm->argc < 1 || fm->argc > 3) break; - mexp = fm; + mexp = fm; break; case 'l': modifiers |= FORMAT_LITERAL; @@ -1991,6 +2043,10 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, break; case 't': modifiers |= FORMAT_TIMESTRING; + if (fm->argc < 1) + break; + if (strchr(fm->argv[0], 'p') != NULL) + modifiers |= FORMAT_PRETTY; break; case 'q': modifiers |= FORMAT_QUOTE; diff --git a/tmux.1 b/tmux.1 index 6a77a273..e433aeae 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4353,6 +4353,10 @@ gives .Ql #{t:window_activity} gives .Ql Sun Oct 25 09:25:02 2015 . +Adding +.Ql p ( +.Ql `t/p` ) +will use shorter but less accurate time format for times in the past. The .Ql b:\& and diff --git a/window-buffer.c b/window-buffer.c index 80092fbd..cf707abe 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -36,7 +36,7 @@ static void window_buffer_key(struct window_mode_entry *, #define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'" #define WINDOW_BUFFER_DEFAULT_FORMAT \ - "#{buffer_size} bytes (#{t:buffer_created})" + "#{t/p:buffer_created}: #{buffer_sample}" static const struct menu_item window_buffer_menu_items[] = { { "Paste", 'p', NULL }, diff --git a/window-client.c b/window-client.c index 4688cbf3..cd424dd7 100644 --- a/window-client.c +++ b/window-client.c @@ -37,8 +37,7 @@ static void window_client_key(struct window_mode_entry *, #define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'" #define WINDOW_CLIENT_DEFAULT_FORMAT \ - "session #{session_name} " \ - "(#{client_width}x#{client_height}, #{t:client_activity})" + "#{t/p:client_activity}: session #{session_name}" static const struct menu_item window_client_menu_items[] = { { "Detach", 'd', NULL }, diff --git a/window-tree.c b/window-tree.c index 095520e2..d245a715 100644 --- a/window-tree.c +++ b/window-tree.c @@ -38,13 +38,13 @@ static void window_tree_key(struct window_mode_entry *, #define WINDOW_TREE_DEFAULT_FORMAT \ "#{?pane_format," \ "#{?pane_marked,#[reverse],}" \ - "#{pane_current_command}#{?pane_active,*,}#{?pane_marked,M,} \"#{pane_title}\"" \ + "#{pane_current_command}#{?pane_active,*,}#{?pane_marked,M,}" \ + "#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \ "," \ "#{?window_format," \ "#{?window_marked_flag,#[reverse],}" \ - "#{window_name}#{window_flags} " \ - "(#{window_panes} panes)" \ - "#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \ + "#{window_name}#{window_flags}" \ + "#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \ "," \ "#{session_windows} windows" \ "#{?session_grouped, " \ From 906dfe9f5c217bb026929120b346722e5cb32106 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 23 Apr 2020 05:48:42 +0000 Subject: [PATCH 0232/1006] Fix a couple of memory leaks, one when creating a new pane and one when adding formats onto the queue item. --- cmd-queue.c | 8 ++++++-- spawn.c | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 5df30940..59f86c64 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -243,8 +243,12 @@ cmdq_copy_state(struct cmdq_state *state) void cmdq_free_state(struct cmdq_state *state) { - if (--state->references == 0) - free(state); + if (--state->references != 0) + return; + + if (state->formats != NULL) + format_free(state->formats); + free(state); } /* Add a format to command queue. */ diff --git a/spawn.c b/spawn.c index 91fb310a..f05887b2 100644 --- a/spawn.c +++ b/spawn.c @@ -366,6 +366,7 @@ spawn_pane(struct spawn_context *sc, char **cause) window_remove_pane(w, new_wp); } sigprocmask(SIG_SETMASK, &oldset, NULL); + environ_free(child); return (NULL); } @@ -443,6 +444,8 @@ complete: sigprocmask(SIG_SETMASK, &oldset, NULL); window_pane_set_event(new_wp); + environ_free(child); + if (sc->flags & SPAWN_RESPAWN) return (new_wp); if ((~sc->flags & SPAWN_DETACHED) || w->active == NULL) { From e25fa4ba1b62bb2e4395bd2ff9c40da409fec51f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 23 Apr 2020 05:48:42 +0000 Subject: [PATCH 0233/1006] Fix a couple of memory leaks, one when creating a new pane and one when adding formats onto the queue item. From 1a612a59361904a2996c8ca8b84e8c76548512b6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 23 Apr 2020 07:15:17 +0100 Subject: [PATCH 0234/1006] Add an attribute for ACS. --- attributes.c | 4 +++- style.c | 4 ++-- tmux.1 | 5 ++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/attributes.c b/attributes.c index ca88a056..b839f06d 100644 --- a/attributes.c +++ b/attributes.c @@ -31,7 +31,8 @@ attributes_tostring(int attr) if (attr == 0) return ("none"); - len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s", + len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + (attr & GRID_ATTR_CHARSET) ? "acs," : "", (attr & GRID_ATTR_BRIGHT) ? "bright," : "", (attr & GRID_ATTR_DIM) ? "dim," : "", (attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "", @@ -62,6 +63,7 @@ attributes_fromstring(const char *str) const char *name; int attr; } table[] = { + { "acs", GRID_ATTR_CHARSET }, { "bright", GRID_ATTR_BRIGHT }, { "bold", GRID_ATTR_BRIGHT }, { "dim", GRID_ATTR_DIM }, diff --git a/style.c b/style.c index 6ba4c524..da3b4c78 100644 --- a/style.c +++ b/style.c @@ -26,7 +26,7 @@ #include "tmux.h" /* Mask for bits not included in style. */ -#define STYLE_ATTR_MASK (~GRID_ATTR_CHARSET) +#define STYLE_ATTR_MASK (~0) /* Default style. */ static struct style style_default = { @@ -247,7 +247,7 @@ style_tostring(struct style *sy) colour_tostring(gc->bg)); comma = ","; } - if (gc->attr != 0 && gc->attr != GRID_ATTR_CHARSET) { + if (gc->attr != 0) { xsnprintf(s + off, sizeof s - off, "%s%s", comma, attributes_tostring(gc->attr)); comma = ","; diff --git a/tmux.1 b/tmux.1 index e433aeae..d5320b15 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4661,7 +4661,8 @@ for the terminal default colour; or a hexadecimal RGB string such as Set the background colour. .It Ic none Set no attributes (turn off any active attributes). -.It Xo Ic bright +.It Xo Ic acs , +.Ic bright (or .Ic bold ) , .Ic dim , @@ -4681,6 +4682,8 @@ Set an attribute. Any of the attributes may be prefixed with .Ql no to unset. +.Ic acs +is the terminal alternate character set. .It Xo Ic align=left (or .Ic noalign ) , From c74572da92d6ede88c04bd65e67e82f682ea2ff0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 23 Apr 2020 10:22:27 +0100 Subject: [PATCH 0235/1006] Remove support for iTerm2's DSR 1337 extension and use the CSI > q extension now supported by a few different terminals. --- CHANGES | 3 +++ input.c | 13 ++++++------ tmux.h | 2 +- tty-features.c | 2 +- tty-keys.c | 54 +++++++++++++++++++++++++++++--------------------- tty.c | 8 ++++---- 6 files changed, 46 insertions(+), 36 deletions(-) diff --git a/CHANGES b/CHANGES index 9c02ca15..cf2be672 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,8 @@ CHANGES FROM 3.1 TO 3.2 +* Remove the DSR 1337 iTerm2 extension and replace by the extended device + attributes sequence (CSI > q) supported by more terminals. + * Add a -s flag to copy-mode to specify a different pane for the source content. This means it is possible to view two places in a pane's history at the same time in different panes, or view the history while still using the diff --git a/input.c b/input.c index 4901e886..b9acfbef 100644 --- a/input.c +++ b/input.c @@ -254,6 +254,7 @@ enum input_csi_type { INPUT_CSI_TBC, INPUT_CSI_VPA, INPUT_CSI_WINOPS, + INPUT_CSI_XDA, }; /* Control (CSI) command table. */ @@ -290,6 +291,7 @@ static const struct input_table_entry input_csi_table[] = { { 'm', "", INPUT_CSI_SGR }, { 'n', "", INPUT_CSI_DSR }, { 'q', " ", INPUT_CSI_DECSCUSR }, + { 'q', ">", INPUT_CSI_XDA }, { 'r', "", INPUT_CSI_DECSTBM }, { 's', "", INPUT_CSI_SCP }, { 't', "", INPUT_CSI_WINOPS }, @@ -1456,13 +1458,6 @@ input_csi_dispatch(struct input_ctx *ictx) case 6: input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); break; - case 1337: /* Terminal version, from iTerm2. */ - copy = xstrdup(getversion()); - for (cp = copy; *cp != '\0'; cp++) - *cp = toupper((u_char)*cp); - input_reply(ictx, "\033[TMUX %sn", copy); - free(copy); - break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; @@ -1597,6 +1592,10 @@ input_csi_dispatch(struct input_ctx *ictx) if (n != -1) screen_set_cursor_style(s, n); break; + case INPUT_CSI_XDA: + input_reply(ictx, "\033P>|tmux %s\033\\", getversion()); + break; + } ictx->last = -1; diff --git a/tmux.h b/tmux.h index f95726d5..0da22299 100644 --- a/tmux.h +++ b/tmux.h @@ -1249,7 +1249,7 @@ struct tty { #define TTY_FOCUS 0x40 #define TTY_BLOCK 0x80 #define TTY_HAVEDA 0x100 -#define TTY_HAVEDSR 0x200 +#define TTY_HAVEXDA 0x200 #define TTY_SYNCING 0x400 int flags; diff --git a/tty-features.c b/tty-features.c index 1996c750..a7f2a4b0 100644 --- a/tty-features.c +++ b/tty-features.c @@ -33,7 +33,7 @@ * - alternate escape (under XT). * * Also: - * - XT is used to decide whether to send DA and DSR; + * - XT is used to decide whether to send DA and XDA; * - DECSLRM and DECFRA use a flag instead of capabilities; * - UTF-8 is a separate flag on the client; needed for unattached clients. */ diff --git a/tty-keys.c b/tty-keys.c index aa775d69..9e8428f2 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -52,7 +52,7 @@ static int tty_keys_clipboard(struct tty *, const char *, size_t, size_t *); static int tty_keys_device_attributes(struct tty *, const char *, size_t, size_t *); -static int tty_keys_device_status_report(struct tty *, const char *, +static int tty_keys_extended_device_attributes(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ @@ -612,8 +612,8 @@ tty_keys_next(struct tty *tty) goto partial_key; } - /* Is this a device status report response? */ - switch (tty_keys_device_status_report(tty, buf, len, &size)) { + /* Is this an extended device attributes response? */ + switch (tty_keys_extended_device_attributes(tty, buf, len, &size)) { case 0: /* yes */ key = KEYC_UNKNOWN; goto complete_key; @@ -936,7 +936,7 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, *size = 0; - /* First three bytes are always \033]52;. */ + /* First five bytes are always \033]52;. */ if (buf[0] != '\033') return (-1); if (len == 1) @@ -1040,9 +1040,11 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, return (1); /* Copy the rest up to a 'c'. */ - for (i = 0; i < (sizeof tmp) - 1 && buf[3 + i] != 'c'; i++) { + for (i = 0; i < (sizeof tmp) - 1; i++) { if (3 + i == len) return (1); + if (buf[3 + i] == 'c') + break; tmp[i] = buf[3 + i]; } if (i == (sizeof tmp) - 1) @@ -1101,48 +1103,54 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, } /* - * Handle device status report input. Returns 0 for success, -1 for failure, 1 - * for partial. + * Handle extended device attributes input. Returns 0 for success, -1 for + * failure, 1 for partial. */ static int -tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, - size_t *size) +tty_keys_extended_device_attributes(struct tty *tty, const char *buf, + size_t len, size_t *size) { struct client *c = tty->client; u_int i; char tmp[64]; *size = 0; - if (tty->flags & TTY_HAVEDSR) + if (tty->flags & TTY_HAVEXDA) return (-1); - /* First three bytes are always \033[. */ + /* First four bytes are always \033P>|. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); - if (buf[1] != '[') + if (buf[1] != 'P') return (-1); if (len == 2) return (1); - if (buf[2] != 'I' && buf[2] != 'T') + if (buf[2] != '>') return (-1); if (len == 3) return (1); + if (buf[3] != '|') + return (-1); + if (len == 4) + return (1); - /* Copy the rest up to a 'n'. */ - for (i = 0; i < (sizeof tmp) - 1 && buf[2 + i] != 'n'; i++) { - if (2 + i == len) + /* Copy the rest up to a '\033\\'. */ + for (i = 0; i < (sizeof tmp) - 1; i++) { + if (4 + i == len) return (1); - tmp[i] = buf[2 + i]; + if (buf[4 + i - 1] == '\033' && buf[4 + i] == '\\') + break; + tmp[i] = buf[4 + i]; } if (i == (sizeof tmp) - 1) return (-1); - tmp[i] = '\0'; - *size = 3 + i; + tmp[i - 1] = '\0'; + *size = 5 + i; /* Add terminal features. */ - if (strncmp(tmp, "ITERM2 ", 7) == 0) { + if (strncmp(tmp, "ITerm2 ", 7) == 0) { tty_add_features(&c->term_features, "256," "RGB," @@ -1152,7 +1160,7 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, "sync," "title", ","); - } else if (strncmp(tmp, "TMUX ", 5) == 0) { + } else if (strncmp(tmp, "tmux ", 5) == 0) { tty_add_features(&c->term_features, "256," "RGB," @@ -1163,10 +1171,10 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, "usstyle", ","); } - log_debug("%s: received DSR %.*s", c->name, (int)*size, buf); + log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); tty_update_features(tty); - tty->flags |= TTY_HAVEDSR; + tty->flags |= TTY_HAVEXDA; return (0); } diff --git a/tty.c b/tty.c index 98a557b5..5d7d2c0d 100644 --- a/tty.c +++ b/tty.c @@ -287,7 +287,7 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data) struct client *c = tty->client; log_debug("%s: start timer fired", c->name); - tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); + tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); } void @@ -361,10 +361,10 @@ tty_send_requests(struct tty *tty) if (tty_term_flag(tty->term, TTYC_XT)) { if (~tty->flags & TTY_HAVEDA) tty_puts(tty, "\033[>c"); - if (~tty->flags & TTY_HAVEDSR) - tty_puts(tty, "\033[1337n"); + if (~tty->flags & TTY_HAVEXDA) + tty_puts(tty, "\033[>q"); } else - tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); + tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); } void From 766b425d051a833dac768d6dc9eafc87a3f03146 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 23 Apr 2020 10:22:53 +0000 Subject: [PATCH 0236/1006] Overrides need to be applied both before and after features in case they change flags used to detect a feature. --- tty-term.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tty-term.c b/tty-term.c index f4a18b63..f3dbae5c 100644 --- a/tty-term.c +++ b/tty-term.c @@ -528,6 +528,9 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) /* Delete curses data. */ del_curterm(cur_term); + /* Apply overrides so any capabilities used for features are changed. */ + tty_term_apply_overrides(term); + /* These are always required. */ if (!tty_term_has(term, TTYC_CLEAR)) { xasprintf(cause, "terminal does not support clear"); @@ -554,7 +557,7 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) if (tty_term_flag(term, TTYC_XT)) tty_add_features(feat, "title", ":,"); - /* Apply the features and overrides. */ + /* Apply the features and overrides again. */ tty_apply_features(term, *feat); tty_term_apply_overrides(term); From 0c73dbb7e1c81e11d6f044ada84d9a492b35d93d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 23 Apr 2020 11:59:13 +0100 Subject: [PATCH 0237/1006] Response is iTerm2 not not ITerm2. --- tty-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index 9e8428f2..8c778b2a 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1150,7 +1150,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, *size = 5 + i; /* Add terminal features. */ - if (strncmp(tmp, "ITerm2 ", 7) == 0) { + if (strncmp(tmp, "iTerm2 ", 7) == 0) { tty_add_features(&c->term_features, "256," "RGB," From ac91635f82a122b45702c5cc19ea8864502fdcc1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 23 Apr 2020 12:11:14 +0100 Subject: [PATCH 0238/1006] Add extension terminfo(5) capabilities for margins. --- tmux.1 | 2 ++ tmux.h | 10 +++++++--- tty-features.c | 15 ++++++++++++--- tty-term.c | 8 ++++++-- tty.c | 13 +++++-------- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/tmux.1 b/tmux.1 index d5320b15..0d0503ad 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5565,6 +5565,8 @@ to change the cursor colour from inside .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed +.It Em \&Cmg, \&Clmg, \&Dsmg , \&Enmg +Set, clear, disable or enable DECSLRM margins. .It Em \&Smol Enable the overline attribute. .It Em \&Smulx diff --git a/tmux.h b/tmux.h index 0da22299..a020ac6a 100644 --- a/tmux.h +++ b/tmux.h @@ -256,6 +256,8 @@ enum tty_code_code { TTYC_BOLD, TTYC_CIVIS, TTYC_CLEAR, + TTYC_CLMG, + TTYC_CMG, TTYC_CNORM, TTYC_COLORS, TTYC_CR, @@ -276,12 +278,14 @@ enum tty_code_code { TTYC_DIM, TTYC_DL, TTYC_DL1, + TTYC_DSMG, TTYC_E3, TTYC_ECH, TTYC_ED, TTYC_EL, TTYC_EL1, TTYC_ENACS, + TTYC_ENMG, TTYC_FSL, TTYC_HOME, TTYC_HPA, @@ -447,11 +451,11 @@ enum tty_code_code { TTYC_SITM, TTYC_SMACS, TTYC_SMCUP, - TTYC_SMOL, TTYC_SMKX, + TTYC_SMOL, TTYC_SMSO, - TTYC_SMULX, TTYC_SMUL, + TTYC_SMULX, TTYC_SMXX, TTYC_SS, TTYC_SYNC, @@ -460,7 +464,7 @@ enum tty_code_code { TTYC_U8, TTYC_VPA, TTYC_XENL, - TTYC_XT, + TTYC_XT }; /* Message codes. */ diff --git a/tty-features.c b/tty-features.c index a7f2a4b0..9eb446d4 100644 --- a/tty-features.c +++ b/tty-features.c @@ -34,7 +34,7 @@ * * Also: * - XT is used to decide whether to send DA and XDA; - * - DECSLRM and DECFRA use a flag instead of capabilities; + * - DECFRA uses a flag instead of capabilities; * - UTF-8 is a separate flag on the client; needed for unattached clients. */ @@ -84,7 +84,7 @@ static const char *tty_feature_rgb_capabilities[] = { static struct tty_feature tty_feature_rgb = { "RGB", tty_feature_rgb_capabilities, - (TERM_256COLOURS|TERM_RGBCOLOURS) + TERM_256COLOURS|TERM_RGBCOLOURS }; /* Terminal supports 256 colours. */ @@ -159,9 +159,16 @@ static struct tty_feature tty_feature_sync = { }; /* Terminal supports DECSLRM margins. */ +static const char *tty_feature_margins_capabilities[] = { + "Enmg=\\E[?69h", + "Dsmg=\\E[?69l", + "Clmg=\\E[s", + "Cmg=\\E[%i%p1%d;%p2%ds", + NULL +}; static struct tty_feature tty_feature_margins = { "margins", - NULL, + tty_feature_margins_capabilities, TERM_DECSLRM }; @@ -194,6 +201,8 @@ tty_add_features(int *feat, const char *s, const char *separators) char *next, *loop, *copy; u_int i; + log_debug("%s: %s", __func__, s); + loop = copy = xstrdup(s); while ((next = strsep(&loop, separators)) != NULL) { for (i = 0; i < nitems(tty_features); i++) { diff --git a/tty-term.c b/tty-term.c index f0457ae2..20022fd9 100644 --- a/tty-term.c +++ b/tty-term.c @@ -64,6 +64,8 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_BOLD] = { TTYCODE_STRING, "bold" }, [TTYC_CIVIS] = { TTYCODE_STRING, "civis" }, [TTYC_CLEAR] = { TTYCODE_STRING, "clear" }, + [TTYC_CLMG] = { TTYCODE_STRING, "Clmg" }, + [TTYC_CMG] = { TTYCODE_STRING, "Cmg" }, [TTYC_CNORM] = { TTYCODE_STRING, "cnorm" }, [TTYC_COLORS] = { TTYCODE_NUMBER, "colors" }, [TTYC_CR] = { TTYCODE_STRING, "Cr" }, @@ -84,12 +86,14 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" }, [TTYC_E3] = { TTYCODE_STRING, "E3" }, [TTYC_ECH] = { TTYCODE_STRING, "ech" }, [TTYC_ED] = { TTYCODE_STRING, "ed" }, [TTYC_EL1] = { TTYCODE_STRING, "el1" }, [TTYC_EL] = { TTYCODE_STRING, "el" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, + [TTYC_ENMG] = { TTYCODE_STRING, "Enmg" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, [TTYC_HOME] = { TTYCODE_STRING, "home" }, [TTYC_HPA] = { TTYCODE_STRING, "hpa" }, @@ -240,8 +244,8 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_OP] = { TTYCODE_STRING, "op" }, [TTYC_REV] = { TTYCODE_STRING, "rev" }, [TTYC_RGB] = { TTYCODE_FLAG, "RGB" }, - [TTYC_RI] = { TTYCODE_STRING, "ri" }, [TTYC_RIN] = { TTYCODE_STRING, "rin" }, + [TTYC_RI] = { TTYCODE_STRING, "ri" }, [TTYC_RMACS] = { TTYCODE_STRING, "rmacs" }, [TTYC_RMCUP] = { TTYCODE_STRING, "rmcup" }, [TTYC_RMKX] = { TTYCODE_STRING, "rmkx" }, @@ -268,7 +272,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_U8] = { TTYCODE_NUMBER, "U8" }, [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, - [TTYC_XT] = { TTYCODE_FLAG, "XT" }, + [TTYC_XT] = { TTYCODE_FLAG, "XT" } }; u_int diff --git a/tty.c b/tty.c index 5d7d2c0d..5d84c9e8 100644 --- a/tty.c +++ b/tty.c @@ -426,7 +426,7 @@ tty_stop_tty(struct tty *tty) } if (tty_use_margin(tty)) - tty_raw(tty, "\033[?69l"); /* DECLRMM */ + tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); setblocking(tty->fd, 1); @@ -473,7 +473,7 @@ tty_update_features(struct tty *tty) tty_term_apply_overrides(tty->term); if (tty_use_margin(tty)) - tty_puts(tty, "\033[?69h"); /* DECLRMM */ + tty_putcode(tty, TTYC_ENMG); } void @@ -2028,7 +2028,7 @@ tty_invalidate(struct tty *tty) if (tty->flags & TTY_STARTED) { if (tty_use_margin(tty)) - tty_puts(tty, "\033[?69h"); /* DECLRMM */ + tty_putcode(tty, TTYC_ENMG); tty_putcode(tty, TTYC_SGR0); tty->mode = ALL_MODES; @@ -2105,8 +2105,6 @@ tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) static void tty_margin(struct tty *tty, u_int rleft, u_int rright) { - char s[64]; - if (!tty_use_margin(tty)) return; if (tty->rleft == rleft && tty->rright == rright) @@ -2118,10 +2116,9 @@ tty_margin(struct tty *tty, u_int rleft, u_int rright) tty->rright = rright; if (rleft == 0 && rright == tty->sx - 1) - snprintf(s, sizeof s, "\033[s"); + tty_putcode(tty, TTYC_CLMG); else - snprintf(s, sizeof s, "\033[%u;%us", rleft + 1, rright + 1); - tty_puts(tty, s); + tty_putcode2(tty, TTYC_CMG, rleft, rright); tty->cx = tty->cy = UINT_MAX; } From 351c5423f0ec8d0267fc2d973990bd55a1a5797b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 23 Apr 2020 16:55:20 +0100 Subject: [PATCH 0239/1006] time.h is needed. --- window-copy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/window-copy.c b/window-copy.c index 2bda5d56..a803f3b3 100644 --- a/window-copy.c +++ b/window-copy.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "tmux.h" From f87be8d0521436c47151233f781794dee94fc1df Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 23 Apr 2020 17:27:39 +0100 Subject: [PATCH 0240/1006] Add XDG_CONFIG home to the configuration search paths. --- Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 72b21f0a..a395ecdb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,8 +12,8 @@ dist_EXTRA_tmux_SOURCES = compat/*.[ch] # Preprocessor flags. AM_CPPFLAGS += @XOPEN_DEFINES@ \ - -DTMUX_VERSION="\"@VERSION@\"" \ - -DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\"" + -DTMUX_VERSION='"@VERSION@"' \ + -DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' # Additional object files. LDADD = $(LIBOBJS) From 0d3fdae7b623bd992b1fd2058e59920b2efd47da Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 23 Apr 2020 17:56:45 +0100 Subject: [PATCH 0241/1006] Build list of paths and weed out duplicates before loading configs. --- cfg.c | 55 ++++++-------------------------------- input.c | 1 - tmux.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ tmux.h | 4 ++- tty-features.c | 2 +- 5 files changed, 83 insertions(+), 50 deletions(-) diff --git a/cfg.c b/cfg.c index 312775eb..84be3967 100644 --- a/cfg.c +++ b/cfg.c @@ -66,45 +66,12 @@ set_cfg_file(const char *path) cfg_file = xstrdup(path); } -static char * -expand_cfg_file(const char *path, const char *home) -{ - char *expanded, *name; - const char *end; - struct environ_entry *value; - - if (strncmp(path, "~/", 2) == 0) { - if (home == NULL) - return (NULL); - xasprintf(&expanded, "%s%s", home, path + 1); - return (expanded); - } - - if (*path == '$') { - end = strchr(path, '/'); - if (end == NULL) - name = xstrdup(path + 1); - else - name = xstrndup(path + 1, end - path - 1); - value = environ_find(global_environ, name); - free(name); - if (value == NULL) - return (NULL); - if (end == NULL) - end = ""; - xasprintf(&expanded, "%s%s", value->value, end); - return (expanded); - } - - return (xstrdup(path)); -} - void start_cfg(void) { - const char *home = find_home(); - struct client *c; - char *path, *copy, *next, *expanded; + struct client *c; + char **paths; + u_int i, n; /* * Configuration files are loaded without a client, so commands are run @@ -123,18 +90,12 @@ start_cfg(void) } if (cfg_file == NULL) { - path = copy = xstrdup(TMUX_CONF); - while ((next = strsep(&path, ":")) != NULL) { - expanded = expand_cfg_file(next, home); - if (expanded == NULL) { - log_debug("couldn't expand %s", next); - continue; - } - log_debug("expanded %s to %s", next, expanded); - load_cfg(expanded, c, NULL, CMD_PARSE_QUIET, NULL); - free(expanded); + expand_paths(TMUX_CONF, &paths, &n); + for (i = 0; i < n; i++) { + load_cfg(paths[i], c, NULL, CMD_PARSE_QUIET, NULL); + free(paths[i]); } - free(copy); + free(paths); } else load_cfg(cfg_file, c, NULL, 0, NULL); diff --git a/input.c b/input.c index b9acfbef..1b1be485 100644 --- a/input.c +++ b/input.c @@ -1326,7 +1326,6 @@ input_csi_dispatch(struct input_ctx *ictx) struct input_table_entry *entry; int i, n, m; u_int cx, bg = ictx->cell.cell.bg; - char *copy, *cp; if (ictx->flags & INPUT_DISCARD) return (0); diff --git a/tmux.c b/tmux.c index 32b04bd1..727c9b99 100644 --- a/tmux.c +++ b/tmux.c @@ -106,6 +106,77 @@ areshell(const char *shell) return (0); } +static char * +expand_path(const char *path, const char *home) +{ + char *expanded, *name; + const char *end; + struct environ_entry *value; + + if (strncmp(path, "~/", 2) == 0) { + if (home == NULL) + return (NULL); + xasprintf(&expanded, "%s%s", home, path + 1); + return (expanded); + } + + if (*path == '$') { + end = strchr(path, '/'); + if (end == NULL) + name = xstrdup(path + 1); + else + name = xstrndup(path + 1, end - path - 1); + value = environ_find(global_environ, name); + free(name); + if (value == NULL) + return (NULL); + if (end == NULL) + end = ""; + xasprintf(&expanded, "%s%s", value->value, end); + return (expanded); + } + + return (xstrdup(path)); +} + +void +expand_paths(const char *s, char ***paths, u_int *n) +{ + const char *home = find_home(); + char *copy, *next, *tmp, resolved[PATH_MAX], *expanded; + u_int i; + + *paths = NULL; + *n = 0; + + copy = tmp = xstrdup(s); + while ((next = strsep(&tmp, ":")) != NULL) { + expanded = expand_path(next, home); + if (expanded == NULL) { + log_debug("%s: invalid path: %s", __func__, next); + continue; + } + if (realpath(expanded, resolved) == NULL) { + log_debug("%s: realpath(\"%s\") failed: %s", __func__, + expanded, strerror(errno)); + free(expanded); + continue; + } + free(expanded); + for (i = 0; i < *n; i++) { + if (strcmp(resolved, (*paths)[i]) == 0) + break; + } + if (i != *n) { + log_debug("%s: duplicate path: %s", __func__, resolved); + continue; + } + *paths = xreallocarray(*paths, (*n) + 1, sizeof *paths); + (*paths)[(*n)++] = xstrdup(resolved); + } + free(copy); +} + static char * make_label(const char *label, char **cause) { diff --git a/tmux.h b/tmux.h index a020ac6a..f1652b68 100644 --- a/tmux.h +++ b/tmux.h @@ -65,7 +65,7 @@ struct winlink; /* Client-server protocol version. */ #define PROTOCOL_VERSION 8 -/* Default configuration files. */ +/* Default configuration file. */ #ifndef TMUX_CONF #define TMUX_CONF "/etc/tmux.conf:~/.tmux.conf" #endif @@ -1748,6 +1748,8 @@ const char *sig2name(int); const char *find_cwd(void); const char *find_home(void); const char *getversion(void); +void expand_paths(const char *, char ***, u_int *); + /* proc.c */ struct imsg; diff --git a/tty-features.c b/tty-features.c index 9eb446d4..7505c96b 100644 --- a/tty-features.c +++ b/tty-features.c @@ -27,7 +27,7 @@ * Still hardcoded: * - bracket paste (sent if application asks for it); * - mouse (under kmous capability); - * - focus events (under focus-events option); + * - focus events (under XT and focus-events option); * - default colours (under AX or op capabilities); * - AIX colours (under colors >= 16); * - alternate escape (under XT). From d53e1fedd595db68a4e5bc9e3e7024fa924300d3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 23 Apr 2020 18:15:02 +0100 Subject: [PATCH 0242/1006] Add TMUX_SOCK like TMUX_PATH for the socket directory. --- tmux.c | 37 +++++++++++++++++++------------------ tmux.h | 5 ++++- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/tmux.c b/tmux.c index 727c9b99..82c3441c 100644 --- a/tmux.c +++ b/tmux.c @@ -180,31 +180,30 @@ expand_paths(const char *s, char ***paths, u_int *n) static char * make_label(const char *label, char **cause) { - char *base, resolved[PATH_MAX], *path, *s; - struct stat sb; - uid_t uid; + char **paths, *path, *base; + u_int i, n; + struct stat sb; + uid_t uid; *cause = NULL; - if (label == NULL) label = "default"; uid = getuid(); - if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') - xasprintf(&base, "%s/tmux-%ld", s, (long)uid); - else - xasprintf(&base, "%s/tmux-%ld", _PATH_TMP, (long)uid); - if (realpath(base, resolved) == NULL && - strlcpy(resolved, base, sizeof resolved) >= sizeof resolved) { - errno = ERANGE; - free(base); - goto fail; + expand_paths(TMUX_SOCK, &paths, &n); + if (n == 0) { + xasprintf(cause, "no suitable socket path"); + return (NULL); } - free(base); + path = paths[0]; /* can only have one socket! */ + for (i = 1; i < n; i++) + free(paths[i]); + free(paths); - if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST) + xasprintf(&base, "%s/tmux-%ld", path, (long)uid); + if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) goto fail; - if (lstat(resolved, &sb) != 0) + if (lstat(base, &sb) != 0) goto fail; if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; @@ -214,11 +213,13 @@ make_label(const char *label, char **cause) errno = EACCES; goto fail; } - xasprintf(&path, "%s/%s", resolved, label); + xasprintf(&path, "%s/%s", base, label); + free(base); return (path); fail: - xasprintf(cause, "error creating %s (%s)", resolved, strerror(errno)); + xasprintf(cause, "error creating %s (%s)", base, strerror(errno)); + free(base); return (NULL); } diff --git a/tmux.h b/tmux.h index f1652b68..6cbc0d81 100644 --- a/tmux.h +++ b/tmux.h @@ -65,10 +65,13 @@ struct winlink; /* Client-server protocol version. */ #define PROTOCOL_VERSION 8 -/* Default configuration file. */ +/* Default configuration files and socket paths. */ #ifndef TMUX_CONF #define TMUX_CONF "/etc/tmux.conf:~/.tmux.conf" #endif +#ifndef TMUX_SOCK +#define TMUX_SOCK "$TMUX_TMPDIR:" _PATH_TMP +#endif /* Minimum layout cell size, NOT including border lines. */ #define PANE_MINIMUM 1 From d1c1e05ea71f4d2f01eb9b4370896049b02a1818 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 23 Apr 2020 18:27:27 +0100 Subject: [PATCH 0243/1006] Update CHANGES. --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index cf2be672..051c5375 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ CHANGES FROM 3.1 TO 3.2 +* Add extension terminfo(5) capabilities for margins. + +* Try $XDG_CONFIG_HOME/tmux/tmux.conf as well as ~/.config/tmux/tmux.conf for + configuration file (the search paths are in TMUX_CONF in Makefile.am). + * Remove the DSR 1337 iTerm2 extension and replace by the extended device attributes sequence (CSI > q) supported by more terminals. From 18886cb510b26a8db0d39c39e8682843bbac7d20 Mon Sep 17 00:00:00 2001 From: jmc Date: Thu, 23 Apr 2020 21:28:09 +0000 Subject: [PATCH 0244/1006] ce examples of "Ar arg Ar arg" with "Ar arg arg" and stop the spread; --- tmux.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 1eed6c78..d1c44216 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2715,7 +2715,7 @@ Commands related to key bindings are as follows: .Op Fl nr .Op Fl N Ar note .Op Fl T Ar key-table -.Ar key Ar command Op Ar arguments +.Ar key command Op Ar arguments .Xc .D1 (alias: Ic bind ) Bind key @@ -5653,7 +5653,7 @@ A notification will never occur inside an output block. .Pp The following notifications are defined: .Bl -tag -width Ds -.It Ic %client-session-changed Ar client Ar session-id Ar name +.It Ic %client-session-changed Ar client session-id name The client is now attached to the session with ID .Ar session-id , which is named From 8650f44340e2b4531a7121c7f05cab3e00e3f3c7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 06:40:30 +0100 Subject: [PATCH 0245/1006] Move terminal features into a single file. --- format.c | 2 ++ server-client.c | 2 ++ tmux.1 | 3 ++- tmux.h | 2 ++ tty-features.c | 38 +++++++++++++++++++++++++++++++++- tty-keys.c | 55 ++++++++++++++----------------------------------- 6 files changed, 60 insertions(+), 42 deletions(-) diff --git a/format.c b/format.c index 88378a27..97fcb81f 100644 --- a/format.c +++ b/format.c @@ -2617,6 +2617,8 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_termname", "%s", c->term_name); format_add(ft, "client_termfeatures", "%s", tty_get_features(c->term_features)); + if (c->term_type != NULL) + format_add(ft, "client_termtype", "%s", c->term_type); format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); diff --git a/server-client.c b/server-client.c index 1bdb103a..26958c5d 100644 --- a/server-client.c +++ b/server-client.c @@ -294,7 +294,9 @@ server_client_lost(struct client *c) if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); + free(c->term_name); + free(c->term_type); status_free(c); diff --git a/tmux.1 b/tmux.1 index 2dd6cbab..dcb6f4c4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4463,7 +4463,8 @@ The following variables are available, where appropriate: .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_termname" Ta "" Ta "Terminal name of client" -.It Li "client_termfeatures" Ta "" Ta "Terminal features of client" +.It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" +.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" diff --git a/tmux.h b/tmux.h index 6cbc0d81..85276aea 100644 --- a/tmux.h +++ b/tmux.h @@ -1506,6 +1506,7 @@ struct client { char *term_name; int term_features; + char *term_type; char *ttyname; struct tty tty; @@ -2030,6 +2031,7 @@ const char *tty_term_describe(struct tty_term *, enum tty_code_code); void tty_add_features(int *, const char *, const char *); const char *tty_get_features(int); int tty_apply_features(struct tty_term *, int); +void tty_default_features(int *, const char *, u_int); /* tty-acs.c */ int tty_acs_needed(struct tty *); diff --git a/tty-features.c b/tty-features.c index 7505c96b..d19160ff 100644 --- a/tty-features.c +++ b/tty-features.c @@ -201,7 +201,7 @@ tty_add_features(int *feat, const char *s, const char *separators) char *next, *loop, *copy; u_int i; - log_debug("%s: %s", __func__, s); + log_debug("adding terminal features %s", s); loop = copy = xstrdup(s); while ((next = strsep(&loop, separators)) != NULL) { @@ -275,3 +275,39 @@ tty_apply_features(struct tty_term *term, int feat) term->features |= feat; return (1); } + +void +tty_default_features(int *feat, const char *name, u_int version) +{ + static struct { + const char *name; + u_int version; + const char *features; + } table[] = { + { .name = "mintty", + .features = "256,RGB,ccolour,clipboard,cstyle,margins,overline,title" + }, + { .name = "tmux", + .features = "256,RGB,ccolour,clipboard,cstyle,overline,title,usstyle" + }, + { .name = "rxvt-unicode", + .features = "256,title" + }, + { .name = "iTerm2", + .features = "256,RGB,clipboard,cstyle,margins,sync,title" + }, + { .name = "XTerm", + .features = "256,RGB,ccolour,clipboard,cstyle,margins,rectfill,title" + } + }; + u_int i; + + for (i = 0; i < nitems(table); i++) { + if (strcmp(table[i].name, name) != 0) + continue; + if (version != 0 && version < table[i].version) + continue; + tty_add_features(feat, table[i].features, ","); + } + +} diff --git a/tty-keys.c b/tty-keys.c index 8c778b2a..f5a3418f 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1070,28 +1070,13 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, ","); break; case 'M': /* mintty */ - tty_add_features(&c->term_features, - "256," - "RGB," - "title", - ","); + tty_default_features(&c->term_features, "mintty", 0); break; case 'T': /* tmux */ - tty_add_features(&c->term_features, - "256," - "RGB," - "ccolour," - "cstyle," - "overline," - "title," - "usstyle", - ","); + tty_default_features(&c->term_features, "tmux", 0); break; case 'U': /* rxvt-unicode */ - tty_add_features(&c->term_features, - "256," - "title", - ","); + tty_default_features(&c->term_features, "rxvt-unicode", 0); break; } log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); @@ -1112,7 +1097,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, { struct client *c = tty->client; u_int i; - char tmp[64]; + char tmp[128]; *size = 0; if (tty->flags & TTY_HAVEXDA) @@ -1150,29 +1135,19 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, *size = 5 + i; /* Add terminal features. */ - if (strncmp(tmp, "iTerm2 ", 7) == 0) { - tty_add_features(&c->term_features, - "256," - "RGB," - "clipboard," - "cstyle," - "margins," - "sync," - "title", - ","); - } else if (strncmp(tmp, "tmux ", 5) == 0) { - tty_add_features(&c->term_features, - "256," - "RGB," - "ccolour," - "cstyle," - "overline," - "title," - "usstyle", - ","); - } + if (strncmp(tmp, "iTerm2 ", 7) == 0) + tty_default_features(&c->term_features, "iTerm2", 0); + else if (strncmp(tmp, "tmux ", 5) == 0) + tty_default_features(&c->term_features, "tmux", 0); + else if (strncmp(tmp, "XTerm(", 6) == 0) + tty_default_features(&c->term_features, "xterm", 0); + else if (strncmp(tmp, "mintty ", 7) == 0) + tty_default_features(&c->term_features, "mintty", 0); log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); + free(c->term_type); + c->term_type = xstrdup(tmp); + tty_update_features(tty); tty->flags |= TTY_HAVEXDA; From 2d8fd35de2c15b376dac41f9a1e6a62b22018976 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 06:51:15 +0100 Subject: [PATCH 0246/1006] Add a feature for strikethrough. --- tty-features.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tty-features.c b/tty-features.c index d19160ff..b0e4b8ca 100644 --- a/tty-features.c +++ b/tty-features.c @@ -147,6 +147,17 @@ static struct tty_feature tty_feature_ccolour = { 0 }; +/* Terminal supports strikethrough. */ +static const char *tty_feature_strikethrough_capabilities[] = { + "smxx=\\E[9m", + NULL +}; +static struct tty_feature tty_feature_strikethrough = { + "strikethrough", + tty_feature_strikethrough_capabilities, + 0 +}; + /* Terminal supports synchronized updates. */ static const char *tty_feature_sync_capabilities[] = { "Sync=\\EP=%p1%ds\\E\\\\", @@ -189,6 +200,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_overline, &tty_feature_rectfill, &tty_feature_rgb, + &tty_feature_strikethrough, &tty_feature_sync, &tty_feature_title, &tty_feature_usstyle @@ -285,19 +297,19 @@ tty_default_features(int *feat, const char *name, u_int version) const char *features; } table[] = { { .name = "mintty", - .features = "256,RGB,ccolour,clipboard,cstyle,margins,overline,title" + .features = "256,RGB,ccolour,clipboard,cstyle,margins,strikethrough,overline,title" }, { .name = "tmux", - .features = "256,RGB,ccolour,clipboard,cstyle,overline,title,usstyle" + .features = "256,RGB,ccolour,clipboard,cstyle,overline,strikethough,title,usstyle" }, { .name = "rxvt-unicode", .features = "256,title" }, { .name = "iTerm2", - .features = "256,RGB,clipboard,cstyle,margins,sync,title" + .features = "256,RGB,clipboard,cstyle,margins,strikethrough,sync,title" }, { .name = "XTerm", - .features = "256,RGB,ccolour,clipboard,cstyle,margins,rectfill,title" + .features = "256,RGB,ccolour,clipboard,cstyle,margins,rectfill,strikethrough,title" } }; u_int i; From 5d69b9c4a7e8adc570e965189de0e5936fbf8e1c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 07:13:02 +0100 Subject: [PATCH 0247/1006] Add a feature for bracketed paste. --- tmux.1 | 2 ++ tmux.h | 2 ++ tty-features.c | 22 +++++++++++++++++----- tty-term.c | 8 +++++--- tty.c | 6 +++--- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/tmux.1 b/tmux.1 index dcb6f4c4..cd7c0869 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5568,6 +5568,8 @@ $ printf '\e033]12;red\e033\e\e' .Ed .It Em \&Cmg, \&Clmg, \&Dsmg , \&Enmg Set, clear, disable or enable DECSLRM margins. +.It Em \&Dsbp , \&Enbp +Disable and enable bracketed paste. .It Em \&Smol Enable the overline attribute. .It Em \&Smulx diff --git a/tmux.h b/tmux.h index 85276aea..b9f48dd7 100644 --- a/tmux.h +++ b/tmux.h @@ -281,6 +281,7 @@ enum tty_code_code { TTYC_DIM, TTYC_DL, TTYC_DL1, + TTYC_DSBP, TTYC_DSMG, TTYC_E3, TTYC_ECH, @@ -288,6 +289,7 @@ enum tty_code_code { TTYC_EL, TTYC_EL1, TTYC_ENACS, + TTYC_ENBP, TTYC_ENMG, TTYC_FSL, TTYC_HOME, diff --git a/tty-features.c b/tty-features.c index b0e4b8ca..6dfc26aa 100644 --- a/tty-features.c +++ b/tty-features.c @@ -25,7 +25,6 @@ /* * Still hardcoded: - * - bracket paste (sent if application asks for it); * - mouse (under kmous capability); * - focus events (under XT and focus-events option); * - default colours (under AX or op capabilities); @@ -123,6 +122,18 @@ static struct tty_feature tty_feature_usstyle = { 0 }; +/* Terminal supports cursor bracketed paste. */ +static const char *tty_feature_bpaste_capabilities[] = { + "Enbp=\E[?2004h", + "Dsbp=\\E[?2004l", + NULL +}; +static struct tty_feature tty_feature_bpaste = { + "bpaste", + tty_feature_bpaste_capabilities, + 0 +}; + /* Terminal supports cursor styles. */ static const char *tty_feature_cstyle_capabilities[] = { "Ss=\\E[%p1%d q", @@ -193,6 +204,7 @@ static struct tty_feature tty_feature_rectfill = { /* Available terminal features. */ static const struct tty_feature *tty_features[] = { &tty_feature_256, + &tty_feature_bpaste, &tty_feature_clipboard, &tty_feature_ccolour, &tty_feature_cstyle, @@ -297,19 +309,19 @@ tty_default_features(int *feat, const char *name, u_int version) const char *features; } table[] = { { .name = "mintty", - .features = "256,RGB,ccolour,clipboard,cstyle,margins,strikethrough,overline,title" + .features = "256,RGB,bpaste,ccolour,clipboard,cstyle,margins,overline,strikethrough,title" }, { .name = "tmux", - .features = "256,RGB,ccolour,clipboard,cstyle,overline,strikethough,title,usstyle" + .features = "256,RGB,bpaste,ccolour,clipboard,cstyle,overline,strikethough,title,usstyle" }, { .name = "rxvt-unicode", .features = "256,title" }, { .name = "iTerm2", - .features = "256,RGB,clipboard,cstyle,margins,strikethrough,sync,title" + .features = "256,RGB,bpaste,clipboard,cstyle,margins,strikethrough,sync,title" }, { .name = "XTerm", - .features = "256,RGB,ccolour,clipboard,cstyle,margins,rectfill,strikethrough,title" + .features = "256,RGB,bpaste,ccolour,clipboard,cstyle,margins,rectfill,strikethrough,title" } }; u_int i; diff --git a/tty-term.c b/tty-term.c index 20022fd9..102359f1 100644 --- a/tty-term.c +++ b/tty-term.c @@ -86,6 +86,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DSBP] = { TTYCODE_STRING, "Dsbp" }, [TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" }, [TTYC_E3] = { TTYCODE_STRING, "E3" }, [TTYC_ECH] = { TTYCODE_STRING, "ech" }, @@ -93,6 +94,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_EL1] = { TTYCODE_STRING, "el1" }, [TTYC_EL] = { TTYCODE_STRING, "el" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, + [TTYC_ENBP] = { TTYCODE_STRING, "Enbp" }, [TTYC_ENMG] = { TTYCODE_STRING, "Enmg" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, [TTYC_HOME] = { TTYCODE_STRING, "home" }, @@ -561,11 +563,11 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) && (!tty_term_has(term, TTYC_SETRGBF) || !tty_term_has(term, TTYC_SETRGBB))) - tty_add_features(feat, "RGB", ":,"); + tty_add_features(feat, "RGB", ","); - /* Add feature if terminal has XT. */ + /* Add some features if terminal has XT. */ if (tty_term_flag(term, TTYC_XT)) - tty_add_features(feat, "title", ":,"); + tty_add_features(feat, "bpaste,title", ","); /* Apply the features and overrides again. */ tty_apply_features(term, *feat); diff --git a/tty.c b/tty.c index 5d84c9e8..7e044540 100644 --- a/tty.c +++ b/tty.c @@ -407,7 +407,7 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); } if (tty->mode & MODE_BRACKETPASTE) - tty_raw(tty, "\033[?2004l"); + tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP)); if (*tty->ccolour != '\0') tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); @@ -729,9 +729,9 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } if (changed & MODE_BRACKETPASTE) { if (mode & MODE_BRACKETPASTE) - tty_puts(tty, "\033[?2004h"); + tty_putcode(tty, TTYC_ENBP); else - tty_puts(tty, "\033[?2004l"); + tty_putcode(tty, TTYC_DSBP); } tty->mode = mode; } From 61550ac2e005a9cc2b8d540e304067f49c7ace24 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 07:37:11 +0100 Subject: [PATCH 0248/1006] Add feature and capabilities for focus reporting. Also document AX and XT even though they aren't tmux's. --- tmux.1 | 20 ++++++++++++++++++++ tmux.h | 2 ++ tty-features.c | 29 +++++++++++++++++++++-------- tty-term.c | 4 +++- tty.c | 4 ++-- 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/tmux.1 b/tmux.1 index cd7c0869..a3a05878 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5556,6 +5556,10 @@ It is not normally necessary to set these manually, instead the .Ic terminal-features option should be used. .Bl -tag -width Ds +.It Em \&AX +An existing extension that tells +.Nm +the terminal supports default colours. .It Em \&Cs , Cr Set the cursor colour. The first takes a single string argument and is used to set the colour; @@ -5568,8 +5572,19 @@ $ printf '\e033]12;red\e033\e\e' .Ed .It Em \&Cmg, \&Clmg, \&Dsmg , \&Enmg Set, clear, disable or enable DECSLRM margins. +These are set automatically if the terminal reports it is +.Em VT420 +compatible. .It Em \&Dsbp , \&Enbp Disable and enable bracketed paste. +These are set automatically if the +.Em XT +capability is present. +.It Em \&Dsfcs , \&Enfcs +Disable and enable focus reporting. +These are set automatically if the +.Em XT +capability is present. .It Em \&Smol Enable the overline attribute. .It Em \&Smulx @@ -5620,6 +5635,11 @@ See the option above and the .Xr xterm 1 man page. +.It Em \&XT +This is an existing extension capability that tmux uses to mean that the +terminal supports the +.Xr xterm 1 +title set sequences and to automatically set some of the capabilities above. .El .Sh CONTROL MODE .Nm diff --git a/tmux.h b/tmux.h index b9f48dd7..2eb17ae0 100644 --- a/tmux.h +++ b/tmux.h @@ -282,6 +282,7 @@ enum tty_code_code { TTYC_DL, TTYC_DL1, TTYC_DSBP, + TTYC_DSFCS, TTYC_DSMG, TTYC_E3, TTYC_ECH, @@ -290,6 +291,7 @@ enum tty_code_code { TTYC_EL1, TTYC_ENACS, TTYC_ENBP, + TTYC_ENFCS, TTYC_ENMG, TTYC_FSL, TTYC_HOME, diff --git a/tty-features.c b/tty-features.c index 6dfc26aa..0e24d292 100644 --- a/tty-features.c +++ b/tty-features.c @@ -26,7 +26,6 @@ /* * Still hardcoded: * - mouse (under kmous capability); - * - focus events (under XT and focus-events option); * - default colours (under AX or op capabilities); * - AIX colours (under colors >= 16); * - alternate escape (under XT). @@ -122,9 +121,9 @@ static struct tty_feature tty_feature_usstyle = { 0 }; -/* Terminal supports cursor bracketed paste. */ +/* Terminal supports bracketed paste. */ static const char *tty_feature_bpaste_capabilities[] = { - "Enbp=\E[?2004h", + "Enbp=\\E[?2004h", "Dsbp=\\E[?2004l", NULL }; @@ -134,6 +133,18 @@ static struct tty_feature tty_feature_bpaste = { 0 }; +/* Terminal supports focus reporting. */ +static const char *tty_feature_focus_capabilities[] = { + "Enfcs=\\E[?1004h", + "Dsfcs=\\E[?1004l", + NULL +}; +static struct tty_feature tty_feature_focus = { + "focus", + tty_feature_focus_capabilities, + 0 +}; + /* Terminal supports cursor styles. */ static const char *tty_feature_cstyle_capabilities[] = { "Ss=\\E[%p1%d q", @@ -205,9 +216,10 @@ static struct tty_feature tty_feature_rectfill = { static const struct tty_feature *tty_features[] = { &tty_feature_256, &tty_feature_bpaste, - &tty_feature_clipboard, &tty_feature_ccolour, + &tty_feature_clipboard, &tty_feature_cstyle, + &tty_feature_focus, &tty_feature_margins, &tty_feature_overline, &tty_feature_rectfill, @@ -308,20 +320,21 @@ tty_default_features(int *feat, const char *name, u_int version) u_int version; const char *features; } table[] = { +#define TTY_FEATURES_BASE_MODERN_XTERM "256,RGB,bpaste,clipboard,strikethrough,title" { .name = "mintty", - .features = "256,RGB,bpaste,ccolour,clipboard,cstyle,margins,overline,strikethrough,title" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,margins,overline" }, { .name = "tmux", - .features = "256,RGB,bpaste,ccolour,clipboard,cstyle,overline,strikethough,title,usstyle" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,overline,usstyle" }, { .name = "rxvt-unicode", .features = "256,title" }, { .name = "iTerm2", - .features = "256,RGB,bpaste,clipboard,cstyle,margins,strikethrough,sync,title" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,margins,sync" }, { .name = "XTerm", - .features = "256,RGB,bpaste,ccolour,clipboard,cstyle,margins,rectfill,strikethrough,title" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,margins,rectfill" } }; u_int i; diff --git a/tty-term.c b/tty-term.c index 102359f1..4217d6d6 100644 --- a/tty-term.c +++ b/tty-term.c @@ -86,6 +86,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DSFCS] = { TTYCODE_STRING, "Dsfcs" }, [TTYC_DSBP] = { TTYCODE_STRING, "Dsbp" }, [TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" }, [TTYC_E3] = { TTYCODE_STRING, "E3" }, @@ -95,6 +96,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_EL] = { TTYCODE_STRING, "el" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, [TTYC_ENBP] = { TTYCODE_STRING, "Enbp" }, + [TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" }, [TTYC_ENMG] = { TTYCODE_STRING, "Enmg" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, [TTYC_HOME] = { TTYCODE_STRING, "home" }, @@ -567,7 +569,7 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) /* Add some features if terminal has XT. */ if (tty_term_flag(term, TTYC_XT)) - tty_add_features(feat, "bpaste,title", ","); + tty_add_features(feat, "bpaste,focus,title", ","); /* Apply the features and overrides again. */ tty_apply_features(term, *feat); diff --git a/tty.c b/tty.c index 7e044540..548c837d 100644 --- a/tty.c +++ b/tty.c @@ -333,7 +333,7 @@ tty_start_tty(struct tty *tty) if (tty_term_flag(tty->term, TTYC_XT)) { if (options_get_number(global_options, "focus-events")) { tty->flags |= TTY_FOCUS; - tty_puts(tty, "\033[?1004h"); + tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); } tty_puts(tty, "\033[?7727h"); } @@ -420,7 +420,7 @@ tty_stop_tty(struct tty *tty) if (tty_term_flag(tty->term, TTYC_XT)) { if (tty->flags & TTY_FOCUS) { tty->flags &= ~TTY_FOCUS; - tty_raw(tty, "\033[?1004l"); + tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); } tty_raw(tty, "\033[?7727l"); } From bb107d297981d2d9eb1e92932fd13eddefa5a704 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 07:47:16 +0100 Subject: [PATCH 0249/1006] All of this stuff can be const. --- tty-features.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/tty-features.c b/tty-features.c index 0e24d292..93b90d45 100644 --- a/tty-features.c +++ b/tty-features.c @@ -49,7 +49,7 @@ static const char *tty_feature_title_capabilities[] = { "fsl=\\a", NULL }; -static struct tty_feature tty_feature_title = { +static const struct tty_feature tty_feature_title = { "title", tty_feature_title_capabilities, 0 @@ -60,7 +60,7 @@ static const char *tty_feature_clipboard_capabilities[] = { "Ms=\\E]52;%p1%s;%p2%s\\a", NULL }; -static struct tty_feature tty_feature_clipboard = { +static const struct tty_feature tty_feature_clipboard = { "clipboard", tty_feature_clipboard_capabilities, 0 @@ -79,7 +79,7 @@ static const char *tty_feature_rgb_capabilities[] = { "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", NULL }; -static struct tty_feature tty_feature_rgb = { +static const struct tty_feature tty_feature_rgb = { "RGB", tty_feature_rgb_capabilities, TERM_256COLOURS|TERM_RGBCOLOURS @@ -92,7 +92,7 @@ static const char *tty_feature_256_capabilities[] = { "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", NULL }; -static struct tty_feature tty_feature_256 = { +static const struct tty_feature tty_feature_256 = { "256", tty_feature_256_capabilities, TERM_256COLOURS @@ -103,7 +103,7 @@ static const char *tty_feature_overline_capabilities[] = { "Smol=\\E[53m", NULL }; -static struct tty_feature tty_feature_overline = { +static const struct tty_feature tty_feature_overline = { "overline", tty_feature_overline_capabilities, 0 @@ -115,7 +115,7 @@ static const char *tty_feature_usstyle_capabilities[] = { "Setulc=\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m", NULL }; -static struct tty_feature tty_feature_usstyle = { +static const struct tty_feature tty_feature_usstyle = { "usstyle", tty_feature_usstyle_capabilities, 0 @@ -127,7 +127,7 @@ static const char *tty_feature_bpaste_capabilities[] = { "Dsbp=\\E[?2004l", NULL }; -static struct tty_feature tty_feature_bpaste = { +static const struct tty_feature tty_feature_bpaste = { "bpaste", tty_feature_bpaste_capabilities, 0 @@ -139,7 +139,7 @@ static const char *tty_feature_focus_capabilities[] = { "Dsfcs=\\E[?1004l", NULL }; -static struct tty_feature tty_feature_focus = { +static const struct tty_feature tty_feature_focus = { "focus", tty_feature_focus_capabilities, 0 @@ -151,7 +151,7 @@ static const char *tty_feature_cstyle_capabilities[] = { "Se=\\E[2 q", NULL }; -static struct tty_feature tty_feature_cstyle = { +static const struct tty_feature tty_feature_cstyle = { "cstyle", tty_feature_cstyle_capabilities, 0 @@ -163,7 +163,7 @@ static const char *tty_feature_ccolour_capabilities[] = { "Cr=\\E]112\\a", NULL }; -static struct tty_feature tty_feature_ccolour = { +static const struct tty_feature tty_feature_ccolour = { "ccolour", tty_feature_ccolour_capabilities, 0 @@ -174,7 +174,7 @@ static const char *tty_feature_strikethrough_capabilities[] = { "smxx=\\E[9m", NULL }; -static struct tty_feature tty_feature_strikethrough = { +static const struct tty_feature tty_feature_strikethrough = { "strikethrough", tty_feature_strikethrough_capabilities, 0 @@ -185,7 +185,7 @@ static const char *tty_feature_sync_capabilities[] = { "Sync=\\EP=%p1%ds\\E\\\\", NULL }; -static struct tty_feature tty_feature_sync = { +static const struct tty_feature tty_feature_sync = { "sync", tty_feature_sync_capabilities, 0 @@ -199,14 +199,14 @@ static const char *tty_feature_margins_capabilities[] = { "Cmg=\\E[%i%p1%d;%p2%ds", NULL }; -static struct tty_feature tty_feature_margins = { +static const struct tty_feature tty_feature_margins = { "margins", tty_feature_margins_capabilities, TERM_DECSLRM }; /* Terminal supports DECFRA rectangle fill. */ -static struct tty_feature tty_feature_rectfill = { +static const struct tty_feature tty_feature_rectfill = { "rectfill", NULL, TERM_DECFRA @@ -346,5 +346,4 @@ tty_default_features(int *feat, const char *name, u_int version) continue; tty_add_features(feat, table[i].features, ","); } - } From 650d38962f180d0fe547ebb79aad433b27c1dde9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 09:57:49 +0100 Subject: [PATCH 0250/1006] tmux 3.1. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9675660c..25d3881c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.1-rc4) +AC_INIT([tmux], 3.1) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From ae73fd363b23ab3b20e8b8d45a8302ac38ff23d7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 11:56:44 +0100 Subject: [PATCH 0251/1006] Do not redraw at all if nothing has changed. --- screen-redraw.c | 3 +++ tty-features.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/screen-redraw.c b/screen-redraw.c index 8e74fe97..30344fe5 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -434,6 +434,9 @@ screen_redraw_screen(struct client *c) return; flags = screen_redraw_update(c, c->flags); + if ((flags & CLIENT_ALLREDRAWFLAGS) == 0) + return; + screen_redraw_set_context(c, &ctx); tty_sync_start(&c->tty); diff --git a/tty-features.c b/tty-features.c index 93b90d45..531cf97c 100644 --- a/tty-features.c +++ b/tty-features.c @@ -328,7 +328,7 @@ tty_default_features(int *feat, const char *name, u_int version) .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,overline,usstyle" }, { .name = "rxvt-unicode", - .features = "256,title" + .features = "256,ccolour,cstyle,title" }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,margins,sync" From a477c03ad59fb1487881df5c6be135dde4765133 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 12:14:53 +0100 Subject: [PATCH 0252/1006] Do not update mode until actually drawing something. --- screen-redraw.c | 2 ++ server-client.c | 2 -- tty.c | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 30344fe5..5ca6024d 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -438,6 +438,7 @@ screen_redraw_screen(struct client *c) return; screen_redraw_set_context(c, &ctx); + tty_update_mode(&c->tty, c->tty.mode, NULL); tty_sync_start(&c->tty); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { @@ -473,6 +474,7 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) return; screen_redraw_set_context(c, &ctx); + tty_update_mode(&c->tty, c->tty.mode, NULL); tty_sync_start(&c->tty); screen_redraw_draw_pane(&ctx, wp); diff --git a/server-client.c b/server-client.c index 26958c5d..92b3a6ad 100644 --- a/server-client.c +++ b/server-client.c @@ -1780,7 +1780,6 @@ server_client_check_redraw(struct client *c) if (!redraw) continue; log_debug("%s: redrawing pane %%%u", __func__, wp->id); - tty_update_mode(tty, mode, NULL); screen_redraw_pane(c, wp); } c->redraw_panes = 0; @@ -1788,7 +1787,6 @@ server_client_check_redraw(struct client *c) } if (c->flags & CLIENT_ALLREDRAWFLAGS) { - tty_update_mode(tty, mode, NULL); if (options_get_number(s->options, "set-titles")) server_client_set_title(c); screen_redraw_screen(c); diff --git a/tty.c b/tty.c index 548c837d..bd1c92b0 100644 --- a/tty.c +++ b/tty.c @@ -676,7 +676,8 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; - log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); + if (changed != 0) + log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); if (changed & MODE_BLINKING) { if (tty_term_has(tty->term, TTYC_CVVIS)) From c107708bcca5d7c014e469b719b2e0e48452be5d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 14:20:17 +0100 Subject: [PATCH 0253/1006] Focus reporting no longer under XT. --- tty.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tty.c b/tty.c index bd1c92b0..75942d2d 100644 --- a/tty.c +++ b/tty.c @@ -330,13 +330,12 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1006l\033[?1005l"); } - if (tty_term_flag(tty->term, TTYC_XT)) { - if (options_get_number(global_options, "focus-events")) { - tty->flags |= TTY_FOCUS; - tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); - } - tty_puts(tty, "\033[?7727h"); + if (options_get_number(global_options, "focus-events")) { + tty->flags |= TTY_FOCUS; + tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); } + if (tty_term_flag(tty->term, TTYC_XT)) + tty_puts(tty, "\033[?7727h"); evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); @@ -417,13 +416,12 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, "\033[?1006l\033[?1005l"); } - if (tty_term_flag(tty->term, TTYC_XT)) { - if (tty->flags & TTY_FOCUS) { - tty->flags &= ~TTY_FOCUS; - tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); - } - tty_raw(tty, "\033[?7727l"); + if (tty->flags & TTY_FOCUS) { + tty->flags &= ~TTY_FOCUS; + tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); } + if (tty_term_flag(tty->term, TTYC_XT)) + tty_raw(tty, "\033[?7727l"); if (tty_use_margin(tty)) tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); From e67d65064e5541482990402fef85342f8cb4996b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 14:20:33 +0100 Subject: [PATCH 0254/1006] rxvt needs XT also for the moment. --- options-table.c | 2 +- tty-features.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/options-table.c b/options-table.c index 9f35adf7..35d6b086 100644 --- a/options-table.c +++ b/options-table.c @@ -260,7 +260,7 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, - .default_str = "tmux*:XT,screen*:XT,xterm*:XT", + .default_str = "tmux*:XT,rxvt*:XT,screen*:XT,xterm*:XT", .separator = "," }, diff --git a/tty-features.c b/tty-features.c index 531cf97c..8d5602a3 100644 --- a/tty-features.c +++ b/tty-features.c @@ -328,7 +328,7 @@ tty_default_features(int *feat, const char *name, u_int version) .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,overline,usstyle" }, { .name = "rxvt-unicode", - .features = "256,ccolour,cstyle,title" + .features = "256,bpaste,ccolour,cstyle,title" }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,margins,sync" From 527f66ed23a88a59fb3d8c1972336f55612059bf Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 15:52:44 +0100 Subject: [PATCH 0255/1006] Instead of having a default set of terminals in terminal-overrides that get XT added and using that as a marker for xterm(1)-like, assume that if the terminfo(5) entry already has XT or the clear capability starts with CSI then the terminal is VT100-like and it should be safe to send DA requests. The DA responses trigger additional features being added. This is all to detect extensions if terminfo(5) is wrong or inadequate. If it fails, tmux will just fall back to using the capabilities in the terminfo(5) entry alone. --- options-table.c | 2 +- tmux.h | 1 + tty-term.c | 22 ++++++++++++++++++---- tty.c | 6 +++--- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/options-table.c b/options-table.c index 35d6b086..d593eff6 100644 --- a/options-table.c +++ b/options-table.c @@ -260,7 +260,7 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, - .default_str = "tmux*:XT,rxvt*:XT,screen*:XT,xterm*:XT", + .default_str = "", .separator = "," }, diff --git a/tmux.h b/tmux.h index 2eb17ae0..fcf39bfb 100644 --- a/tmux.h +++ b/tmux.h @@ -1202,6 +1202,7 @@ struct tty_term { #define TERM_DECSLRM 0x4 #define TERM_DECFRA 0x8 #define TERM_RGBCOLOURS 0x10 +#define TERM_VT100LIKE 0x20 int flags; LIST_ENTRY(tty_term) entry; diff --git a/tty-term.c b/tty-term.c index 4217d6d6..dd65e1e7 100644 --- a/tty-term.c +++ b/tty-term.c @@ -561,16 +561,30 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) goto error; } + /* + * If TERM has XT or clear starts with CSI then it is safe to assume + * the terminal is derived from a VT100. This controls whether device + * attributes requests are sent to get more information. + * + * This is a bit of a hack but there aren't that many alternatives. + * Worst case tmux will just fall back to using whatever terminfo(5) + * says without trying to correct anything that is missing. + * + * Also add few features that VT100-like terminals should either + * support or safely ignore. + */ + s = tty_term_string(term, TTYC_CLEAR); + if (tty_term_flag(term, TTYC_XT) || strncmp(s, "\033[", 2) == 0) { + term->flags |= TERM_VT100LIKE; + tty_add_features(feat, "bpaste,focus,title", ","); + } + /* Add RGB feature if terminal has RGB colours. */ if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) && (!tty_term_has(term, TTYC_SETRGBF) || !tty_term_has(term, TTYC_SETRGBB))) tty_add_features(feat, "RGB", ","); - /* Add some features if terminal has XT. */ - if (tty_term_flag(term, TTYC_XT)) - tty_add_features(feat, "bpaste,focus,title", ","); - /* Apply the features and overrides again. */ tty_apply_features(term, *feat); tty_term_apply_overrides(term); diff --git a/tty.c b/tty.c index 75942d2d..016ce399 100644 --- a/tty.c +++ b/tty.c @@ -334,7 +334,7 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_FOCUS; tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); } - if (tty_term_flag(tty->term, TTYC_XT)) + if (tty->term->flags & TERM_VT100LIKE) tty_puts(tty, "\033[?7727h"); evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); @@ -357,7 +357,7 @@ tty_send_requests(struct tty *tty) if (~tty->flags & TTY_STARTED) return; - if (tty_term_flag(tty->term, TTYC_XT)) { + if (tty->term->flags & TERM_VT100LIKE) { if (~tty->flags & TTY_HAVEDA) tty_puts(tty, "\033[>c"); if (~tty->flags & TTY_HAVEXDA) @@ -420,7 +420,7 @@ tty_stop_tty(struct tty *tty) tty->flags &= ~TTY_FOCUS; tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); } - if (tty_term_flag(tty->term, TTYC_XT)) + if (tty->term->flags & TERM_VT100LIKE) tty_raw(tty, "\033[?7727l"); if (tty_use_margin(tty)) From 9b571daceec3ba038c669e90e20c3c0c2c755c57 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 16:40:10 +0100 Subject: [PATCH 0256/1006] Instead of forbidding invalid session names, sanitize them. --- cmd-new-session.c | 35 ++++++++++++++--------------------- cmd-rename-session.c | 12 ++++-------- session.c | 18 +++++++++++++----- tmux.h | 2 +- 4 files changed, 32 insertions(+), 35 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 353f7bed..f08155c0 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -70,13 +70,14 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmdq_get_client(item); - struct session *s, *as, *groupwith; + struct session *s, *as, *groupwith = NULL; struct environ *env; struct options *oo; struct termios tio, *tiop; - struct session_group *sg; - const char *errstr, *template, *group, *prefix, *tmp; + struct session_group *sg = NULL; + const char *errstr, *template, *group, *tmp; char *cause, *cwd = NULL, *cp, *newname = NULL; + char *name, *prefix = NULL; int detached, already_attached, is_control = 0; u_int sx, sy, dsx, dsy; struct spawn_context sc; @@ -98,11 +99,9 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) tmp = args_get(args, 's'); if (tmp != NULL) { - newname = format_single(item, tmp, c, NULL, NULL, NULL); - if (!session_check_name(newname)) { - cmdq_error(item, "bad session name: %s", newname); - goto fail; - } + name = format_single(item, tmp, c, NULL, NULL, NULL); + newname = session_check_name(name); + free(name); } if (args_has(args, 'A')) { if (newname != NULL) @@ -126,24 +125,16 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) group = args_get(args, 't'); if (group != NULL) { groupwith = target->s; - if (groupwith == NULL) { - if (!session_check_name(group)) { - cmdq_error(item, "bad group name: %s", group); - goto fail; - } + if (groupwith == NULL) sg = session_group_find(group); - } else + else sg = session_group_contains(groupwith); if (sg != NULL) - prefix = sg->name; + prefix = xstrdup(sg->name); else if (groupwith != NULL) - prefix = groupwith->name; + prefix = xstrdup(groupwith->name); else - prefix = group; - } else { - groupwith = NULL; - sg = NULL; - prefix = NULL; + prefix = session_check_name(group); } /* Set -d if no client. */ @@ -353,10 +344,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) free(cwd); free(newname); + free(prefix); return (CMD_RETURN_NORMAL); fail: free(cwd); free(newname); + free(prefix); return (CMD_RETURN_ERROR); } diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 4b2c3d88..51b8ffc8 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -49,19 +49,15 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); struct session *s = target->s; - char *newname; + char *newname, *tmp; - newname = format_single_from_target(item, args->argv[0]); + tmp = format_single_from_target(item, args->argv[0]); + newname = session_check_name(tmp); + free(tmp); if (strcmp(newname, s->name) == 0) { free(newname); return (CMD_RETURN_NORMAL); } - - if (!session_check_name(newname)) { - cmdq_error(item, "bad session name: %s", newname); - free(newname); - return (CMD_RETURN_ERROR); - } if (session_find(newname) != NULL) { cmdq_error(item, "duplicate session: %s", newname); free(newname); diff --git a/session.c b/session.c index eddafa2c..87c5b83c 100644 --- a/session.c +++ b/session.c @@ -122,7 +122,6 @@ session_create(const char *prefix, const char *name, const char *cwd, s->cwd = xstrdup(cwd); - s->curw = NULL; TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); @@ -141,7 +140,6 @@ session_create(const char *prefix, const char *name, const char *cwd, s->name = xstrdup(name); s->id = next_session_id++; } else { - s->name = NULL; do { s->id = next_session_id++; free(s->name); @@ -231,11 +229,20 @@ session_destroy(struct session *s, int notify, const char *from) session_remove_ref(s, __func__); } -/* Check a session name is valid: not empty and no colons or periods. */ -int +/* Sanitize session name. */ +char * session_check_name(const char *name) { - return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); + char *copy, *cp, *new_name; + + copy = xstrdup(name); + for (cp = copy; *cp != '\0'; cp++) { + if (*cp == ':' || *cp == '.') + *cp = '_'; + } + utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); + free(copy); + return (new_name); } /* Lock session if it has timed out. */ @@ -555,6 +562,7 @@ session_group_remove(struct session *s) TAILQ_REMOVE(&sg->sessions, s, gentry); if (TAILQ_EMPTY(&sg->sessions)) { RB_REMOVE(session_groups, &session_groups, sg); + free((void *)sg->name); free(sg); } } diff --git a/tmux.h b/tmux.h index fcf39bfb..62eb7c98 100644 --- a/tmux.h +++ b/tmux.h @@ -2722,7 +2722,7 @@ struct session *session_create(const char *, const char *, const char *, void session_destroy(struct session *, int, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); -int session_check_name(const char *); +char *session_check_name(const char *); void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); From f3d6d4e80251519577c52212e9fff1883abb51a5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Apr 2020 16:47:38 +0100 Subject: [PATCH 0257/1006] CUD is not a requirement and tweak some comments. --- tty-features.c | 3 +-- tty-term.c | 8 +------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tty-features.c b/tty-features.c index 8d5602a3..30d3d1a0 100644 --- a/tty-features.c +++ b/tty-features.c @@ -28,10 +28,9 @@ * - mouse (under kmous capability); * - default colours (under AX or op capabilities); * - AIX colours (under colors >= 16); - * - alternate escape (under XT). + * - alternate escape (if terminal is VT100-like). * * Also: - * - XT is used to decide whether to send DA and XDA; * - DECFRA uses a flag instead of capabilities; * - UTF-8 is a separate flag on the client; needed for unattached clients. */ diff --git a/tty-term.c b/tty-term.c index dd65e1e7..e8ac6634 100644 --- a/tty-term.c +++ b/tty-term.c @@ -555,15 +555,9 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) goto error; } - /* These can be emulated so one of the two is required. */ - if (!tty_term_has(term, TTYC_CUD1) && !tty_term_has(term, TTYC_CUD)) { - xasprintf(cause, "terminal does not support cud1 or cud"); - goto error; - } - /* * If TERM has XT or clear starts with CSI then it is safe to assume - * the terminal is derived from a VT100. This controls whether device + * the terminal is derived from the VT100. This controls whether device * attributes requests are sent to get more information. * * This is a bit of a hack but there aren't that many alternatives. From 5811dd7ceb4d29c9871399047522adb22ee77ece Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 27 Apr 2020 09:33:46 +0100 Subject: [PATCH 0258/1006] Do not close stdout file descriptor in control mode since it will be needed for printing the exit messages. --- client.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 108873e7..4e7f7dcc 100644 --- a/client.c +++ b/client.c @@ -520,7 +520,7 @@ client_write_open(void *data, size_t datalen) errno = EBADF; else { cf->fd = dup(msg->fd); - if (client_flags & CLIENT_CONTROL) + if (~client_flags & CLIENT_CONTROL) close(msg->fd); /* can only be used once */ } } @@ -675,7 +675,8 @@ client_read_open(void *data, size_t datalen) errno = EBADF; else { cf->fd = dup(msg->fd); - close(msg->fd); /* can only be used once */ + if (~client_flags & CLIENT_CONTROL) + close(msg->fd); /* can only be used once */ } } if (cf->fd == -1) { From 1574126e8a424a442b66b4bd778539b571eebfcf Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Apr 2020 08:35:09 +0000 Subject: [PATCH 0259/1006] Do not close the stdout file descriptor in control mode as it will be needed for printing the exit messages. Fixes a bug when detaching with iTerm2. --- client.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 98565c5d..b3519690 100644 --- a/client.c +++ b/client.c @@ -518,7 +518,7 @@ client_write_open(void *data, size_t datalen) errno = EBADF; else { cf->fd = dup(msg->fd); - if (client_flags & CLIENT_CONTROL) + if (~client_flags & CLIENT_CONTROL) close(msg->fd); /* can only be used once */ } } @@ -673,7 +673,8 @@ client_read_open(void *data, size_t datalen) errno = EBADF; else { cf->fd = dup(msg->fd); - close(msg->fd); /* can only be used once */ + if (~client_flags & CLIENT_CONTROL) + close(msg->fd); /* can only be used once */ } } if (cf->fd == -1) { From 646bfe403e638f2a005ddfba37da0f94805d7e11 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 27 Apr 2020 09:36:30 +0100 Subject: [PATCH 0260/1006] Do not close stdout file descriptor in control mode since it will be needed for printing the exit messages. --- client.c | 5 +++-- configure.ac | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client.c b/client.c index 91e084a5..f247d059 100644 --- a/client.c +++ b/client.c @@ -523,7 +523,7 @@ client_write_open(void *data, size_t datalen) errno = EBADF; else { cf->fd = dup(msg->fd); - if (client_flags & CLIENT_CONTROL) + if (~client_flags & CLIENT_CONTROL) close(msg->fd); /* can only be used once */ } } @@ -678,7 +678,8 @@ client_read_open(void *data, size_t datalen) errno = EBADF; else { cf->fd = dup(msg->fd); - close(msg->fd); /* can only be used once */ + if (~client_flags & CLIENT_CONTROL) + close(msg->fd); /* can only be used once */ } } if (cf->fd == -1) { diff --git a/configure.ac b/configure.ac index 25d3881c..6a9ba594 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.1) +AC_INIT([tmux], 3.1a) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From d0fa5207889ee9ee4db45ac3684c882109f91e5b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 27 Apr 2020 09:38:04 +0100 Subject: [PATCH 0261/1006] Update CHANGES. --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index ec373fa3..12d6aac1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +CHANGES FROM 3.1 TO 3.1a + +* Do not close stdout prematurely in control mode since it is needed to print + exit messages. Prevents hanging when detaching with iTerm2. + CHANGES FROM 3.0a TO 3.1 * Only search the visible part of the history when marking (highlighting) From c30e765c7b4659b472d2830bd2f114560a5205df Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 27 Apr 2020 14:33:17 +0100 Subject: [PATCH 0262/1006] Add some additional format helper functions. --- format.c | 52 +++++++++++++++++++++++++++++++++++++++++++--------- menu.c | 4 ++-- tmux.h | 7 +++++++ 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/format.c b/format.c index 97fcb81f..857bacac 100644 --- a/format.c +++ b/format.c @@ -2472,25 +2472,59 @@ format_single(struct cmdq_item *item, const char *fmt, struct client *c, struct format_tree *ft; char *expanded; - if (item != NULL) - ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); - else - ft = format_create(NULL, item, FORMAT_NONE, 0); - format_defaults(ft, c, s, wl, wp); - + ft = format_create_defaults(item, c, s, wl, wp); expanded = format_expand(ft, fmt); format_free(ft); return (expanded); } +/* Expand a single string using state. */ +char * +format_single_from_state(struct cmdq_item *item, const char *fmt, + struct client *c, struct cmd_find_state *fs) +{ + return (format_single(item, fmt, c, fs->s, fs->wl, fs->wp)); +} + /* Expand a single string using target. */ char * format_single_from_target(struct cmdq_item *item, const char *fmt) { - struct cmd_find_state *target = cmdq_get_target(item); - struct client *tc = cmdq_get_target_client(item); + struct client *tc = cmdq_get_target_client(item); - return (format_single(item, fmt, tc, target->s, target->wl, target->wp)); + return (format_single_from_state(item, fmt, tc, cmdq_get_target(item))); +} + +/* Create and add defaults. */ +struct format_tree * +format_create_defaults(struct cmdq_item *item, struct client *c, + struct session *s, struct winlink *wl, struct window_pane *wp) +{ + struct format_tree *ft; + + if (item != NULL) + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); + else + ft = format_create(NULL, item, FORMAT_NONE, 0); + format_defaults(ft, c, s, wl, wp); + return (ft); +} + +/* Create and add defaults using state. */ +struct format_tree * +format_create_from_state(struct cmdq_item *item, struct client *c, + struct cmd_find_state *fs) +{ + return (format_create_defaults(item, c, fs->s, fs->wl, fs->wp)); +} + +/* Create and add defaults using target. */ +struct format_tree * +format_create_from_target(struct cmdq_item *item) +{ + struct client *tc = cmdq_get_target_client(item); + + return (format_create_from_state(item, tc, cmdq_get_target(item))); } /* Set defaults for any of arguments that are not NULL. */ diff --git a/menu.c b/menu.c index 39cb50c7..fd744e7d 100644 --- a/menu.c +++ b/menu.c @@ -73,7 +73,7 @@ menu_add_item(struct menu *menu, const struct menu_item *item, return; if (fs != NULL) - s = format_single(qitem, item->name, c, fs->s, fs->wl, fs->wp); + s = format_single_from_state(qitem, item->name, c, fs); else s = format_single(qitem, item->name, c, NULL, NULL, NULL); if (*s == '\0') { /* no item if empty after format expanded */ @@ -91,7 +91,7 @@ menu_add_item(struct menu *menu, const struct menu_item *item, cmd = item->command; if (cmd != NULL) { if (fs != NULL) - s = format_single(qitem, cmd, c, fs->s, fs->wl, fs->wp); + s = format_single_from_state(qitem, cmd, c, fs); else s = format_single(qitem, cmd, c, NULL, NULL, NULL); } else diff --git a/tmux.h b/tmux.h index 62eb7c98..17e5a04b 100644 --- a/tmux.h +++ b/tmux.h @@ -1827,7 +1827,14 @@ char *format_expand(struct format_tree *, const char *); char *format_single(struct cmdq_item *, const char *, struct client *, struct session *, struct winlink *, struct window_pane *); +char *format_single_from_state(struct cmdq_item *, const char *, + struct client *, struct cmd_find_state *); char *format_single_from_target(struct cmdq_item *, const char *); +struct format_tree *format_create_defaults(struct cmdq_item *, struct client *, + struct session *, struct winlink *, struct window_pane *); +struct format_tree *format_create_from_state(struct cmdq_item *, + struct client *, struct cmd_find_state *); +struct format_tree *format_create_from_target(struct cmdq_item *); void format_defaults(struct format_tree *, struct client *, struct session *, struct winlink *, struct window_pane *); void format_defaults_window(struct format_tree *, struct window *); From 79b4d839521c154c39d7f86932f7dd4a7b870576 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 27 Apr 2020 15:15:12 +0100 Subject: [PATCH 0263/1006] Use a grid cell not a style for the pane style. --- grid.c | 23 ++++++++++++++++------- style.c | 29 ++--------------------------- tmux.h | 8 ++++---- tty.c | 24 ++++++++---------------- window.c | 18 +++++++++--------- 5 files changed, 39 insertions(+), 63 deletions(-) diff --git a/grid.c b/grid.c index 0cef412b..81f3709c 100644 --- a/grid.c +++ b/grid.c @@ -211,19 +211,28 @@ grid_check_y(struct grid *gd, const char *from, u_int py) return (0); } +/* Check if two styles are (visibly) the same. */ +int +grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + if (gc1->fg != gc2->fg || gc1->bg != gc2->bg) + return (0); + if (gc1->attr != gc2->attr || gc1->flags != gc2->flags) + return (0); + return (1); +} + /* Compare grid cells. Return 1 if equal, 0 if not. */ int -grid_cells_equal(const struct grid_cell *gca, const struct grid_cell *gcb) +grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) { - if (gca->fg != gcb->fg || gca->bg != gcb->bg) + if (!grid_cells_look_equal(gc1, gc2)) return (0); - if (gca->attr != gcb->attr || gca->flags != gcb->flags) + if (gc1->data.width != gc2->data.width) return (0); - if (gca->data.width != gcb->data.width) + if (gc1->data.size != gc2->data.size) return (0); - if (gca->data.size != gcb->data.size) - return (0); - return (memcmp(gca->data.data, gcb->data.data, gca->data.size) == 0); + return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0); } /* Free one line. */ diff --git a/style.c b/style.c index da3b4c78..2ac78e97 100644 --- a/style.c +++ b/style.c @@ -59,6 +59,7 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) return (0); style_copy(&saved, sy); + log_debug("%s: %s", __func__, in); do { while (*in != '\0' && strchr(delimiters, *in) != NULL) in++; @@ -71,6 +72,7 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) memcpy(tmp, in, end); tmp[end] = '\0'; + log_debug("%s: %s", __func__, tmp); if (strcasecmp(tmp, "default") == 0) { sy->gc.fg = base->fg; sy->gc.bg = base->bg; @@ -285,30 +287,3 @@ style_copy(struct style *dst, struct style *src) { memcpy(dst, src, sizeof *dst); } - -/* Check if two styles are (visibly) the same. */ -int -style_equal(struct style *sy1, struct style *sy2) -{ - struct grid_cell *gc1 = &sy1->gc; - struct grid_cell *gc2 = &sy2->gc; - - if (gc1->fg != gc2->fg) - return (0); - if (gc1->bg != gc2->bg) - return (0); - if ((gc1->attr & STYLE_ATTR_MASK) != (gc2->attr & STYLE_ATTR_MASK)) - return (0); - if (sy1->fill != sy2->fill) - return (0); - if (sy1->align != sy2->align) - return (0); - return (1); -} - -/* Is this style default? */ -int -style_is_default(struct style *sy) -{ - return (style_equal(sy, &style_default)); -} diff --git a/tmux.h b/tmux.h index 17e5a04b..e10710d1 100644 --- a/tmux.h +++ b/tmux.h @@ -930,8 +930,8 @@ struct window_pane { struct input_ctx *ictx; - struct style cached_style; - struct style cached_active_style; + struct grid_cell cached_gc; + struct grid_cell cached_active_gc; int *palette; int pipe_fd; @@ -2365,6 +2365,8 @@ int attributes_fromstring(const char *); extern const struct grid_cell grid_default_cell; void grid_empty_line(struct grid *, u_int, u_int); int grid_cells_equal(const struct grid_cell *, const struct grid_cell *); +int grid_cells_look_equal(const struct grid_cell *, + const struct grid_cell *); struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); int grid_compare(struct grid *, struct grid *); @@ -2821,10 +2823,8 @@ int style_parse(struct style *,const struct grid_cell *, const char *style_tostring(struct style *); void style_apply(struct grid_cell *, struct options *, const char *); -int style_equal(struct style *, struct style *); void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); -int style_is_default(struct style *); /* spawn.c */ struct winlink *spawn_window(struct spawn_context *, char **); diff --git a/tty.c b/tty.c index 016ce399..99b433ae 100644 --- a/tty.c +++ b/tty.c @@ -2690,27 +2690,19 @@ static void tty_default_colours(struct grid_cell *gc, struct window_pane *wp) { struct options *oo = wp->options; - struct style *style, *active_style; int c; if (wp->flags & PANE_STYLECHANGED) { wp->flags &= ~PANE_STYLECHANGED; - - active_style = options_get_style(oo, "window-active-style"); - style = options_get_style(oo, "window-style"); - - style_copy(&wp->cached_active_style, active_style); - style_copy(&wp->cached_style, style); - } else { - active_style = &wp->cached_active_style; - style = &wp->cached_style; + style_apply(&wp->cached_active_gc, oo, "window-active-style"); + style_apply(&wp->cached_gc, oo, "window-style"); } if (gc->fg == 8) { - if (wp == wp->window->active && active_style->gc.fg != 8) - gc->fg = active_style->gc.fg; + if (wp == wp->window->active && wp->cached_active_gc.fg != 8) + gc->fg = wp->cached_active_gc.fg; else - gc->fg = style->gc.fg; + gc->fg = wp->cached_gc.fg; if (gc->fg != 8) { c = window_pane_get_palette(wp, gc->fg); @@ -2720,10 +2712,10 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) } if (gc->bg == 8) { - if (wp == wp->window->active && active_style->gc.bg != 8) - gc->bg = active_style->gc.bg; + if (wp == wp->window->active && wp->cached_active_gc.bg != 8) + gc->bg = wp->cached_active_gc.bg; else - gc->bg = style->gc.bg; + gc->bg = wp->cached_gc.bg; if (gc->bg != 8) { c = window_pane_get_palette(wp, gc->bg); diff --git a/window.c b/window.c index 3bf9ef95..7cdbf6a7 100644 --- a/window.c +++ b/window.c @@ -495,8 +495,8 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) void window_redraw_active_switch(struct window *w, struct window_pane *wp) { - struct style *sy1, *sy2; - int c1, c2; + struct grid_cell *gc1, *gc2; + int c1, c2; if (wp == w->active) return; @@ -506,18 +506,18 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp) * If the active and inactive styles or palettes are different, * need to redraw the panes. */ - sy1 = &wp->cached_style; - sy2 = &wp->cached_active_style; - if (!style_equal(sy1, sy2)) + gc1 = &wp->cached_gc; + gc2 = &wp->cached_active_gc; + if (!grid_cells_look_equal(gc1, gc2)) wp->flags |= PANE_REDRAW; else { - c1 = window_pane_get_palette(wp, sy1->gc.fg); - c2 = window_pane_get_palette(wp, sy2->gc.fg); + c1 = window_pane_get_palette(wp, gc1->fg); + c2 = window_pane_get_palette(wp, gc2->fg); if (c1 != c2) wp->flags |= PANE_REDRAW; else { - c1 = window_pane_get_palette(wp, sy1->gc.bg); - c2 = window_pane_get_palette(wp, sy2->gc.bg); + c1 = window_pane_get_palette(wp, gc1->bg); + c2 = window_pane_get_palette(wp, gc2->bg); if (c1 != c2) wp->flags |= PANE_REDRAW; } From a43a15684667d0ef223b8ad88538cca04186dd8b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 28 Apr 2020 10:53:35 +0100 Subject: [PATCH 0264/1006] Call format_defaults_window for panes as well. --- format.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/format.c b/format.c index 857bacac..2e599c60 100644 --- a/format.c +++ b/format.c @@ -2543,7 +2543,7 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s, else log_debug("%s: s=none", __func__); if (wl != NULL) - log_debug("%s: wl=%u w=@%u", __func__, wl->idx, wl->window->id); + log_debug("%s: wl=%u", __func__, wl->idx); else log_debug("%s: wl=none", __func__); if (wp != NULL) @@ -2717,11 +2717,9 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl) u_int ox, oy, sx, sy; if (ft->w == NULL) - ft->w = wl->window; + format_defaults_window(ft, w); ft->wl = wl; - format_defaults_window(ft, w); - if (c != NULL) { flag = tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); format_add(ft, "window_bigger", "%d", flag); @@ -2781,7 +2779,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) struct window_mode_entry *wme; if (ft->w == NULL) - ft->w = w; + format_defaults_window(ft, w); ft->wp = wp; format_add(ft, "history_size", "%u", gd->hsize); From 1f8256fc508bbee24b53fcd588ebf74653d69dfa Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 28 Apr 2020 13:50:07 +0100 Subject: [PATCH 0265/1006] Drop having a separate type for style options and make them all strings, which allows formats to be expanded. Any styles without a '#{' are still validated when they are set but any with a '#{' are not. Formats are not expanded usefully in many cases yet, that will be changed later. To make this work, a few other changes: - set-option -a with a style option automatically appends a ",". - OSC 10 and 11 don't set the window-style option anymore, instead the fg and bg are stored in the pane struct and act as the defaults that can be overridden by window-style. - status-fg and -bg now override status-style instead of trying to keep them in sync. --- cmd-select-pane.c | 28 ++++++-------- cmd-set-option.c | 57 ++++++++++++++-------------- input.c | 10 +---- menu.c | 3 +- mode-tree.c | 2 +- options-table.c | 94 ++++++++++++++++++++++++++++++---------------- options.c | 96 +++++++++++++++++++++-------------------------- screen-redraw.c | 9 ++--- status.c | 16 +++++--- style.c | 34 +++++++++++++---- tmux.h | 28 ++++++++------ tty.c | 16 +++++++- window-copy.c | 4 +- window.c | 3 ++ 14 files changed, 226 insertions(+), 174 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index db110ff9..c2b53bff 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -91,9 +91,9 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) struct window *w = wl->window; struct session *s = target->s; struct window_pane *wp = target->wp, *lastwp, *markedwp; + struct options *oo = wp->options; char *title; const char *style; - struct style *sy; struct options_entry *o; if (entry == &cmd_last_pane_entry || args_has(args, 'l')) { @@ -147,22 +147,18 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'P') || args_has(args, 'g')) { - if ((style = args_get(args, 'P')) != NULL) { - o = options_set_style(wp->options, "window-style", 0, - style); - if (o == NULL) { - cmdq_error(item, "bad style: %s", style); - return (CMD_RETURN_ERROR); - } - options_set_style(wp->options, "window-active-style", 0, - style); - wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); - } - if (args_has(args, 'g')) { - sy = options_get_style(wp->options, "window-style"); - cmdq_print(item, "%s", style_tostring(sy)); + style = args_get(args, 'P'); + if (style != NULL) { + o = options_set_string(oo, "window-style", 0, "%s", style); + if (o == NULL) { + cmdq_error(item, "bad style: %s", style); + return (CMD_RETURN_ERROR); } + options_set_string(oo, "window-active-style", 0, "%s", style); + wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); + } + if (args_has(args, 'g')) { + cmdq_print(item, "%s", options_get_string(oo, "window-style")); return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-option.c b/cmd-set-option.c index 1752d093..e04aa7ff 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -93,7 +93,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) char *name, *argument, *value = NULL, *cause; int window, idx, already, error, ambiguous; int scope; - struct style *sy; window = (cmd_get_entry(self) == &cmd_set_window_option_entry); @@ -232,16 +231,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) tty_keys_build(&loop->tty); } } - if (strcmp(name, "status-fg") == 0 || strcmp(name, "status-bg") == 0) { - sy = options_get_style(oo, "status-style"); - sy->gc.fg = options_get_number(oo, "status-fg"); - sy->gc.bg = options_get_number(oo, "status-bg"); - } - if (strcmp(name, "status-style") == 0) { - sy = options_get_style(oo, "status-style"); - options_set_number(oo, "status-fg", sy->gc.fg); - options_set_number(oo, "status-bg", sy->gc.bg); - } if (strcmp(name, "status") == 0 || strcmp(name, "status-interval") == 0) status_timer_start_all(); @@ -282,6 +271,29 @@ fail: return (CMD_RETURN_ERROR); } +static int +cmd_set_option_check_string(const struct options_table_entry *oe, + const char *value, char **cause) +{ + struct style sy; + + if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) { + xasprintf(cause, "not a suitable shell: %s", value); + return (-1); + } + if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) { + xasprintf(cause, "value is invalid: %s", value); + return (-1); + } + if ((oe->flags & OPTIONS_TABLE_IS_STYLE) && + strstr(value, "#{") == NULL && + style_parse(&sy, &grid_default_cell, value) != 0) { + xasprintf(cause, "invalid style: %s", value); + return (-1); + } + return (0); +} + static int cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, struct options_entry *parent, const char *value) @@ -289,10 +301,9 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, const struct options_table_entry *oe; struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); - struct options_entry *o; long long number; const char *errstr, *new; - char *old; + char *old, *cause; key_code key; oe = options_table_entry(parent); @@ -308,17 +319,12 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, old = xstrdup(options_get_string(oo, oe->name)); options_set_string(oo, oe->name, append, "%s", value); new = options_get_string(oo, oe->name); - if (strcmp(oe->name, "default-shell") == 0 && - !checkshell(new)) { + if (cmd_set_option_check_string(oe, new, &cause) != 0) { + cmdq_error(item, "%s", cause); + free(cause); + options_set_string(oo, oe->name, 0, "%s", old); free(old); - cmdq_error(item, "not a suitable shell: %s", value); - return (-1); - } - if (oe->pattern != NULL && fnmatch(oe->pattern, new, 0) != 0) { - options_set_string(oo, oe->name, 0, "%s", old); - free(old); - cmdq_error(item, "value is invalid: %s", value); return (-1); } free(old); @@ -350,13 +356,6 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, return (cmd_set_option_flag(item, oe, oo, value)); case OPTIONS_TABLE_CHOICE: return (cmd_set_option_choice(item, oe, oo, value)); - case OPTIONS_TABLE_STYLE: - o = options_set_style(oo, oe->name, append, value); - if (o == NULL) { - cmdq_error(item, "bad style: %s", value); - return (-1); - } - return (0); case OPTIONS_TABLE_COMMAND: break; } diff --git a/input.c b/input.c index 1b1be485..aad08ed3 100644 --- a/input.c +++ b/input.c @@ -2484,7 +2484,6 @@ input_osc_10(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; u_int r, g, b; - char tmp[16]; if (wp == NULL) return; @@ -2493,9 +2492,7 @@ input_osc_10(struct input_ctx *ictx, const char *p) if (!input_osc_parse_colour(p, &r, &g, &b)) goto bad; - xsnprintf(tmp, sizeof tmp, "fg=#%02x%02x%02x", r, g, b); - options_set_style(wp->options, "window-style", 1, tmp); - options_set_style(wp->options, "window-active-style", 1, tmp); + wp->fg = colour_join_rgb(r, g, b); wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; @@ -2510,7 +2507,6 @@ input_osc_11(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; u_int r, g, b; - char tmp[16]; if (wp == NULL) return; @@ -2519,9 +2515,7 @@ input_osc_11(struct input_ctx *ictx, const char *p) if (!input_osc_parse_colour(p, &r, &g, &b)) goto bad; - xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x", r, g, b); - options_set_style(wp->options, "window-style", 1, tmp); - options_set_style(wp->options, "window-active-style", 1, tmp); + wp->bg = colour_join_rgb(r, g, b); wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; diff --git a/menu.c b/menu.c index fd744e7d..2f92af34 100644 --- a/menu.c +++ b/menu.c @@ -151,8 +151,7 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) u_int i, px = md->px, py = md->py; struct grid_cell gc; - memcpy(&gc, &grid_default_cell, sizeof gc); - style_apply(&gc, c->session->curw->window->options, "mode-style"); + style_apply(&gc, c->session->curw->window->options, "mode-style", NULL); screen_write_start(&ctx, NULL, s); screen_write_clearscreen(&ctx, 8); diff --git a/mode-tree.c b/mode-tree.c index 783ffcfa..645e2ae9 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -557,7 +557,7 @@ mode_tree_draw(struct mode_tree_data *mtd) memcpy(&gc0, &grid_default_cell, sizeof gc0); memcpy(&gc, &grid_default_cell, sizeof gc); - style_apply(&gc, oo, "mode-style"); + style_apply(&gc, oo, "mode-style", NULL); w = mtd->width; h = mtd->height; diff --git a/options-table.c b/options-table.c index d593eff6..7c60e404 100644 --- a/options-table.c +++ b/options-table.c @@ -400,15 +400,19 @@ const struct options_table_entry options_table[] = { }, { .name = "message-command-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "bg=black,fg=yellow" + .default_str = "bg=black,fg=yellow", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "message-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "bg=yellow,fg=black" + .default_str = "bg=yellow,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "mouse", @@ -472,13 +476,13 @@ const struct options_table_entry options_table[] = { { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, - .default_num = 2, + .default_num = 8, }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, - .default_num = 0, + .default_num = 8, }, { .name = "status-format", @@ -525,9 +529,11 @@ const struct options_table_entry options_table[] = { }, { .name = "status-left-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "status-position", @@ -554,15 +560,19 @@ const struct options_table_entry options_table[] = { }, { .name = "status-right-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "status-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "bg=green,fg=black" + .default_str = "bg=green,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "update-environment", @@ -665,9 +675,11 @@ const struct options_table_entry options_table[] = { }, { .name = "mode-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "bg=yellow,fg=black" + .default_str = "bg=yellow,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "monitor-activity", @@ -703,9 +715,11 @@ const struct options_table_entry options_table[] = { }, { .name = "pane-active-border-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "fg=green" + .default_str = "fg=green", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "pane-base-index", @@ -731,9 +745,11 @@ const struct options_table_entry options_table[] = { }, { .name = "pane-border-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "remain-on-exit", @@ -749,9 +765,11 @@ const struct options_table_entry options_table[] = { }, { .name = "window-active-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-size", @@ -762,21 +780,27 @@ const struct options_table_entry options_table[] = { }, { .name = "window-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-status-activity-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "reverse" + .default_str = "reverse", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-status-bell-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "reverse" + .default_str = "reverse", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-status-current-format", @@ -786,9 +810,11 @@ const struct options_table_entry options_table[] = { }, { .name = "window-status-current-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-status-format", @@ -798,9 +824,11 @@ const struct options_table_entry options_table[] = { }, { .name = "window-status-last-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-status-separator", @@ -810,9 +838,11 @@ const struct options_table_entry options_table[] = { }, { .name = "window-status-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "wrap-search", diff --git a/options.c b/options.c index 7402b724..7a00dc90 100644 --- a/options.c +++ b/options.c @@ -53,6 +53,9 @@ struct options_entry { const struct options_table_entry *tableentry; union options_value value; + int cached; + struct style style; + RB_ENTRY(options_entry) entry; }; @@ -73,9 +76,6 @@ static struct options_entry *options_add(struct options *, const char *); (o)->tableentry->type == OPTIONS_TABLE_COLOUR || \ (o)->tableentry->type == OPTIONS_TABLE_FLAG || \ (o)->tableentry->type == OPTIONS_TABLE_CHOICE)) -#define OPTIONS_IS_STYLE(o) \ - ((o)->tableentry != NULL && \ - (o)->tableentry->type == OPTIONS_TABLE_STYLE) #define OPTIONS_IS_COMMAND(o) \ ((o)->tableentry != NULL && \ (o)->tableentry->type == OPTIONS_TABLE_COMMAND) @@ -123,8 +123,6 @@ options_value_tostring(struct options_entry *o, union options_value *ov, if (OPTIONS_IS_COMMAND(o)) return (cmd_list_print(ov->cmdlist, 0)); - if (OPTIONS_IS_STYLE(o)) - return (xstrdup(style_tostring(&ov->style))); if (OPTIONS_IS_NUMBER(o)) { switch (o->tableentry->type) { case OPTIONS_TABLE_NUMBER: @@ -146,7 +144,6 @@ options_value_tostring(struct options_entry *o, union options_value *ov, s = xstrdup(o->tableentry->choices[ov->number]); break; case OPTIONS_TABLE_STRING: - case OPTIONS_TABLE_STYLE: case OPTIONS_TABLE_COMMAND: fatalx("not a number option type"); } @@ -258,10 +255,6 @@ options_default(struct options *oo, const struct options_table_entry *oe) case OPTIONS_TABLE_STRING: ov->string = xstrdup(oe->default_str); break; - case OPTIONS_TABLE_STYLE: - style_set(&ov->style, &grid_default_cell); - style_parse(&ov->style, &grid_default_cell, oe->default_str); - break; default: ov->number = oe->default_num; break; @@ -653,25 +646,13 @@ options_get_number(struct options *oo, const char *name) return (o->value.number); } -struct style * -options_get_style(struct options *oo, const char *name) -{ - struct options_entry *o; - - o = options_get(oo, name); - if (o == NULL) - fatalx("missing option %s", name); - if (!OPTIONS_IS_STYLE(o)) - fatalx("option %s is not a style", name); - return (&o->value.style); -} - struct options_entry * options_set_string(struct options *oo, const char *name, int append, const char *fmt, ...) { struct options_entry *o; va_list ap; + const char *separator = ""; char *s, *value; va_start(ap, fmt); @@ -680,7 +661,12 @@ options_set_string(struct options *oo, const char *name, int append, o = options_get_only(oo, name); if (o != NULL && append && OPTIONS_IS_STRING(o)) { - xasprintf(&value, "%s%s", o->value.string, s); + if (*name != '@') { + separator = o->tableentry->separator; + if (separator == NULL) + separator = ""; + } + xasprintf(&value, "%s%s%s", o->value.string, separator, s); free(s); } else value = s; @@ -696,6 +682,7 @@ options_set_string(struct options *oo, const char *name, int append, fatalx("option %s is not a string", name); free(o->value.string); o->value.string = value; + o->cached = 0; return (o); } @@ -720,35 +707,6 @@ options_set_number(struct options *oo, const char *name, long long value) return (o); } -struct options_entry * -options_set_style(struct options *oo, const char *name, int append, - const char *value) -{ - struct options_entry *o; - struct style sy; - - if (*name == '@') - fatalx("user option %s must be a string", name); - - o = options_get_only(oo, name); - if (o != NULL && append && OPTIONS_IS_STYLE(o)) - style_copy(&sy, &o->value.style); - else - style_set(&sy, &grid_default_cell); - if (style_parse(&sy, &grid_default_cell, value) == -1) - return (NULL); - if (o == NULL) { - o = options_default(oo, options_parent_table_entry(oo, name)); - if (o == NULL) - return (NULL); - } - - if (!OPTIONS_IS_STYLE(o)) - fatalx("option %s is not a style", name); - style_copy(&o->value.style, &sy); - return (o); -} - int options_scope_from_name(struct args *args, int window, const char *name, struct cmd_find_state *fs, struct options **oo, @@ -874,3 +832,35 @@ options_scope_from_flags(struct args *args, int window, return (OPTIONS_TABLE_SESSION); } } + +struct style * +options_string_to_style(struct options *oo, const char *name, + struct format_tree *ft) +{ + struct options_entry *o; + const char *s; + char *expanded; + + o = options_get(oo, name); + if (o == NULL || !OPTIONS_IS_STRING(o)) + return (NULL); + + if (o->cached) + return (&o->style); + s = o->value.string; + log_debug("%s: %s is '%s'", __func__, name, s); + + o->cached = (strstr(s, "#{") == NULL); + if (ft != NULL && !o->cached) { + expanded = format_expand(ft, s); + if (style_parse(&o->style, &grid_default_cell, expanded) != 0) { + free(expanded); + return (NULL); + } + free(expanded); + } else { + if (style_parse(&o->style, &grid_default_cell, s) != 0) + return (NULL); + } + return (&o->style); +} diff --git a/screen-redraw.c b/screen-redraw.c index 5ca6024d..d0020e65 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -260,10 +260,9 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, struct screen old; if (wp == w->active) - style_apply(&gc, w->options, "pane-active-border-style"); + style_apply(&gc, w->options, "pane-active-border-style", NULL); else - style_apply(&gc, w->options, "pane-border-style"); - + style_apply(&gc, w->options, "pane-border-style", NULL); fmt = options_get_string(w->options, "pane-border-format"); ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); @@ -536,8 +535,8 @@ screen_redraw_draw_borders(struct screen_redraw_ctx *ctx) log_debug("%s: %s @%u", __func__, c->name, w->id); - style_apply(&other_gc, oo, "pane-border-style"); - style_apply(&active_gc, oo, "pane-active-border-style"); + style_apply(&other_gc, oo, "pane-border-style", NULL); + style_apply(&active_gc, oo, "pane-active-border-style", NULL); active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; memcpy(&m_other_gc, &other_gc, sizeof m_other_gc); diff --git a/status.c b/status.c index 6beadb81..c9da873e 100644 --- a/status.c +++ b/status.c @@ -321,7 +321,7 @@ status_redraw(struct client *c) struct screen_write_ctx ctx; struct grid_cell gc; u_int lines, i, n, width = c->tty.sx; - int flags, force = 0, changed = 0; + int flags, force = 0, changed = 0, fg, bg; struct options_entry *o; union options_value *ov; struct format_tree *ft; @@ -339,7 +339,13 @@ status_redraw(struct client *c) return (1); /* Set up default colour. */ - style_apply(&gc, s->options, "status-style"); + style_apply(&gc, s->options, "status-style", NULL); + fg = options_get_number(s->options, "status-fg"); + if (fg != 8) + gc.fg = fg; + bg = options_get_number(s->options, "status-bg"); + if (bg != 8) + gc.bg = bg; if (!grid_cells_equal(&gc, &sl->style)) { force = 1; memcpy(&sl->style, &gc, sizeof sl->style); @@ -490,7 +496,7 @@ status_message_redraw(struct client *c) if (len > c->tty.sx) len = c->tty.sx; - style_apply(&gc, s->options, "message-style"); + style_apply(&gc, s->options, "message-style", NULL); screen_write_start(&ctx, NULL, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); @@ -633,9 +639,9 @@ status_prompt_redraw(struct client *c) screen_init(sl->active, c->tty.sx, lines, 0); if (c->prompt_mode == PROMPT_COMMAND) - style_apply(&gc, s->options, "message-command-style"); + style_apply(&gc, s->options, "message-command-style", NULL); else - style_apply(&gc, s->options, "message-style"); + style_apply(&gc, s->options, "message-style", NULL); memcpy(&cursorgc, &gc, sizeof cursorgc); cursorgc.attr ^= GRID_ATTR_REVERSE; diff --git a/style.c b/style.c index 2ac78e97..3c615852 100644 --- a/style.c +++ b/style.c @@ -260,17 +260,37 @@ style_tostring(struct style *sy) return (s); } -/* Apply a style. */ +/* Apply a style on top of the given style. */ void -style_apply(struct grid_cell *gc, struct options *oo, const char *name) +style_add(struct grid_cell *gc, struct options *oo, const char *name, + struct format_tree *ft) { - struct style *sy; + struct style *sy; + struct format_tree *ft0 = NULL; - memcpy(gc, &grid_default_cell, sizeof *gc); - sy = options_get_style(oo, name); - gc->fg = sy->gc.fg; - gc->bg = sy->gc.bg; + if (ft == NULL) + ft = ft0 = format_create(NULL, NULL, 0, FORMAT_NOJOBS); + + sy = options_string_to_style(oo, name, ft); + if (sy == NULL) + sy = &style_default; + if (sy->gc.fg != 8) + gc->fg = sy->gc.fg; + if (sy->gc.bg != 8) + gc->bg = sy->gc.bg; gc->attr |= sy->gc.attr; + + if (ft0 != NULL) + format_free(ft0); +} + +/* Apply a style on top of the default style. */ +void +style_apply(struct grid_cell *gc, struct options *oo, const char *name, + struct format_tree *ft) +{ + memcpy(gc, &grid_default_cell, sizeof *gc); + style_add(gc, oo, name, ft); } /* Initialize style from cell. */ diff --git a/tmux.h b/tmux.h index e10710d1..9fd05130 100644 --- a/tmux.h +++ b/tmux.h @@ -898,6 +898,9 @@ struct window_pane { u_int xoff; u_int yoff; + int fg; + int bg; + int flags; #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 @@ -1664,7 +1667,6 @@ enum options_table_type { OPTIONS_TABLE_COLOUR, OPTIONS_TABLE_FLAG, OPTIONS_TABLE_CHOICE, - OPTIONS_TABLE_STYLE, OPTIONS_TABLE_COMMAND }; @@ -1676,12 +1678,13 @@ enum options_table_type { #define OPTIONS_TABLE_IS_ARRAY 0x1 #define OPTIONS_TABLE_IS_HOOK 0x2 +#define OPTIONS_TABLE_IS_STYLE 0x4 struct options_table_entry { const char *name; enum options_table_type type; int scope; - int flags; + int flags; u_int minimum; u_int maximum; @@ -1720,7 +1723,7 @@ struct spawn_context { const char *name; char **argv; int argc; - struct environ *environ; + struct environ *environ; int idx; const char *cwd; @@ -1900,18 +1903,17 @@ struct options_entry *options_match_get(struct options *, const char *, int *, int, int *); const char *options_get_string(struct options *, const char *); long long options_get_number(struct options *, const char *); -struct style *options_get_style(struct options *, const char *); struct options_entry * printflike(4, 5) options_set_string(struct options *, const char *, int, const char *, ...); struct options_entry *options_set_number(struct options *, const char *, long long); -struct options_entry *options_set_style(struct options *, const char *, int, - const char *); int options_scope_from_name(struct args *, int, const char *, struct cmd_find_state *, struct options **, char **); int options_scope_from_flags(struct args *, int, struct cmd_find_state *, struct options **, char **); +struct style *options_string_to_style(struct options *, const char *, + struct format_tree *); /* options-table.c */ extern const struct options_table_entry options_table[]; @@ -2138,7 +2140,7 @@ enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int, int, const char *, int); /* cmd-parse.c */ -void cmd_parse_empty(struct cmd_parse_input *); +void cmd_parse_empty(struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_string(const char *, struct cmd_parse_input *); @@ -2229,8 +2231,8 @@ void file_fire_done(struct client_file *); void file_fire_read(struct client_file *); int file_can_print(struct client *); void printflike(2, 3) file_print(struct client *, const char *, ...); -void file_vprint(struct client *, const char *, va_list); -void file_print_buffer(struct client *, void *, size_t); +void file_vprint(struct client *, const char *, va_list); +void file_print_buffer(struct client *, void *, size_t); void printflike(2, 3) file_error(struct client *, const char *, ...); void file_write(struct client *, const char *, int, const void *, size_t, client_file_cb, void *); @@ -2728,7 +2730,7 @@ struct session *session_find_by_id_str(const char *); struct session *session_find_by_id(u_int); struct session *session_create(const char *, const char *, const char *, struct environ *, struct options *, struct termios *); -void session_destroy(struct session *, int, const char *); +void session_destroy(struct session *, int, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); char *session_check_name(const char *); @@ -2798,7 +2800,7 @@ struct menu *menu_create(const char *); void menu_add_items(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); -void menu_add_item(struct menu *, const struct menu_item *, +void menu_add_item(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); void menu_free(struct menu *); @@ -2821,8 +2823,10 @@ int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, int style_parse(struct style *,const struct grid_cell *, const char *); const char *style_tostring(struct style *); +void style_add(struct grid_cell *, struct options *, + const char *, struct format_tree *); void style_apply(struct grid_cell *, struct options *, - const char *); + const char *, struct format_tree *); void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); diff --git a/tty.c b/tty.c index 99b433ae..e378b45d 100644 --- a/tty.c +++ b/tty.c @@ -2686,6 +2686,14 @@ tty_try_colour(struct tty *tty, int colour, const char *type) return (-1); } +static void +tty_window_default_style(struct grid_cell *gc, struct window_pane *wp) +{ + memcpy(gc, &grid_default_cell, sizeof *gc); + gc->fg = wp->fg; + gc->bg = wp->bg; +} + static void tty_default_colours(struct grid_cell *gc, struct window_pane *wp) { @@ -2694,8 +2702,12 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) if (wp->flags & PANE_STYLECHANGED) { wp->flags &= ~PANE_STYLECHANGED; - style_apply(&wp->cached_active_gc, oo, "window-active-style"); - style_apply(&wp->cached_gc, oo, "window-style"); + + tty_window_default_style(&wp->cached_active_gc, wp); + style_add(&wp->cached_active_gc, oo, "window-active-style", + NULL); + tty_window_default_style(&wp->cached_gc, wp); + style_add(&wp->cached_gc, oo, "window-style", NULL); } if (gc->fg == 8) { diff --git a/window-copy.c b/window-copy.c index a803f3b3..2c50a1cd 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3004,7 +3004,7 @@ window_copy_write_line(struct window_mode_entry *wme, size_t size = 0; u_int hsize = screen_hsize(data->backing); - style_apply(&gc, oo, "mode-style"); + style_apply(&gc, oo, "mode-style", NULL); gc.flags |= GRID_FLAG_NOPALETTE; if (py == 0 && s->rupper < s->rlower && !data->hide_position) { @@ -3311,7 +3311,7 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, } /* Set colours and selection. */ - style_apply(&gc, oo, "mode-style"); + style_apply(&gc, oo, "mode-style", NULL); gc.flags |= GRID_FLAG_NOPALETTE; screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, data->modekeys, &gc); diff --git a/window.c b/window.c index 7cdbf6a7..cbbc0d3e 100644 --- a/window.c +++ b/window.c @@ -872,6 +872,9 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->fd = -1; wp->event = NULL; + wp->fg = 8; + wp->bg = 8; + TAILQ_INIT(&wp->modes); wp->layout_cell = NULL; From c1acfb4341a57ba4f345bef40d7f125f873c3e27 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 28 Apr 2020 17:27:07 +0100 Subject: [PATCH 0266/1006] Start with style initialized to default. --- options.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/options.c b/options.c index 7a00dc90..39a0d08f 100644 --- a/options.c +++ b/options.c @@ -850,7 +850,9 @@ options_string_to_style(struct options *oo, const char *name, s = o->value.string; log_debug("%s: %s is '%s'", __func__, name, s); + style_set(&o->style, &grid_default_cell); o->cached = (strstr(s, "#{") == NULL); + if (ft != NULL && !o->cached) { expanded = format_expand(ft, s); if (style_parse(&o->style, &grid_default_cell, expanded) != 0) { From 24316bed495781dd6ae3105a5e34d582f02c31e9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 08:21:29 +0100 Subject: [PATCH 0267/1006] Apply a format when redrawing pane borders. --- screen-redraw.c | 280 +++++++++++++++++++++++++++++------------------- tmux.h | 3 + 2 files changed, 170 insertions(+), 113 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index d0020e65..c8e52ece 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -45,29 +45,84 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *, #define CELL_BORDERS " xqlkmjwvtun~" +/* Return if window has only two panes. */ +static int +screen_redraw_two_panes(struct window *w) +{ + struct window_pane *wp; + + wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); + if (wp == NULL) + return (0); /* one pane */ + if (TAILQ_NEXT(wp, entry) != NULL) + return (0); /* more than two panes */ + return (1); +} + /* Check if cell is on the border of a particular pane. */ static int -screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py) +screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py, + int pane_status) { + u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy; + /* Inside pane. */ - if (px >= wp->xoff && px < wp->xoff + wp->sx && - py >= wp->yoff && py < wp->yoff + wp->sy) + if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey) return (0); /* Left/right borders. */ - if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= wp->yoff + wp->sy) { - if (wp->xoff != 0 && px == wp->xoff - 1) - return (1); - if (px == wp->xoff + wp->sx) - return (2); + if (pane_status == PANE_STATUS_OFF) { + if (screen_redraw_two_panes(wp->window)) { + if (wp->xoff == 0 && px == wp->sx && py <= wp->sy / 2) + return (2); + if (wp->xoff != 0 && + px == wp->xoff - 1 && + py > wp->sy / 2) + return (1); + } else { + if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { + if (wp->xoff != 0 && px == wp->xoff - 1) + return (1); + if (px == ex) + return (2); + } + } + } else { + if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { + if (wp->xoff != 0 && px == wp->xoff - 1) + return (1); + if (px == ex) + return (2); + } } /* Top/bottom borders. */ - if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= wp->xoff + wp->sx) { - if (wp->yoff != 0 && py == wp->yoff - 1) - return (3); - if (py == wp->yoff + wp->sy) - return (4); + if (pane_status == PANE_STATUS_OFF) { + if (screen_redraw_two_panes(wp->window)) { + if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2) + return (4); + if (wp->yoff != 0 && + py == wp->yoff - 1 && + px > wp->sx / 2) + return (3); + } else { + if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { + if (wp->yoff != 0 && py == wp->yoff - 1) + return (3); + if (py == ey) + return (4); + } + } + } else if (pane_status == PANE_STATUS_TOP) { + if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { + if (wp->yoff != 0 && py == wp->yoff - 1) + return (3); + } + } else { + if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { + if (py == ey) + return (4); + } } /* Outside pane. */ @@ -76,7 +131,7 @@ screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py) /* Check if a cell is on the pane border. */ static int -screen_redraw_cell_border(struct client *c, u_int px, u_int py) +screen_redraw_cell_border(struct client *c, u_int px, u_int py, int pane_status) { struct window *w = c->session->curw->window; struct window_pane *wp; @@ -86,7 +141,8 @@ screen_redraw_cell_border(struct client *c, u_int px, u_int py) TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; - if ((retval = screen_redraw_cell_border1(wp, px, py)) != -1) + retval = screen_redraw_cell_border1(wp, px, py, pane_status); + if (retval != -1) return (!!retval); } @@ -100,7 +156,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, { struct window *w = c->session->curw->window; struct window_pane *wp; - int borders; + int borders, border; u_int right, line; *wpp = NULL; @@ -109,9 +165,10 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, return (CELL_OUTSIDE); if (pane_status != PANE_STATUS_OFF) { - TAILQ_FOREACH(wp, &w->panes, entry) { + wp = w->active; + do { if (!window_pane_visible(wp)) - continue; + goto next1; if (pane_status == PANE_STATUS_TOP) line = wp->yoff - 1; @@ -121,12 +178,18 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, if (py == line && px >= wp->xoff + 2 && px <= right) return (CELL_INSIDE); - } + + next1: + wp = TAILQ_NEXT(wp, entry); + if (wp == NULL) + wp = TAILQ_FIRST(&w->panes); + } while (wp != w->active); } - TAILQ_FOREACH(wp, &w->panes, entry) { + wp = w->active; + do { if (!window_pane_visible(wp)) - continue; + goto next2; *wpp = wp; /* If outside the pane and its border, skip it. */ @@ -134,29 +197,36 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, px > wp->xoff + wp->sx || (wp->yoff != 0 && py < wp->yoff - 1) || py > wp->yoff + wp->sy) - continue; + goto next2; - /* If definitely inside, return so. */ - if (!screen_redraw_cell_border(c, px, py)) + /* If definitely inside, return. If not on border, skip. */ + border = screen_redraw_cell_border1(wp, px, py, pane_status); + if (border == 0) return (CELL_INSIDE); + if (border == -1) + goto next2; /* * Construct a bitmask of whether the cells to the left (bit * 4), right, top, and bottom (bit 1) of this cell are borders. */ borders = 0; - if (px == 0 || screen_redraw_cell_border(c, px - 1, py)) + if (px == 0 || + screen_redraw_cell_border(c, px - 1, py, pane_status)) borders |= 8; - if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py)) + if (px <= w->sx && + screen_redraw_cell_border(c, px + 1, py, pane_status)) borders |= 4; - if (pane_status == PANE_STATUS_TOP) { - if (py != 0 && screen_redraw_cell_border(c, px, py - 1)) - borders |= 2; - } else { - if (py == 0 || screen_redraw_cell_border(c, px, py - 1)) - borders |= 2; - } - if (py <= w->sy && screen_redraw_cell_border(c, px, py + 1)) + if (pane_status == PANE_STATUS_TOP && + py != 0 && + screen_redraw_cell_border(c, px, py - 1, pane_status)) + borders |= 2; + else if (pane_status != PANE_STATUS_TOP && + (py == 0 || + screen_redraw_cell_border(c, px, py - 1, pane_status))) + borders |= 2; + if (py <= w->sy && + screen_redraw_cell_border(c, px, py + 1, pane_status)) borders |= 1; /* @@ -188,61 +258,26 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, case 3: /* 0011, top bottom */ return (CELL_LEFTRIGHT); } - } + + next2: + wp = TAILQ_NEXT(wp, entry); + if (wp == NULL) + wp = TAILQ_FIRST(&w->panes); + } while (wp != w->active); return (CELL_OUTSIDE); } /* Check if the border of a particular pane. */ static int -screen_redraw_check_is(u_int px, u_int py, int type, int pane_status, - struct window *w, struct window_pane *wantwp, struct window_pane *wp) +screen_redraw_check_is(u_int px, u_int py, int pane_status, + struct window_pane *wp) { int border; - /* Is this off the active pane border? */ - border = screen_redraw_cell_border1(wantwp, px, py); + border = screen_redraw_cell_border1(wp, px, py, pane_status); if (border == 0 || border == -1) return (0); - if (pane_status == PANE_STATUS_TOP && border == 4) - return (0); - if (pane_status == PANE_STATUS_BOTTOM && border == 3) - return (0); - - /* If there are more than two panes, that's enough. */ - if (window_count_panes(w) != 2) - return (1); - - /* Else if the cell is not a border cell, forget it. */ - if (wp == NULL || (type == CELL_OUTSIDE || type == CELL_INSIDE)) - return (1); - - /* With status lines mark the entire line. */ - if (pane_status != PANE_STATUS_OFF) - return (1); - - /* Check if the pane covers the whole width. */ - if (wp->xoff == 0 && wp->sx == w->sx) { - /* This can either be the top pane or the bottom pane. */ - if (wp->yoff == 0) { /* top pane */ - if (wp == wantwp) - return (px <= wp->sx / 2); - return (px > wp->sx / 2); - } - return (0); - } - - /* Check if the pane covers the whole height. */ - if (wp->yoff == 0 && wp->sy == w->sy) { - /* This can either be the left pane or the right pane. */ - if (wp->xoff == 0) { /* left pane */ - if (wp == wantwp) - return (py <= wp->sy / 2); - return (py > wp->sy / 2); - } - return (0); - } - return (1); } @@ -481,39 +516,68 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) tty_reset(&c->tty); } -/* Draw a border cell. */ -static void -screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j, - struct grid_cell *m_active_gc, struct grid_cell *active_gc, - struct grid_cell *m_other_gc, struct grid_cell *other_gc) +/* Get border cell style. */ +static const struct grid_cell * +screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, + u_int y, struct window_pane *wp) { struct client *c = ctx->c; struct session *s = c->session; struct window *w = s->curw->window; + struct options *oo = w->options; + struct grid_cell *gc; + struct format_tree *ft; + + if (wp->border_gc_set) + return (&wp->border_gc); + wp->border_gc_set = 1; + + ft = format_create_defaults(NULL, c, s, s->curw, wp); + gc = &wp->border_gc; + + if (screen_redraw_check_is(x, y, ctx->pane_status, w->active)) { + style_apply(gc, oo, "pane-active-border-style", ft); + gc->attr |= GRID_ATTR_CHARSET; + } else { + style_apply(gc, oo, "pane-border-style", ft); + gc->attr |= GRID_ATTR_CHARSET; + } + + format_free(ft); + return (gc); +} + +/* Draw a border cell. */ +static void +screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) +{ + struct client *c = ctx->c; + struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; - struct window_pane *active = w->active; - struct window_pane *marked = marked_pane.wp; u_int type, x = ctx->ox + i, y = ctx->oy + j; - int flag, pane_status = ctx->pane_status; + const struct grid_cell *gc; + struct grid_cell copy; if (c->overlay_check != NULL && !c->overlay_check(c, x, y)) return; - type = screen_redraw_check_cell(c, x, y, pane_status, &wp); + + type = screen_redraw_check_cell(c, x, y, ctx->pane_status, &wp); if (type == CELL_INSIDE) return; - flag = screen_redraw_check_is(x, y, type, pane_status, w, active, wp); + + gc = screen_redraw_draw_borders_style(ctx, x, y, wp); + if (gc == NULL) + return; if (server_is_marked(s, s->curw, marked_pane.wp) && - screen_redraw_check_is(x, y, type, pane_status, w, marked, wp)) { - if (flag) - tty_attributes(tty, m_active_gc, NULL); - else - tty_attributes(tty, m_other_gc, NULL); - } else if (flag) - tty_attributes(tty, active_gc, NULL); - else - tty_attributes(tty, other_gc, NULL); + screen_redraw_check_is(x, y, ctx->pane_status, marked_pane.wp)) { + memcpy(©, gc, sizeof copy); + copy.attr ^= GRID_ATTR_REVERSE; + gc = © + } + + tty_attributes(tty, gc, NULL); if (ctx->statustop) tty_cursor(tty, i, ctx->statuslines + j); else @@ -528,27 +592,17 @@ screen_redraw_draw_borders(struct screen_redraw_ctx *ctx) struct client *c = ctx->c; struct session *s = c->session; struct window *w = s->curw->window; - struct tty *tty = &c->tty; - struct options *oo = w->options; - struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; + struct window_pane *wp; u_int i, j; log_debug("%s: %s @%u", __func__, c->name, w->id); - style_apply(&other_gc, oo, "pane-border-style", NULL); - style_apply(&active_gc, oo, "pane-active-border-style", NULL); - active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; + TAILQ_FOREACH(wp, &w->panes, entry) + wp->border_gc_set = 0; - memcpy(&m_other_gc, &other_gc, sizeof m_other_gc); - m_other_gc.attr ^= GRID_ATTR_REVERSE; - memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); - m_active_gc.attr ^= GRID_ATTR_REVERSE; - - for (j = 0; j < tty->sy - ctx->statuslines; j++) { - for (i = 0; i < tty->sx; i++) { - screen_redraw_draw_borders_cell(ctx, i, j, - &m_active_gc, &active_gc, &m_other_gc, &other_gc); - } + for (j = 0; j < c->tty.sy - ctx->statuslines; j++) { + for (i = 0; i < c->tty.sx; i++) + screen_redraw_draw_borders_cell(ctx, i, j); } } diff --git a/tmux.h b/tmux.h index 9fd05130..e1b86322 100644 --- a/tmux.h +++ b/tmux.h @@ -955,6 +955,9 @@ struct window_pane { size_t written; size_t skipped; + int border_gc_set; + struct grid_cell border_gc; + TAILQ_ENTRY(window_pane) entry; RB_ENTRY(window_pane) tree_entry; }; From 2d151d8ca53a3fbc07441e6d691fb13a2aec2ee3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 08:24:09 +0100 Subject: [PATCH 0268/1006] Apply format to pane status line also. --- screen-redraw.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index c8e52ece..fefb935d 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -294,14 +294,14 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, struct screen_write_ctx ctx; struct screen old; - if (wp == w->active) - style_apply(&gc, w->options, "pane-active-border-style", NULL); - else - style_apply(&gc, w->options, "pane-border-style", NULL); - fmt = options_get_string(w->options, "pane-border-format"); - ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); - format_defaults(ft, c, NULL, NULL, wp); + format_defaults(ft, c, c->session, c->session->curw, wp); + + if (wp == w->active) + style_apply(&gc, w->options, "pane-active-border-style", ft); + else + style_apply(&gc, w->options, "pane-border-style", ft); + fmt = options_get_string(w->options, "pane-border-format"); expanded = format_expand_time(ft, fmt); if (wp->sx < 4) From 3d7674816127332e456b0a82cf53dce82a591a3f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 08:55:21 +0100 Subject: [PATCH 0269/1006] Need to redraw borders now when some things change. Also change default so that the active border colour is different in a mode or with synchronize-panes on. --- cmd-rename-window.c | 1 + cmd-resize-pane.c | 1 - cmd-respawn-pane.c | 1 + cmd-select-pane.c | 4 +++- input.c | 19 ++++++++++++++----- names.c | 1 + options-table.c | 2 +- window.c | 2 ++ 8 files changed, 23 insertions(+), 8 deletions(-) diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 1fb17ad9..593e0b9e 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -54,6 +54,7 @@ cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item) window_set_name(wl->window, newname); options_set_number(wl->window->options, "automatic-rename", 0); + server_redraw_window_borders(wl->window); server_status_window(wl->window); free(newname); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 105f48e0..563c95fb 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -91,7 +91,6 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) else window_zoom(wp); server_redraw_window(w); - server_status_window(w); return (CMD_RETURN_NORMAL); } server_unzoom_window(w); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 498a89a7..9db280b4 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -90,6 +90,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) } wp->flags |= PANE_REDRAW; + server_redraw_window_borders(wp->window); server_status_window(wp->window); environ_free(sc.environ); diff --git a/cmd-select-pane.c b/cmd-select-pane.c index c2b53bff..224370ab 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -193,8 +193,10 @@ 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)) + if (screen_set_title(&wp->base, title)) { + server_redraw_window_borders(wp->window); server_status_window(wp->window); + } free(title); return (CMD_RETURN_NORMAL); } diff --git a/input.c b/input.c index aad08ed3..5b4c42f8 100644 --- a/input.c +++ b/input.c @@ -1859,8 +1859,10 @@ input_csi_dispatch_winops(struct input_ctx *ictx) case 0: case 2: screen_pop_title(sctx->s); - if (wp != NULL) + if (wp != NULL) { + server_redraw_window_borders(wp->window); server_status_window(wp->window); + } break; } break; @@ -2251,8 +2253,10 @@ input_exit_osc(struct input_ctx *ictx) switch (option) { case 0: case 2: - if (screen_set_title(sctx->s, p) && wp != NULL) - server_status_window(ictx->wp->window); + if (screen_set_title(sctx->s, p) && wp != NULL) { + server_redraw_window_borders(wp->window); + server_status_window(wp->window); + } break; case 4: input_osc_4(ictx, p); @@ -2260,8 +2264,10 @@ input_exit_osc(struct input_ctx *ictx) case 7: if (utf8_isvalid(p)) { screen_set_path(sctx->s, p); - if (wp != NULL) + if (wp != NULL) { + server_redraw_window_borders(wp->window); server_status_window(wp->window); + } } break; case 10: @@ -2312,8 +2318,10 @@ input_exit_apc(struct input_ctx *ictx) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); - if (screen_set_title(sctx->s, ictx->input_buf) && wp != NULL) + if (screen_set_title(sctx->s, ictx->input_buf) && wp != NULL) { + server_redraw_window_borders(wp->window); server_status_window(wp->window); + } } /* Rename string started. */ @@ -2353,6 +2361,7 @@ input_exit_rename(struct input_ctx *ictx) } window_set_name(wp->window, ictx->input_buf); options_set_number(wp->window->options, "automatic-rename", 0); + server_redraw_window_borders(wp->window); server_status_window(wp->window); } diff --git a/names.c b/names.c index 661ba06e..07c689d1 100644 --- a/names.c +++ b/names.c @@ -96,6 +96,7 @@ check_window_name(struct window *w) if (strcmp(name, w->name) != 0) { log_debug("@%u new name %s (was %s)", w->id, name, w->name); window_set_name(w, name); + server_redraw_window_borders(w); server_status_window(w); } else log_debug("@%u name not changed (still %s)", w->id, w->name); diff --git a/options-table.c b/options-table.c index 7c60e404..b0c5bcc5 100644 --- a/options-table.c +++ b/options-table.c @@ -717,7 +717,7 @@ const struct options_table_entry options_table[] = { { .name = "pane-active-border-style", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "fg=green", + .default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,,fg=green}}", .flags = OPTIONS_TABLE_IS_STYLE, .separator = "," }, diff --git a/window.c b/window.c index cbbc0d3e..b1d812fc 100644 --- a/window.c +++ b/window.c @@ -1109,6 +1109,7 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, wp->screen = wme->screen; wp->flags |= (PANE_REDRAW|PANE_CHANGED); + server_redraw_window_borders(wp->window); server_status_window(wp->window); notify_pane("pane-mode-changed", wp); @@ -1140,6 +1141,7 @@ window_pane_reset_mode(struct window_pane *wp) } wp->flags |= (PANE_REDRAW|PANE_CHANGED); + server_redraw_window_borders(wp->window); server_status_window(wp->window); notify_pane("pane-mode-changed", wp); } From 7c52d702e4bb6d3783984001678f5be98b56b7e6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 08:59:20 +0100 Subject: [PATCH 0270/1006] Remove an unnecessary comma. --- options-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index b0c5bcc5..0781da46 100644 --- a/options-table.c +++ b/options-table.c @@ -717,7 +717,7 @@ const struct options_table_entry options_table[] = { { .name = "pane-active-border-style", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,,fg=green}}", + .default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}", .flags = OPTIONS_TABLE_IS_STYLE, .separator = "," }, From b06235c345d8d6426d3c8208b4e475e6c60ac884 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 13:56:10 +0100 Subject: [PATCH 0271/1006] Improve command prompt completion: - Show a menu with completions if there are multiple. - Don't complete argument stuff (options, layouts) at start of text. - For -t and -s, if there is no : then complete sessions but if there is a :, show a menu of all windows in the session rather than trying to complete the window name which is a bit useless if there are duplicates. Lots of scope for being more sophisticated left here. --- Makefile.am | 2 +- menu.c | 31 +++ server-client.c | 10 +- status.c | 507 +++++++++++++++++++++++++++++++----------------- tmux.h | 1 + 5 files changed, 370 insertions(+), 181 deletions(-) diff --git a/Makefile.am b/Makefile.am index a395ecdb..97174c1d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,7 @@ LDADD = $(LIBOBJS) if IS_GCC AM_CFLAGS += -std=gnu99 -O2 if IS_DEBUG -AM_CFLAGS += -g +AM_CFLAGS += -g -O0 AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare diff --git a/menu.c b/menu.c index 2f92af34..c6962036 100644 --- a/menu.c +++ b/menu.c @@ -240,6 +240,12 @@ menu_key_cb(struct client *c, struct key_event *event) } while ((name == NULL || *name == '-') && md->choice != old); c->flags |= CLIENT_REDRAWOVERLAY; return (0); + case '\011': /* Tab */ + if (~md->flags & MENU_TAB) + break; + if (md->choice == count - 1) + return (1); + /* FALLTHROUGH */ case KEYC_DOWN: case 'j': if (old == -1) @@ -253,6 +259,31 @@ menu_key_cb(struct client *c, struct key_event *event) } while ((name == NULL || *name == '-') && md->choice != old); c->flags |= CLIENT_REDRAWOVERLAY; return (0); + case 'g': + case KEYC_PPAGE: + case '\002': /* C-b */ + if (md->choice > 5) + md->choice -= 5; + else + md->choice = 0; + while (md->choice != count && (name == NULL || *name == '-')) + md->choice++; + if (md->choice == count) + md->choice = -1; + c->flags |= CLIENT_REDRAWOVERLAY; + break; + case 'G': + case KEYC_NPAGE: + if (md->choice > count - 6) + md->choice = count - 1; + else + md->choice += 5; + while (md->choice != -1 && (name == NULL || *name == '-')) + md->choice--; + c->flags |= CLIENT_REDRAWOVERLAY; + break; + case '\006': /* C-f */ + break; case '\r': goto chosen; case '\033': /* Escape */ diff --git a/server-client.c b/server-client.c index 92b3a6ad..5f7ce6b8 100644 --- a/server-client.c +++ b/server-client.c @@ -1294,10 +1294,6 @@ server_client_handle_key(struct client *c, struct key_event *event) */ if (~c->flags & CLIENT_READONLY) { status_message_clear(c); - if (c->prompt_string != NULL) { - if (status_prompt_key(c, event->key) == 0) - return (0); - } if (c->overlay_key != NULL) { switch (c->overlay_key(c, event)) { case 0: @@ -1308,6 +1304,10 @@ server_client_handle_key(struct client *c, struct key_event *event) } } server_client_clear_overlay(c); + if (c->prompt_string != NULL) { + if (status_prompt_key(c, event->key) == 0) + return (0); + } } /* @@ -1562,6 +1562,8 @@ server_client_reset_state(struct client *c) } else { s = wp->screen; 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); diff --git a/status.c b/status.c index c9da873e..0acf4233 100644 --- a/status.c +++ b/status.c @@ -37,9 +37,15 @@ static const char *status_prompt_up_history(u_int *); static const char *status_prompt_down_history(u_int *); static void status_prompt_add_history(const char *); -static char **status_prompt_complete_list(u_int *, const char *); -static char *status_prompt_complete_prefix(char **, u_int); -static char *status_prompt_complete(struct session *, const char *); +static char *status_prompt_complete(struct client *, const char *, u_int); + +struct status_prompt_menu { + struct client *c; + u_int start; + u_int size; + char **list; + char flag; +}; /* Status prompt history. */ #define PROMPT_HISTORY 100 @@ -919,15 +925,88 @@ status_prompt_paste(struct client *c) return (1); } +/* Finish completion. */ +static int +status_prompt_replace_complete(struct client *c, const char *s) +{ + char word[64], *allocated = NULL; + size_t size, n, off, idx, used; + struct utf8_data *first, *last, *ud; + + if (c->prompt_buffer[0].size == 0) + return (0); + size = utf8_strlen(c->prompt_buffer); + + idx = c->prompt_index; + if (idx != 0) + idx--; + + /* Find the word we are in. */ + first = &c->prompt_buffer[idx]; + while (first > c->prompt_buffer && !status_prompt_space(first)) + first--; + while (first->size != 0 && status_prompt_space(first)) + first++; + last = &c->prompt_buffer[idx]; + while (last->size != 0 && !status_prompt_space(last)) + last++; + while (last > c->prompt_buffer && status_prompt_space(last)) + last--; + if (last->size != 0) + last++; + if (last <= first) + return (0); + if (s == NULL) { + used = 0; + for (ud = first; ud < last; ud++) { + if (used + ud->size >= sizeof word) + break; + memcpy(word + used, ud->data, ud->size); + used += ud->size; + } + if (ud != last) + return (0); + word[used] = '\0'; + } + + /* Try to complete it. */ + if (s == NULL) { + allocated = status_prompt_complete(c, word, + first - c->prompt_buffer); + if (allocated == NULL) + return (0); + s = allocated; + } + + /* Trim out word. */ + n = size - (last - c->prompt_buffer) + 1; /* with \0 */ + memmove(first, last, n * sizeof *c->prompt_buffer); + size -= last - first; + + /* Insert the new word. */ + size += strlen(s); + off = first - c->prompt_buffer; + c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1, + sizeof *c->prompt_buffer); + first = c->prompt_buffer + off; + memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer); + for (idx = 0; idx < strlen(s); idx++) + utf8_set(&first[idx], s[idx]); + c->prompt_index = (first - c->prompt_buffer) + strlen(s); + + free(allocated); + return (1); +} + /* Handle keys in prompt. */ int status_prompt_key(struct client *c, key_code key) { struct options *oo = c->session->options; - char *s, *cp, word[64], prefix = '='; + char *s, *cp, prefix = '='; const char *histstr, *ws = NULL, *keystring; - size_t size, n, off, idx, used; - struct utf8_data tmp, *first, *last, *ud; + size_t size, idx; + struct utf8_data tmp; int keys; if (c->prompt_flags & PROMPT_KEY) { @@ -992,63 +1071,9 @@ process_key: } break; case '\011': /* Tab */ - if (c->prompt_buffer[0].size == 0) - break; - - idx = c->prompt_index; - if (idx != 0) - idx--; - - /* Find the word we are in. */ - first = &c->prompt_buffer[idx]; - while (first > c->prompt_buffer && !status_prompt_space(first)) - first--; - while (first->size != 0 && status_prompt_space(first)) - first++; - last = &c->prompt_buffer[idx]; - while (last->size != 0 && !status_prompt_space(last)) - last++; - while (last > c->prompt_buffer && status_prompt_space(last)) - last--; - if (last->size != 0) - last++; - if (last <= first) - break; - - used = 0; - for (ud = first; ud < last; ud++) { - if (used + ud->size >= sizeof word) - break; - memcpy(word + used, ud->data, ud->size); - used += ud->size; - } - if (ud != last) - break; - word[used] = '\0'; - - /* And try to complete it. */ - if ((s = status_prompt_complete(c->session, word)) == NULL) - break; - - /* Trim out word. */ - n = size - (last - c->prompt_buffer) + 1; /* with \0 */ - memmove(first, last, n * sizeof *c->prompt_buffer); - size -= last - first; - - /* Insert the new word. */ - size += strlen(s); - off = first - c->prompt_buffer; - c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1, - sizeof *c->prompt_buffer); - first = c->prompt_buffer + off; - memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer); - for (idx = 0; idx < strlen(s); idx++) - utf8_set(&first[idx], s[idx]); - - c->prompt_index = (first - c->prompt_buffer) + strlen(s); - free(s); - - goto changed; + if (status_prompt_replace_complete(c, NULL)) + goto changed; + break; case KEYC_BSPACE: case '\010': /* C-h */ if (c->prompt_index != 0) { @@ -1330,14 +1355,13 @@ status_prompt_add_history(const char *line) } /* Build completion list. */ -char ** -status_prompt_complete_list(u_int *size, const char *s) +static char ** +status_prompt_complete_list(u_int *size, const char *s, int at_start) { char **list = NULL; const char **layout, *value, *cp; const struct cmd_entry **cmdent; const struct options_table_entry *oe; - u_int idx; size_t slen = strlen(s), valuelen; struct options_entry *o; struct options_array_item *a; @@ -1353,18 +1377,6 @@ status_prompt_complete_list(u_int *size, const char *s) list[(*size)++] = xstrdup((*cmdent)->name); } } - for (oe = options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup(oe->name); - } - } - for (layout = layouts; *layout != NULL; layout++) { - if (strncmp(*layout, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup(*layout); - } - } o = options_get_only(global_options, "command-alias"); if (o != NULL) { a = options_array_first(o); @@ -1383,8 +1395,21 @@ status_prompt_complete_list(u_int *size, const char *s) a = options_array_next(a); } } - for (idx = 0; idx < (*size); idx++) - log_debug("complete %u: %s", idx, list[idx]); + if (at_start) + return (list); + + for (oe = options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, slen) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = xstrdup(oe->name); + } + } + for (layout = layouts; *layout != NULL; layout++) { + if (strncmp(*layout, s, slen) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = xstrdup(*layout); + } + } return (list); } @@ -1409,124 +1434,254 @@ status_prompt_complete_prefix(char **list, u_int size) return (out); } +/* Complete word menu callback. */ +static void +status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key, + void *data) +{ + struct status_prompt_menu *spm = data; + struct client *c = spm->c; + u_int i; + char *s; + + if (key != KEYC_NONE) { + idx += spm->start; + if (spm->flag == '\0') + s = xstrdup(spm->list[idx]); + else + xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); + if (status_prompt_replace_complete(c, s)) + c->flags |= CLIENT_REDRAWSTATUS; + free(s); + } + + for (i = 0; i < spm->size; i++) + free(spm->list[i]); + free(spm->list); +} + +/* Show complete word menu. */ +static int +status_prompt_complete_list_menu(struct client *c, char **list, u_int size, + u_int offset, char flag) +{ + struct menu *menu; + struct menu_item item; + struct status_prompt_menu *spm; + u_int lines = status_line_size(c), height, i; + u_int py; + + if (size <= 1) + return (0); + if (c->tty.sy - lines < 3) + return (0); + + spm = xmalloc(sizeof *spm); + spm->c = c; + spm->size = size; + spm->list = list; + spm->flag = flag; + + height = c->tty.sy - lines - 2; + if (height > 10) + height = 10; + if (height > size) + height = size; + spm->start = size - height; + + menu = menu_create(""); + for (i = spm->start; i < size; i++) { + item.name = list[i]; + item.key = '0' + (i - spm->start); + item.command = NULL; + menu_add_item(menu, &item, NULL, NULL, NULL); + } + + if (options_get_number(c->session->options, "status-position") == 0) + py = lines; + else + py = c->tty.sy - 3 - height; + offset += utf8_cstrwidth(c->prompt_string); + if (offset > 2) + offset -= 2; + else + offset = 0; + + if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, + py, c, NULL, status_prompt_menu_callback, spm) != 0) { + menu_free(menu); + free(spm); + return (0); + } + return (1); +} + +/* Show complete word menu. */ +static char * +status_prompt_complete_window_menu(struct client *c, struct session *s, + u_int offset, char flag) +{ + struct menu *menu; + struct menu_item item; + struct status_prompt_menu *spm; + struct winlink *wl; + char **list = NULL, *tmp; + u_int lines = status_line_size(c), height; + u_int py, size = 0; + + if (c->tty.sy - lines < 3) + return (NULL); + + spm = xmalloc(sizeof *spm); + spm->c = c; + spm->flag = flag; + + height = c->tty.sy - lines - 2; + if (height > 10) + height = 10; + spm->start = 0; + + menu = menu_create(""); + RB_FOREACH(wl, winlinks, &s->windows) { + list = xreallocarray(list, size + 1, sizeof *list); + xasprintf(&list[size++], "%s:%d", s->name, wl->idx); + + xasprintf(&tmp, "%s:%d (%s)", s->name, wl->idx, + wl->window->name); + item.name = tmp; + item.key = '0' + size - 1; + item.command = NULL; + menu_add_item(menu, &item, NULL, NULL, NULL); + free(tmp); + + if (size == height) + break; + } + if (size == 1) { + menu_free(menu); + xasprintf(&tmp, "-%c%s", flag, list[0]); + free(list[0]); + free(list); + return (tmp); + } + if (height > size) + height = size; + + spm->size = size; + spm->list = list; + + if (options_get_number(c->session->options, "status-position") == 0) + py = lines; + else + py = c->tty.sy - 3 - height; + offset += utf8_cstrwidth(c->prompt_string); + if (offset > 2) + offset -= 2; + else + offset = 0; + + if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, + py, c, NULL, status_prompt_menu_callback, spm) != 0) { + menu_free(menu); + free(spm); + return (NULL); + } + return (NULL); +} + +/* Sort complete list. */ +static int +status_prompt_complete_sort(const void *a, const void *b) +{ + const char **aa = (const char **)a, **bb = (const char **)b; + + return (strcmp(*aa, *bb)); +} + /* Complete word. */ static char * -status_prompt_complete(struct session *session, const char *s) +status_prompt_complete(struct client *c, const char *word, u_int offset) { - char **list = NULL; - const char *colon; + struct session *session, *loop; + const char *s, *colon; + size_t slen; + char **list = NULL, *copy = NULL, *out = NULL, *tmp; + char flag = '\0'; u_int size = 0, i; - struct session *s_loop; - struct winlink *wl; - struct window *w; - char *copy, *out, *tmp; - if (*s == '\0') + if (*word == '\0') return (NULL); - out = NULL; - if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) { - list = status_prompt_complete_list(&size, s); + if (strncmp(word, "-t", 2) != 0 && strncmp(word, "-s", 2) != 0) { + list = status_prompt_complete_list(&size, word, offset == 0); if (size == 0) out = NULL; else if (size == 1) xasprintf(&out, "%s ", list[0]); else out = status_prompt_complete_prefix(list, size); - for (i = 0; i < size; i++) - free(list[i]); - free(list); - return (out); - } - copy = xstrdup(s); - - colon = ":"; - if (copy[strlen(copy) - 1] == ':') - copy[strlen(copy) - 1] = '\0'; - else - colon = ""; - s = copy + 2; - - RB_FOREACH(s_loop, sessions, &sessions) { - if (strncmp(s_loop->name, s, strlen(s)) == 0) { - list = xreallocarray(list, size + 2, sizeof *list); - list[size++] = s_loop->name; - } - } - if (size == 1) { - out = xstrdup(list[0]); - if (session_find(list[0]) != NULL) - colon = ":"; - } else if (size != 0) - out = status_prompt_complete_prefix(list, size); - if (out != NULL) { - xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); - free(out); - out = tmp; goto found; } - colon = ""; - if (*s == ':') { - RB_FOREACH(wl, winlinks, &session->windows) { - xasprintf(&tmp, ":%s", wl->window->name); - if (strncmp(tmp, s, strlen(s)) == 0){ - list = xreallocarray(list, size + 1, - sizeof *list); - list[size++] = tmp; + s = word + 2; + slen = strlen(s); + + flag = word[1]; + offset += 2; + + colon = strchr(s, ':'); + + /* If there is no colon, complete as a session. */ + if (colon == NULL) { + RB_FOREACH(loop, sessions, &sessions) { + if (strncmp(loop->name, s, strlen(s)) != 0) continue; - } - free(tmp); - - xasprintf(&tmp, ":%d", wl->idx); - if (strncmp(tmp, s, strlen(s)) == 0) { - list = xreallocarray(list, size + 1, - sizeof *list); - list[size++] = tmp; - continue; - } - free(tmp); + list = xreallocarray(list, size + 2, sizeof *list); + xasprintf(&list[size++], "%s:", loop->name); } - } else { - RB_FOREACH(s_loop, sessions, &sessions) { - RB_FOREACH(wl, winlinks, &s_loop->windows) { - w = wl->window; - - xasprintf(&tmp, "%s:%s", s_loop->name, w->name); - if (strncmp(tmp, s, strlen(s)) == 0) { - list = xreallocarray(list, size + 1, - sizeof *list); - list[size++] = tmp; - continue; - } - free(tmp); - - xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx); - if (strncmp(tmp, s, strlen(s)) == 0) { - list = xreallocarray(list, size + 1, - sizeof *list); - list[size++] = tmp; - continue; - } - free(tmp); - } - } - } - if (size == 1) { - out = xstrdup(list[0]); - colon = " "; - } else if (size != 0) out = status_prompt_complete_prefix(list, size); - if (out != NULL) { - xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); - out = tmp; + if (out != NULL) { + xasprintf(&tmp, "-%c%s", flag, out); + free(out); + out = tmp; + } + goto found; } - for (i = 0; i < size; i++) - free((void *)list[i]); + /* If there is a colon but no period, find session and show a menu. */ + if (strchr(colon + 1, '.') == NULL) { + if (*s == ':') + session = c->session; + else { + copy = xstrdup(s); + *strchr(copy, ':') = '\0'; + session = session_find(copy); + free(copy); + if (session == NULL) + goto found; + } + out = status_prompt_complete_window_menu(c, session, offset, + flag); + if (out == NULL) + return (NULL); + } found: - free(copy); - free(list); + if (size != 0) { + qsort(list, size, sizeof *list, status_prompt_complete_sort); + for (i = 0; i < size; i++) + log_debug("complete %u: %s", i, list[i]); + } + + if (out != NULL && strcmp(word, out) == 0) { + free(out); + out = NULL; + } + if (out != NULL || + !status_prompt_complete_list_menu(c, list, size, offset, flag)) { + for (i = 0; i < size; i++) + free(list[i]); + free(list); + } return (out); } diff --git a/tmux.h b/tmux.h index e1b86322..d3cc1a53 100644 --- a/tmux.h +++ b/tmux.h @@ -2799,6 +2799,7 @@ __dead void printflike(1, 2) fatalx(const char *, ...); /* menu.c */ #define MENU_NOMOUSE 0x1 +#define MENU_TAB 0x2 struct menu *menu_create(const char *); void menu_add_items(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, From fe601e54171a431ce908f01c1f8aea3166af3d38 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 15:19:39 +0100 Subject: [PATCH 0272/1006] Update CHANGES. --- CHANGES | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index b218e46b..7cf046a7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,29 @@ CHANGES FROM 3.1 TO 3.2 -* Add extension terminfo(5) capabilities for margins. +* Sanitize session names like window names instead of forbidding invalid ones. + +* Check if the clear terminfo(5) capability starts with CSI and if so then + assume the terminal is VT100-like, rather than relying on the XT capability. + +* Improve command prompt tab completion and add menus both for strings and for + -t and -s (when used without a trailing space). + +* Change all the style options to string options so they can support formats. + Change pane-border-active-style to use this to change the border colour when + in a mode or with synchronize-panes on. This also implies a few minor changes + to existing behaviour: + + - set-option -a with a style option automatically inserts a comma between the + old value and appended text. + + - OSC 10 and 11 no longer set the window-style option, instead they store the + colour internally in the pane data and it is used as the default when the + option is evaluated. + + - status-fg and -bg now override status-style instead of the option values + being changed. + +* Add extension terminfo(5) capabilities for margins and focus reporting. * Try $XDG_CONFIG_HOME/tmux/tmux.conf as well as ~/.config/tmux/tmux.conf for configuration file (the search paths are in TMUX_CONF in Makefile.am). From d9fa122fd23f9769758026fa9866dda0379f3086 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 15:21:15 +0100 Subject: [PATCH 0273/1006] Do not want -O0 by default. --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 97174c1d..a395ecdb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,7 @@ LDADD = $(LIBOBJS) if IS_GCC AM_CFLAGS += -std=gnu99 -O2 if IS_DEBUG -AM_CFLAGS += -g -O0 +AM_CFLAGS += -g AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare From 04033add19a91ffce9e8495c27cae3049fc306c6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 15:26:49 +0100 Subject: [PATCH 0274/1006] Close menu on backspace with TAB flag. --- menu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/menu.c b/menu.c index c6962036..e265bf90 100644 --- a/menu.c +++ b/menu.c @@ -150,8 +150,11 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) struct screen_write_ctx ctx; u_int i, px = md->px, py = md->py; struct grid_cell gc; + struct format_tree *ft; - style_apply(&gc, c->session->curw->window->options, "mode-style", NULL); + ft = format_create_from_state(md->item, c, &md->fs); + style_apply(&gc, c->session->curw->window->options, "mode-style", ft); + format_free(ft); screen_write_start(&ctx, NULL, s); screen_write_clearscreen(&ctx, 8); @@ -240,6 +243,10 @@ menu_key_cb(struct client *c, struct key_event *event) } while ((name == NULL || *name == '-') && md->choice != old); c->flags |= CLIENT_REDRAWOVERLAY; return (0); + case KEYC_BSPACE: + if (~md->flags & MENU_TAB) + break; + return (1); case '\011': /* Tab */ if (~md->flags & MENU_TAB) break; From a9743fa047ed0e938d4185970ba0542c8265eb09 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 15:27:38 +0100 Subject: [PATCH 0275/1006] Did not mean to commit this bit. --- menu.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/menu.c b/menu.c index e265bf90..e78999c6 100644 --- a/menu.c +++ b/menu.c @@ -150,11 +150,8 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) struct screen_write_ctx ctx; u_int i, px = md->px, py = md->py; struct grid_cell gc; - struct format_tree *ft; - ft = format_create_from_state(md->item, c, &md->fs); - style_apply(&gc, c->session->curw->window->options, "mode-style", ft); - format_free(ft); + style_apply(&gc, c->session->curw->window->options, "mode-style", NULL); screen_write_start(&ctx, NULL, s); screen_write_clearscreen(&ctx, 8); From 881b8e9bb56651daf241574a5fc0c5c9691d73e4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 16:50:20 +0100 Subject: [PATCH 0276/1006] Handle cells outside any pane correctly. --- screen-redraw.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index fefb935d..cf6b7e69 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -45,6 +45,10 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *, #define CELL_BORDERS " xqlkmjwvtun~" +const struct grid_cell screen_redraw_border_cell = { + { { ' ' }, 0, 1, 1 }, GRID_ATTR_CHARSET, 0, 8, 8, 0 +}; + /* Return if window has only two panes. */ static int screen_redraw_two_panes(struct window *w) @@ -556,25 +560,30 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) struct tty *tty = &c->tty; struct window_pane *wp; u_int type, x = ctx->ox + i, y = ctx->oy + j; + int pane_status = ctx->pane_status; const struct grid_cell *gc; struct grid_cell copy; if (c->overlay_check != NULL && !c->overlay_check(c, x, y)) return; - type = screen_redraw_check_cell(c, x, y, ctx->pane_status, &wp); + type = screen_redraw_check_cell(c, x, y, pane_status, &wp); if (type == CELL_INSIDE) return; - gc = screen_redraw_draw_borders_style(ctx, x, y, wp); - if (gc == NULL) - return; + if (wp == NULL) + gc = &screen_redraw_border_cell; + else { + gc = screen_redraw_draw_borders_style(ctx, x, y, wp); + if (gc == NULL) + return; - if (server_is_marked(s, s->curw, marked_pane.wp) && - screen_redraw_check_is(x, y, ctx->pane_status, marked_pane.wp)) { - memcpy(©, gc, sizeof copy); - copy.attr ^= GRID_ATTR_REVERSE; - gc = © + if (server_is_marked(s, s->curw, marked_pane.wp) && + screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) { + memcpy(©, gc, sizeof copy); + copy.attr ^= GRID_ATTR_REVERSE; + gc = © + } } tty_attributes(tty, gc, NULL); From 6f700904a9f3dfdd611486686edf1439af55e5f0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 18:08:21 +0100 Subject: [PATCH 0277/1006] Copy mode search improvements: - Add styles for the search marking styles (copy-mode-match-style and copy-mode-current-match-style). - Show the current match (the one with the cursor on it) in a different style. - Copying without a selection will copy the current match if there is one. --- options-table.c | 16 ++++++ screen-write.c | 39 +------------ tmux.1 | 16 ++++++ tmux.h | 2 - window-copy.c | 148 +++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 167 insertions(+), 54 deletions(-) diff --git a/options-table.c b/options-table.c index 0781da46..e04002ff 100644 --- a/options-table.c +++ b/options-table.c @@ -655,6 +655,22 @@ const struct options_table_entry options_table[] = { .default_num = 1 }, + { .name = "copy-mode-match-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "bg=cyan,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," + }, + + { .name = "copy-mode-current-match-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "bg=magenta,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," + }, + { .name = "main-pane-height", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, diff --git a/screen-write.c b/screen-write.c index afa1e96a..e3e51020 100644 --- a/screen-write.c +++ b/screen-write.c @@ -344,44 +344,9 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, free(msg); } -/* Copy from another screen. Assumes target region is big enough. */ -void -screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, - u_int py, u_int nx, u_int ny, bitstr_t *mbs, const struct grid_cell *mgc) -{ - struct screen *s = ctx->s; - struct grid *gd = src->grid; - struct grid_cell gc; - u_int xx, yy, cx, cy, b; - - if (nx == 0 || ny == 0) - return; - - cx = s->cx; - cy = s->cy; - - for (yy = py; yy < py + ny; yy++) { - for (xx = px; xx < px + nx; xx++) { - grid_get_cell(gd, xx, yy, &gc); - if (mbs != NULL) { - b = (yy * screen_size_x(src)) + xx; - if (bit_test(mbs, b)) { - gc.attr = mgc->attr; - gc.fg = mgc->fg; - gc.bg = mgc->bg; - } - } - if (xx + gc.data.width <= px + nx) - screen_write_cell(ctx, &gc); - } - cy++; - screen_write_set_cursor(ctx, cx, cy); - } -} - /* - * Copy from another screen but without the selection stuff. Also assumes the - * target region is already big enough. + * Copy from another screen but without the selection stuff. Assumes the target + * region is already big enough. */ void screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, diff --git a/tmux.1 b/tmux.1 index a3a05878..c85e7e39 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3717,6 +3717,22 @@ If suffixed by .Ql % , this is a percentage of the window size. .Pp +.It Ic copy-mode-match-style Ar style +Set the style of search matches in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy-mode-current-match-style Ar style +Set the style of the current search match in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp .It Xo Ic mode-keys .Op Ic vi | emacs .Xc diff --git a/tmux.h b/tmux.h index d3cc1a53..c9c8637c 100644 --- a/tmux.h +++ b/tmux.h @@ -2436,8 +2436,6 @@ void screen_write_vnputs(struct screen_write_ctx *, ssize_t, const struct grid_cell *, const char *, va_list); void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *, u_char); -void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int, - u_int, u_int, u_int, bitstr_t *, const struct grid_cell *); void screen_write_fast_copy(struct screen_write_ctx *, struct screen *, u_int, u_int, u_int, u_int); void screen_write_hline(struct screen_write_ctx *, u_int, int, int); diff --git a/window-copy.c b/window-copy.c index 2c50a1cd..f5a07a10 100644 --- a/window-copy.c +++ b/window-copy.c @@ -257,12 +257,13 @@ struct window_copy_mode_data { int searchtype; int searchregex; char *searchstr; - bitstr_t *searchmark; + u_char *searchmark; u_int searchcount; int searchthis; int searchx; int searchy; int searcho; + u_char searchgen; int timeout; /* search has timed out */ #define WINDOW_COPY_SEARCH_TIMEOUT 10 @@ -362,10 +363,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->searchregex = 0; data->searchstr = NULL; } - data->searchmark = NULL; data->searchx = data->searchy = data->searcho = -1; - data->timeout = 0; - data->viewmode = 0; data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; @@ -2860,7 +2858,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, const struct grid_line *gl; int found, cis, which = -1; int cflags = REG_EXTENDED; - u_int px, py, b, nfound = 0, width; + u_int px, py, i, b, nfound = 0, width; u_int ssize = 1; char *sbuf; regex_t reg; @@ -2880,7 +2878,8 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, cis = window_copy_is_lowercase(data->searchstr); free(data->searchmark); - data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx); + data->searchmark = xcalloc(gd->hsize + gd->sy, gd->sx); + data->searchgen = 1; if (regex) { sbuf = xmalloc(ssize); @@ -2921,7 +2920,12 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, which = nfound; b = (py * gd->sx) + px; - bit_nset(data->searchmark, b, b + width - 1); + for (i = b; i < b + width; i++) + data->searchmark[i] = data->searchgen; + if (data->searchgen == UCHAR_MAX) + data->searchgen = 1; + else + data->searchgen++; px++; } @@ -2991,6 +2995,114 @@ window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) window_copy_redraw_screen(wme); } +static void +window_copy_match_start_end(struct window_copy_mode_data *data, u_int at, + u_int *start, u_int *end) +{ + struct grid *gd = data->backing->grid; + u_int last = (gd->hsize + gd->sy) * gd->sx - 1; + u_char mark = data->searchmark[at]; + + *start = *end = at; + while (*start != 0 && data->searchmark[*start] == mark) + (*start)--; + if (data->searchmark[*start] != mark) + (*start)++; + while (*end != last && data->searchmark[*end] == mark) + (*end)++; + if (data->searchmark[*end] != mark) + (*end)--; +} + +static char * +window_copy_match_at_cursor(struct window_copy_mode_data *data) +{ + struct grid *gd = data->backing->grid; + struct grid_cell gc; + u_int at, start, end, cy, px, py; + u_int sx = screen_size_x(data->backing); + char *buf = NULL; + size_t len = 0; + + if (data->searchmark == NULL) + return (NULL); + + cy = screen_hsize(data->backing) - data->oy + data->cy; + at = (cy * sx) + data->cx; + if (data->searchmark[at] == 0) + return (NULL); + window_copy_match_start_end(data, at, &start, &end); + + /* + * Cells will not be set in the marked array unless they are valid text + * and wrapping will be taken care of, so we can just copy. + */ + for (at = start; at <= end; at++) { + py = at / sx; + px = at % (py * sx); + + grid_get_cell(gd, px, py, &gc); + buf = xrealloc(buf, len + gc.data.size + 1); + memcpy(buf + len, gc.data.data, gc.data.size); + len += gc.data.size; + } + if (len != 0) + buf[len] = '\0'; + return (buf); +} + +static void +window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, + struct grid_cell *gc, const struct grid_cell *mgc, + const struct grid_cell *cgc) +{ + struct window_copy_mode_data *data = wme->data; + u_int mark, at, start, end, cy; + u_int sx = screen_size_x(data->backing); + + if (data->searchmark == NULL) + return; + mark = data->searchmark[(fy * sx) + fx]; + if (mark == 0) + return; + + cy = screen_hsize(data->backing) - data->oy + data->cy; + at = (cy * sx) + data->cx; + if (data->searchmark[at] == mark) { + window_copy_match_start_end(data, at, &start, &end); + if (at >= start && at <= end) { + gc->attr = cgc->attr; + gc->fg = cgc->fg; + gc->bg = cgc->bg; + } + return; + } + + gc->attr = mgc->attr; + gc->fg = mgc->fg; + gc->bg = mgc->bg; +} + +static void +window_copy_write_one(struct window_mode_entry *wme, + struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx, + const struct grid_cell *mgc, const struct grid_cell *cgc) +{ + struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->backing->grid; + struct grid_cell gc; + u_int fx; + + screen_write_cursormove(ctx, 0, py, 0); + for (fx = 0; fx < nx; fx++) { + grid_get_cell(gd, fx, fy, &gc); + if (fx + gc.data.width <= nx) { + window_copy_update_style(wme, fx, fy, &gc, mgc, cgc); + screen_write_cell(ctx, &gc); + } + } +} + static void window_copy_write_line(struct window_mode_entry *wme, struct screen_write_ctx *ctx, u_int py) @@ -2999,13 +3111,17 @@ window_copy_write_line(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct options *oo = wp->window->options; - struct grid_cell gc; + struct grid_cell gc, mgc, cgc; char hdr[512]; size_t size = 0; u_int hsize = screen_hsize(data->backing); style_apply(&gc, oo, "mode-style", NULL); gc.flags |= GRID_FLAG_NOPALETTE; + style_apply(&mgc, oo, "copy-mode-match-style", NULL); + mgc.flags |= GRID_FLAG_NOPALETTE; + style_apply(&cgc, oo, "copy-mode-current-match-style", NULL); + cgc.flags |= GRID_FLAG_NOPALETTE; if (py == 0 && s->rupper < s->rlower && !data->hide_position) { if (data->searchmark == NULL) { @@ -3035,15 +3151,13 @@ window_copy_write_line(struct window_mode_entry *wme, size = 0; if (size < screen_size_x(s)) { - screen_write_cursormove(ctx, 0, py, 0); - screen_write_copy(ctx, data->backing, 0, hsize - data->oy + py, - screen_size_x(s) - size, 1, data->searchmark, &gc); + window_copy_write_one(wme, ctx, py, hsize - data->oy + py, + screen_size_x(s) - size, &mgc, &cgc); } if (py == data->cy && data->cx == screen_size_x(s)) { - memcpy(&gc, &grid_default_cell, sizeof gc); screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0); - screen_write_putc(ctx, &gc, '$'); + screen_write_putc(ctx, &grid_default_cell, '$'); } } @@ -3354,8 +3468,12 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len) u_int firstsx, lastex, restex, restsx, selx; int keys; - if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) - return (NULL); + if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) { + buf = window_copy_match_at_cursor(data); + if (buf != NULL) + *len = strlen(buf); + return (buf); + } buf = xmalloc(1); off = 0; From 7324442b42061c99182d4f864c9cde609e19858c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 19:48:11 +0100 Subject: [PATCH 0278/1006] Add to CHANGES. --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 7cf046a7..9feca481 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ CHANGES FROM 3.1 TO 3.2 +* Improve search match marking in copy mode. Two new options + copy-mode-match-style and copy-mode-current-match-style to set the style for + matches and for the current match respectively. Also a change so that if a + copy key is pressed with no selection, the current match (if any) is copied. + * Sanitize session names like window names instead of forbidding invalid ones. * Check if the clear terminfo(5) capability starts with CSI and if so then From 5af694394018940819c0734be9b5fd44df1bf857 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 Apr 2020 19:55:20 +0100 Subject: [PATCH 0279/1006] Complete aliases as well as commands. --- status.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/status.c b/status.c index 0acf4233..712a93df 100644 --- a/status.c +++ b/status.c @@ -1376,6 +1376,11 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start) list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = xstrdup((*cmdent)->name); } + if ((*cmdent)->alias != NULL && + strncmp((*cmdent)->alias, s, slen) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = xstrdup((*cmdent)->alias); + } } o = options_get_only(global_options, "command-alias"); if (o != NULL) { From 25487757bcda66d027a79af9bc3283e2d6acfc39 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 30 Apr 2020 12:02:21 +0100 Subject: [PATCH 0280/1006] Add -W and -T flags to command-prompt to only complete a window and a target. --- cmd-command-prompt.c | 8 +++- key-bindings.c | 4 +- status.c | 107 ++++++++++++++++++++++++++++--------------- tmux.1 | 10 +++- tmux.h | 2 + 5 files changed, 90 insertions(+), 41 deletions(-) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index e53c4320..b8e3bd5c 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,8 +40,8 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1kiI:Np:t:", 0, 1 }, - .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " + .args = { "1kiI:Np:Tt:W", 0, 1 }, + .usage = "[-1kiNTW] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " "[template]", .flags = CMD_CLIENT_TFLAG, @@ -121,6 +121,10 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_INCREMENTAL; else if (args_has(args, 'k')) cdata->flags |= PROMPT_KEY; + else if (args_has(args, 'W')) + cdata->flags |= PROMPT_WINDOW; + else if (args_has(args, 'T')) + cdata->flags |= PROMPT_TARGET; status_prompt_set(tc, prompt, input, cmd_command_prompt_callback, cmd_command_prompt_free, cdata, cdata->flags); free(prompt); diff --git a/key-bindings.c b/key-bindings.c index 09a4eafa..85bfb788 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -243,12 +243,12 @@ key_bindings_init(void) "bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"", "bind -N 'Split window horizontally' % split-window -h", "bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window", - "bind -N 'Prompt for window index to select' \"'\" command-prompt -pindex \"select-window -t ':%%'\"", + "bind -N 'Prompt for window index to select' \"'\" command-prompt -Wpindex \"select-window -t ':%%'\"", "bind -N 'Switch to previous client' ( switch-client -p", "bind -N 'Switch to next client' ) switch-client -n", "bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"", "bind -N 'Delete the most recent paste buffer' - delete-buffer", - "bind -N 'Move the current window' . command-prompt \"move-window -t '%%'\"", + "bind -N 'Move the current window' . command-prompt -T \"move-window -t '%%'\"", "bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'", "bind -N 'Select window 0' 0 select-window -t:=0", "bind -N 'Select window 1' 1 select-window -t:=1", diff --git a/status.c b/status.c index 712a93df..77b92717 100644 --- a/status.c +++ b/status.c @@ -38,6 +38,8 @@ static const char *status_prompt_down_history(u_int *); static void status_prompt_add_history(const char *); static char *status_prompt_complete(struct client *, const char *, u_int); +static char *status_prompt_complete_window_menu(struct client *, + struct session *, u_int, char); struct status_prompt_menu { struct client *c; @@ -933,13 +935,11 @@ status_prompt_replace_complete(struct client *c, const char *s) size_t size, n, off, idx, used; struct utf8_data *first, *last, *ud; - if (c->prompt_buffer[0].size == 0) - return (0); - size = utf8_strlen(c->prompt_buffer); - + /* Work out where the cursor currently is. */ idx = c->prompt_index; if (idx != 0) idx--; + size = utf8_strlen(c->prompt_buffer); /* Find the word we are in. */ first = &c->prompt_buffer[idx]; @@ -954,7 +954,7 @@ status_prompt_replace_complete(struct client *c, const char *s) last--; if (last->size != 0) last++; - if (last <= first) + if (last < first) return (0); if (s == NULL) { used = 0; @@ -1071,7 +1071,15 @@ process_key: } break; case '\011': /* Tab */ - if (status_prompt_replace_complete(c, NULL)) + if (c->prompt_flags & PROMPT_WINDOW) { + s = status_prompt_complete_window_menu(c, c->session, + 0, '\0'); + if (s != NULL) { + free(c->prompt_buffer); + c->prompt_buffer = utf8_fromcstr(s); + c->prompt_index = utf8_strlen(c->prompt_buffer); + } + } else if (status_prompt_replace_complete(c, NULL)) goto changed; break; case KEYC_BSPACE: @@ -1455,7 +1463,12 @@ status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key, s = xstrdup(spm->list[idx]); else xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); - if (status_prompt_replace_complete(c, s)) + if (c->prompt_flags & PROMPT_WINDOW) { + free(c->prompt_buffer); + c->prompt_buffer = utf8_fromcstr(s); + c->prompt_index = utf8_strlen(c->prompt_buffer); + c->flags |= CLIENT_REDRAWSTATUS; + } else if (status_prompt_replace_complete(c, s)) c->flags |= CLIENT_REDRAWSTATUS; free(s); } @@ -1549,10 +1562,14 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, menu = menu_create(""); RB_FOREACH(wl, winlinks, &s->windows) { list = xreallocarray(list, size + 1, sizeof *list); - xasprintf(&list[size++], "%s:%d", s->name, wl->idx); - - xasprintf(&tmp, "%s:%d (%s)", s->name, wl->idx, - wl->window->name); + if (c->prompt_flags & PROMPT_WINDOW) { + xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); + xasprintf(&list[size++], "%d", wl->idx); + } else { + xasprintf(&tmp, "%s:%d (%s)", s->name, wl->idx, + wl->window->name); + xasprintf(&list[size++], "%s:%d", s->name, wl->idx); + } item.name = tmp; item.key = '0' + size - 1; item.command = NULL; @@ -1564,8 +1581,11 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, } if (size == 1) { menu_free(menu); - xasprintf(&tmp, "-%c%s", flag, list[0]); - free(list[0]); + if (flag != '\0') { + xasprintf(&tmp, "-%c%s", flag, list[0]); + free(list[0]); + } else + tmp = list[0]; free(list); return (tmp); } @@ -1603,21 +1623,45 @@ status_prompt_complete_sort(const void *a, const void *b) return (strcmp(*aa, *bb)); } +/* Complete a session. */ +static char * +status_prompt_complete_session(char ***list, u_int *size, const char *s, + char flag) +{ + struct session *loop; + char *out, *tmp; + + RB_FOREACH(loop, sessions, &sessions) { + if (*s != '\0' && strncmp(loop->name, s, strlen(s)) != 0) + continue; + *list = xreallocarray(*list, (*size) + 2, sizeof **list); + xasprintf(&(*list)[(*size)++], "%s:", loop->name); + } + out = status_prompt_complete_prefix(*list, *size); + if (out != NULL && flag != '\0') { + xasprintf(&tmp, "-%c%s", flag, out); + free(out); + out = tmp; + } + return (out); +} + /* Complete word. */ static char * status_prompt_complete(struct client *c, const char *word, u_int offset) { - struct session *session, *loop; + struct session *session; const char *s, *colon; - size_t slen; - char **list = NULL, *copy = NULL, *out = NULL, *tmp; + char **list = NULL, *copy = NULL, *out = NULL; char flag = '\0'; u_int size = 0, i; - if (*word == '\0') + if (*word == '\0' && (~c->prompt_flags & PROMPT_TARGET)) return (NULL); - if (strncmp(word, "-t", 2) != 0 && strncmp(word, "-s", 2) != 0) { + if ((~c->prompt_flags & PROMPT_TARGET) && + strncmp(word, "-t", 2) != 0 && + strncmp(word, "-s", 2) != 0) { list = status_prompt_complete_list(&size, word, offset == 0); if (size == 0) out = NULL; @@ -1628,28 +1672,19 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) goto found; } - s = word + 2; - slen = strlen(s); - - flag = word[1]; - offset += 2; - + if (c->prompt_flags & PROMPT_TARGET) { + s = word; + flag = '\0'; + } else { + s = word + 2; + flag = word[1]; + offset += 2; + } colon = strchr(s, ':'); /* If there is no colon, complete as a session. */ if (colon == NULL) { - RB_FOREACH(loop, sessions, &sessions) { - if (strncmp(loop->name, s, strlen(s)) != 0) - continue; - list = xreallocarray(list, size + 2, sizeof *list); - xasprintf(&list[size++], "%s:", loop->name); - } - out = status_prompt_complete_prefix(list, size); - if (out != NULL) { - xasprintf(&tmp, "-%c%s", flag, out); - free(out); - out = tmp; - } + out = status_prompt_complete_session(&list, &size, s, flag); goto found; } diff --git a/tmux.1 b/tmux.1 index c85e7e39..198f5660 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4969,7 +4969,7 @@ session option. Commands related to the status line are as follows: .Bl -tag -width Ds .It Xo Ic command-prompt -.Op Fl 1ikN +.Op Fl 1ikNTW .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client @@ -5028,6 +5028,14 @@ makes the prompt only accept numeric key presses. .Fl i executes the command every time the prompt input changes instead of when the user exits the command prompt. +.Fl T +tells +.Nm +that the prompt is for a target which affects what completions are offered when +.Em Tab +is pressed; +.Fl W +is similar but indicates the prompt is for a window. .Pp The following keys have a special meaning in the command prompt, depending on the value of the diff --git a/tmux.h b/tmux.h index c9c8637c..7d7e0e2d 100644 --- a/tmux.h +++ b/tmux.h @@ -1604,6 +1604,8 @@ struct client { #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 #define PROMPT_KEY 0x10 +#define PROMPT_WINDOW 0x20 +#define PROMPT_TARGET 0x40 int prompt_flags; struct session *session; From 66bab1f6cfb369857d4bfa2e34504f75a749456a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 30 Apr 2020 13:05:21 +0100 Subject: [PATCH 0281/1006] Complete partial window indexes properly. --- status.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/status.c b/status.c index 77b92717..397faf19 100644 --- a/status.c +++ b/status.c @@ -39,7 +39,7 @@ static void status_prompt_add_history(const char *); static char *status_prompt_complete(struct client *, const char *, u_int); static char *status_prompt_complete_window_menu(struct client *, - struct session *, u_int, char); + struct session *, const char *, u_int, char); struct status_prompt_menu { struct client *c; @@ -1071,15 +1071,7 @@ process_key: } break; case '\011': /* Tab */ - if (c->prompt_flags & PROMPT_WINDOW) { - s = status_prompt_complete_window_menu(c, c->session, - 0, '\0'); - if (s != NULL) { - free(c->prompt_buffer); - c->prompt_buffer = utf8_fromcstr(s); - c->prompt_index = utf8_strlen(c->prompt_buffer); - } - } else if (status_prompt_replace_complete(c, NULL)) + if (status_prompt_replace_complete(c, NULL)) goto changed; break; case KEYC_BSPACE: @@ -1537,7 +1529,7 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size, /* Show complete word menu. */ static char * status_prompt_complete_window_menu(struct client *c, struct session *s, - u_int offset, char flag) + const char *word, u_int offset, char flag) { struct menu *menu; struct menu_item item; @@ -1561,6 +1553,15 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, menu = menu_create(""); RB_FOREACH(wl, winlinks, &s->windows) { + if (word != NULL && *word != '\0') { + xasprintf(&tmp, "%d", wl->idx); + if (strncmp(tmp, word, strlen(word)) != 0) { + free(tmp); + continue; + } + free(tmp); + } + list = xreallocarray(list, size + 1, sizeof *list); if (c->prompt_flags & PROMPT_WINDOW) { xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); @@ -1579,6 +1580,10 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, if (size == height) break; } + if (size == 0) { + menu_free(menu); + return (NULL); + } if (size == 1) { menu_free(menu); if (flag != '\0') { @@ -1656,10 +1661,11 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) char flag = '\0'; u_int size = 0, i; - if (*word == '\0' && (~c->prompt_flags & PROMPT_TARGET)) + if (*word == '\0' && + ((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0)) return (NULL); - if ((~c->prompt_flags & PROMPT_TARGET) && + if (((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0) && strncmp(word, "-t", 2) != 0 && strncmp(word, "-s", 2) != 0) { list = status_prompt_complete_list(&size, word, offset == 0); @@ -1672,7 +1678,7 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) goto found; } - if (c->prompt_flags & PROMPT_TARGET) { + if (c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) { s = word; flag = '\0'; } else { @@ -1680,6 +1686,13 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) flag = word[1]; offset += 2; } + + /* If this is a window completion, open the window menu. */ + if (c->prompt_flags & PROMPT_WINDOW) { + out = status_prompt_complete_window_menu(c, c->session, s, + offset, '\0'); + goto found; + } colon = strchr(s, ':'); /* If there is no colon, complete as a session. */ @@ -1700,8 +1713,8 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) if (session == NULL) goto found; } - out = status_prompt_complete_window_menu(c, session, offset, - flag); + out = status_prompt_complete_window_menu(c, session, colon + 1, + offset, flag); if (out == NULL) return (NULL); } From 048f1ff18adda4a3b7a5771924defea052cd7884 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 30 Apr 2020 13:31:22 +0000 Subject: [PATCH 0282/1006] Do not remove the automatic-rename option from the global set, only from the window (it must stay in the global set or tmux will crash). GitHub issue 2188. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 4901e886..919cf15d 100644 --- a/input.c +++ b/input.c @@ -2348,7 +2348,7 @@ input_exit_rename(struct input_ctx *ictx) return; if (ictx->input_len == 0) { - oe = options_get(wp->window->options, "automatic-rename"); + oe = options_get_only(wp->window->options, "automatic-rename"); if (oe != NULL) options_remove(oe); return; From 272f3dbf2e4230064be6de6d6d6a71c724f49ec6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 30 Apr 2020 14:56:46 +0100 Subject: [PATCH 0283/1006] Use format for status-style. --- status.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/status.c b/status.c index 397faf19..89216f8c 100644 --- a/status.c +++ b/status.c @@ -346,8 +346,15 @@ status_redraw(struct client *c) if (c->tty.sy == 0 || lines == 0) return (1); + /* Create format tree. */ + flags = FORMAT_STATUS; + if (c->flags & CLIENT_STATUSFORCE) + flags |= FORMAT_FORCE; + ft = format_create(c, NULL, FORMAT_NONE, flags); + format_defaults(ft, c, NULL, NULL, NULL); + /* Set up default colour. */ - style_apply(&gc, s->options, "status-style", NULL); + style_apply(&gc, s->options, "status-style", ft); fg = options_get_number(s->options, "status-fg"); if (fg != 8) gc.fg = fg; @@ -367,13 +374,6 @@ status_redraw(struct client *c) } screen_write_start(&ctx, NULL, &sl->screen); - /* Create format tree. */ - flags = FORMAT_STATUS; - if (c->flags & CLIENT_STATUSFORCE) - flags |= FORMAT_FORCE; - ft = format_create(c, NULL, FORMAT_NONE, flags); - format_defaults(ft, c, NULL, NULL, NULL); - /* Write the status lines. */ o = options_get(s->options, "status-format"); if (o == NULL) { From 8e01221d022fa702dd99db52285b2d2941e0f4cc Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 30 Apr 2020 14:59:58 +0100 Subject: [PATCH 0284/1006] Allow formats for message-style. --- status.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/status.c b/status.c index 89216f8c..a4844294 100644 --- a/status.c +++ b/status.c @@ -490,6 +490,7 @@ status_message_redraw(struct client *c) size_t len; u_int lines, offset; struct grid_cell gc; + struct format_tree *ft; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); @@ -504,7 +505,9 @@ status_message_redraw(struct client *c) if (len > c->tty.sx) len = c->tty.sx; - style_apply(&gc, s->options, "message-style", NULL); + ft = format_create_defaults(NULL, c, NULL, NULL, NULL); + style_apply(&gc, s->options, "message-style", ft); + format_free(ft); screen_write_start(&ctx, NULL, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); @@ -636,6 +639,7 @@ status_prompt_redraw(struct client *c) u_int i, lines, offset, left, start, width; u_int pcursor, pwidth; struct grid_cell gc, cursorgc; + struct format_tree *ft; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); @@ -646,10 +650,12 @@ status_prompt_redraw(struct client *c) lines = 1; screen_init(sl->active, c->tty.sx, lines, 0); + ft = format_create_defaults(NULL, c, NULL, NULL, NULL); if (c->prompt_mode == PROMPT_COMMAND) - style_apply(&gc, s->options, "message-command-style", NULL); + style_apply(&gc, s->options, "message-command-style", ft); else - style_apply(&gc, s->options, "message-style", NULL); + style_apply(&gc, s->options, "message-style", ft); + format_free(ft); memcpy(&cursorgc, &gc, sizeof cursorgc); cursorgc.attr ^= GRID_ATTR_REVERSE; From 6a33a12798b2afeee6fb7bba74d86d628137921e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 30 Apr 2020 15:20:08 +0100 Subject: [PATCH 0285/1006] Do not remove the automatic-rename option from the global set, only from the window (it must stay in the global set or tmux will crash). GitHub issue 2188. --- CHANGES | 4 ++++ configure.ac | 2 +- input.c | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 12d6aac1..ad215116 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +CHANGES FROM 3.1a TO 3.1b + +* Fix crash when allow-rename is on and an empty name is set. + CHANGES FROM 3.1 TO 3.1a * Do not close stdout prematurely in control mode since it is needed to print diff --git a/configure.ac b/configure.ac index 6a9ba594..d5dbd9c4 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.1a) +AC_INIT([tmux], 3.1b) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) diff --git a/input.c b/input.c index 82d2b398..e794be2a 100644 --- a/input.c +++ b/input.c @@ -2304,7 +2304,7 @@ input_exit_rename(struct input_ctx *ictx) return; if (ictx->input_len == 0) { - oe = options_get(wp->window->options, "automatic-rename"); + oe = options_get_only(wp->window->options, "automatic-rename"); if (oe != NULL) options_remove(oe); return; From cc19203be2aab1adc4930b18e46d522005159412 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 09:02:44 +0100 Subject: [PATCH 0286/1006] Add 'e' key in buffer mode to open the buffer in an editor. --- cmd-display-menu.c | 2 +- format-draw.c | 2 +- job.c | 7 +++ options-table.c | 6 ++ paste.c | 9 +++ popup.c | 9 ++- server.c | 1 + tmux.1 | 5 ++ tmux.c | 1 + tmux.h | 5 +- window-buffer.c | 133 +++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 176 insertions(+), 4 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 0a5c7f78..ae322444 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -313,7 +313,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, - cmd, cwd, tc, target) != 0) + cmd, cwd, tc, target, NULL, NULL) != 0) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } diff --git a/format-draw.c b/format-draw.c index 3ac33ce4..4a4fc6bc 100644 --- a/format-draw.c +++ b/format-draw.c @@ -738,7 +738,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, /* * Draw the screens. How they are arranged depends on where the list - * appearsq. + * appears. */ switch (list_align) { case STYLE_ALIGN_DEFAULT: diff --git a/job.c b/job.c index 607f7138..6c1a3e09 100644 --- a/job.c +++ b/job.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -283,6 +284,12 @@ job_check_died(pid_t pid, int status) } if (job == NULL) return; + if (WIFSTOPPED(status)) { + if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) + return; + killpg(job->pid, SIGCONT); + return; + } log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); job->status = status; diff --git a/options-table.c b/options-table.c index e04002ff..b4aa47e2 100644 --- a/options-table.c +++ b/options-table.c @@ -209,6 +209,12 @@ const struct options_table_entry options_table[] = { .default_str = "screen" }, + { .name = "editor", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = _PATH_VI + }, + { .name = "escape-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, diff --git a/paste.c b/paste.c index c1036ad9..f1f3c9f7 100644 --- a/paste.c +++ b/paste.c @@ -296,6 +296,15 @@ paste_set(char *data, size_t size, const char *name, char **cause) return (0); } +/* Set paste data without otherwise changing it. */ +void +paste_replace(struct paste_buffer *pb, char *data, size_t size) +{ + free(pb->data); + pb->data = data; + pb->size = size; +} + /* Convert start of buffer into a nice string. */ char * paste_make_sample(struct paste_buffer *pb) diff --git a/popup.c b/popup.c index 5d39e599..160b5aa3 100644 --- a/popup.c +++ b/popup.c @@ -40,6 +40,8 @@ struct popup_data { struct job *job; struct input_ctx *ictx; int status; + popup_close_cb cb; + void *arg; u_int px; u_int py; @@ -150,6 +152,9 @@ popup_free_cb(struct client *c) struct cmdq_item *item = pd->item; u_int i; + if (pd->cb != NULL) + pd->cb(pd->status, pd->arg); + if (item != NULL) { if (pd->ictx != NULL && cmdq_get_client(item) != NULL && @@ -403,7 +408,7 @@ int popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, u_int sy, u_int nlines, const char **lines, const char *shellcmd, const char *cmd, const char *cwd, struct client *c, - struct cmd_find_state *fs) + struct cmd_find_state *fs, popup_close_cb cb, void *arg) { struct popup_data *pd; u_int i; @@ -422,6 +427,8 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->c = c; pd->c->references++; + pd->cb = cb; + pd->arg = arg; pd->status = 128 + SIGHUP; if (fs != NULL) diff --git a/server.c b/server.c index bf8d0310..1077377b 100644 --- a/server.c +++ b/server.c @@ -480,4 +480,5 @@ server_child_stopped(pid_t pid, int status) } } } + job_check_died(pid, status); } diff --git a/tmux.1 b/tmux.1 index 198f5660..0c5b6625 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3113,6 +3113,10 @@ Set the time in milliseconds for which waits after an escape is input to determine if it is part of a function or meta key sequences. The default is 500 milliseconds. +.It Ic editor Ar shell-command +Set the command used when +.Nm +runs an editor. .It Xo Ic exit-empty .Op Ic on | off .Xc @@ -5339,6 +5343,7 @@ The following keys may be used in buffer mode: .It Li "P" Ta "Paste tagged buffers" .It Li "d" Ta "Delete selected buffer" .It Li "D" Ta "Delete tagged buffers" +,It Li "e" Ta "Open the buffer in an editor" .It Li "f" Ta "Enter a format to filter items" .It Li "O" Ta "Change sort field" .It Li "r" Ta "Reverse sort order" diff --git a/tmux.c b/tmux.c index 82c3441c..48b3b8b9 100644 --- a/tmux.c +++ b/tmux.c @@ -434,6 +434,7 @@ main(int argc, char **argv) /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { + options_set_string(global_options, "editor", 0, "%s", s); if (strrchr(s, '/') != NULL) s = strrchr(s, '/') + 1; if (strstr(s, "vi") != NULL) diff --git a/tmux.h b/tmux.h index 7d7e0e2d..94d130ba 100644 --- a/tmux.h +++ b/tmux.h @@ -1808,6 +1808,7 @@ void paste_free(struct paste_buffer *); void paste_add(const char *, char *, size_t); int paste_rename(const char *, const char *, char **); int paste_set(char *, size_t, const char *, char **); +void paste_replace(struct paste_buffer *, char *, size_t); char *paste_make_sample(struct paste_buffer *); /* format.c */ @@ -2816,12 +2817,14 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int, #define POPUP_WRITEKEYS 0x1 #define POPUP_CLOSEEXIT 0x2 #define POPUP_CLOSEEXITZERO 0x4 +typedef void (*popup_close_cb)(int, void *); u_int popup_width(struct cmdq_item *, u_int, const char **, struct client *, struct cmd_find_state *); u_int popup_height(u_int, const char **); int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, u_int, u_int, const char **, const char *, const char *, - const char *, struct client *, struct cmd_find_state *); + const char *, struct client *, struct cmd_find_state *, + popup_close_cb, void *); /* style.c */ int style_parse(struct style *,const struct grid_cell *, diff --git a/window-buffer.c b/window-buffer.c index cf707abe..4e17a7fa 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -18,9 +18,11 @@ #include +#include #include #include #include +#include #include "tmux.h" @@ -94,6 +96,13 @@ struct window_buffer_modedata { u_int item_size; }; +struct window_buffer_editdata { + u_int wp_id; + char *path; + char *name; + struct paste_buffer *pb; +}; + static struct window_buffer_itemdata * window_buffer_add_item(struct window_buffer_modedata *data) { @@ -352,6 +361,126 @@ window_buffer_do_paste(void *modedata, void *itemdata, struct client *c, mode_tree_run_command(c, NULL, data->command, item->name); } +static void +window_buffer_finish_edit(struct window_buffer_editdata *ed) +{ + unlink(ed->path); + free(ed->path); + free(ed->name); + free(ed); +} + +static void +window_buffer_edit_close_cb(int status, void *arg) +{ + struct window_buffer_editdata *ed = arg; + FILE *f; + off_t len; + char *buf; + size_t oldlen; + const char *oldbuf; + struct paste_buffer *pb; + struct window_pane *wp; + struct window_buffer_modedata *data; + struct window_mode_entry *wme; + + if (status != 0) { + window_buffer_finish_edit(ed); + return; + } + + pb = paste_get_name(ed->name); + if (pb == NULL || pb != ed->pb) { + window_buffer_finish_edit(ed); + return; + } + + f = fopen(ed->path, "r"); + if (f != NULL) { + fseeko(f, 0, SEEK_END); + len = ftello(f); + fseeko(f, 0, SEEK_SET); + + if (len > 0 && + (uintmax_t)len <= (uintmax_t)SIZE_MAX && + (buf = malloc(len)) != NULL && + fread(buf, len, 1, f) == 1) { + oldbuf = paste_buffer_data(pb, &oldlen); + if (oldlen != '\0' && + oldbuf[oldlen - 1] != '\n' && + buf[len - 1] == '\n') + len--; + if (len != 0) + paste_replace(pb, buf, len); + } + fclose(f); + } + + wp = window_pane_find_by_id(ed->wp_id); + if (wp != NULL) { + wme = TAILQ_FIRST(&wp->modes); + if (wme->mode == &window_buffer_mode) { + data = wme->data; + mode_tree_build(data->data); + mode_tree_draw(data->data); + } + wp->flags |= PANE_REDRAW; + } + window_buffer_finish_edit(ed); +} + +static void +window_buffer_start_edit(struct window_buffer_modedata *data, + struct window_buffer_itemdata *item, struct client *c) +{ + struct paste_buffer *pb; + int fd; + FILE *f; + const char *buf; + size_t len; + struct window_buffer_editdata *ed; + char *cmd; + char path[] = _PATH_TMP "tmux.XXXXXXXX"; + const char *editor; + u_int px, py, sx, sy; + + if ((pb = paste_get_name(item->name)) == NULL) + return; + buf = paste_buffer_data(pb, &len); + + editor = options_get_string(global_options, "editor"); + if (*editor == '\0') + return; + + fd = mkstemp(path); + if (fd == -1) + return; + f = fdopen(fd, "w"); + if (fwrite(buf, len, 1, f) != 1) { + fclose(f); + return; + } + fclose(f); + + ed = xcalloc(1, sizeof *ed); + ed->wp_id = data->wp->id; + ed->path = xstrdup(path); + ed->name = xstrdup(paste_buffer_name(pb)); + ed->pb = pb; + + sx = c->tty.sx * 9 / 10; + sy = c->tty.sy * 9 / 10; + px = (c->tty.sx / 2) - (sx / 2); + py = (c->tty.sy / 2) - (sy / 2); + + xasprintf(&cmd, "%s %s", editor, path); + if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, + 0, NULL, cmd, NULL, _PATH_TMP, c, NULL, window_buffer_edit_close_cb, + ed) != 0) + window_buffer_finish_edit(ed); + free(cmd); +} + static void window_buffer_key(struct window_mode_entry *wme, struct client *c, __unused struct session *s, __unused struct winlink *wl, key_code key, @@ -365,6 +494,10 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c, finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); switch (key) { + case 'e': + item = mode_tree_get_current(mtd); + window_buffer_start_edit(data, item, c); + break; case 'd': item = mode_tree_get_current(mtd); window_buffer_do_delete(data, item, c, key); From 5c888e168eef6b711b968fb16d502d4c9b25b1c2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 09:05:56 +0100 Subject: [PATCH 0287/1006] Typo in man page. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 0c5b6625..060dd791 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5343,7 +5343,7 @@ The following keys may be used in buffer mode: .It Li "P" Ta "Paste tagged buffers" .It Li "d" Ta "Delete selected buffer" .It Li "D" Ta "Delete tagged buffers" -,It Li "e" Ta "Open the buffer in an editor" +.It Li "e" Ta "Open the buffer in an editor" .It Li "f" Ta "Enter a format to filter items" .It Li "O" Ta "Change sort field" .It Li "r" Ta "Reverse sort order" From ec61aa30251974118b6a19438ca5bffc616eb10e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 09:11:56 +0100 Subject: [PATCH 0288/1006] Solaris at least does not have _PATH_VI. --- compat.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compat.h b/compat.h index b7ec5a69..736a14fc 100644 --- a/compat.h +++ b/compat.h @@ -90,6 +90,10 @@ void warnx(const char *, ...); #define _PATH_DEFPATH "/usr/bin:/bin" #endif +#ifndef _PATH_VI +#define _PATH_VI "/usr/bin/vi" +#endif + #ifndef __OpenBSD__ #define pledge(s, p) (0) #endif From af21e76fdb36e6d2615c15f0d791eac6d1fd0062 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 12:01:58 +0100 Subject: [PATCH 0289/1006] Add -e for new-session. --- cmd-new-session.c | 16 +++++++++++----- tmux.1 | 6 ++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index f08155c0..9815e1e1 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -39,10 +39,10 @@ const struct cmd_entry cmd_new_session_entry = { .name = "new-session", .alias = "new", - .args = { "Ac:dDEF:n:Ps:t:x:Xy:", 0, -1 }, - .usage = "[-AdDEPX] [-c start-directory] [-F format] [-n window-name] " - "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " - "[-y height] [command]", + .args = { "Ac:dDe:EF:n:Ps:t:x:Xy:", 0, -1 }, + .usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] " + "[-n window-name] [-s session-name] " + CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -75,7 +75,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct options *oo; struct termios tio, *tiop; struct session_group *sg = NULL; - const char *errstr, *template, *group, *tmp; + const char *errstr, *template, *group, *tmp, *add; char *cause, *cwd = NULL, *cp, *newname = NULL; char *name, *prefix = NULL; int detached, already_attached, is_control = 0; @@ -83,6 +83,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct spawn_context sc; enum cmd_retval retval; struct cmd_find_state fs; + struct args_value *value; if (cmd_get_entry(self) == &cmd_has_session_entry) { /* @@ -254,6 +255,11 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) env = environ_create(); if (c != NULL && !args_has(args, 'E')) environ_update(global_s_options, c->environ, env); + add = args_first_value(args, 'e', &value); + while (add != NULL) { + environ_put(env, add, 0); + add = args_next_value(&value); + } s = session_create(prefix, newname, cwd, env, oo, tiop); /* Spawn the initial window. */ diff --git a/tmux.1 b/tmux.1 index 060dd791..2b460519 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1094,6 +1094,7 @@ Lock all clients attached to .It Xo Ic new-session .Op Fl AdDEPX .Op Fl c Ar start-directory +.Op Fl e Ar environment .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name @@ -1200,6 +1201,11 @@ If is used, the .Ic update-environment option will not be applied. +.Fl e +takes the form +.Ql VARIABLE=value +and sets an environment variable for the newly created session; it may be +specified multiple times. .It Xo Ic refresh-client .Op Fl cDlLRSU .Op Fl C Ar XxY From 5ce194f15dddff403506255d1e67c101a7ebef7f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 13:01:38 +0100 Subject: [PATCH 0290/1006] Rename some tty_ctx members. --- tmux.h | 12 ++++----- tty.c | 80 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/tmux.h b/tmux.h index 94d130ba..e00a4f96 100644 --- a/tmux.h +++ b/tmux.h @@ -1307,19 +1307,19 @@ struct tty_ctx { u_int orupper; u_int orlower; - /* Pane offset. */ + /* Target region (usually pane) offset and size. */ u_int xoff; u_int yoff; /* The background colour used for clearing (erasing). */ u_int bg; - /* Window offset and size. */ + /* Containing region (usually window) offset and size. */ int bigger; - u_int ox; - u_int oy; - u_int sx; - u_int sy; + u_int wox; + u_int woy; + u_int wsx; + u_int wsy; }; /* Saved message entry. */ diff --git a/tty.c b/tty.c index e378b45d..9f197ff3 100644 --- a/tty.c +++ b/tty.c @@ -970,8 +970,8 @@ tty_is_visible(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, else lines = 0; - if (xoff + nx <= ctx->ox || xoff >= ctx->ox + ctx->sx || - yoff + ny <= ctx->oy || yoff >= lines + ctx->oy + ctx->sy) + if (xoff + nx <= ctx->wox || xoff >= ctx->wox + ctx->wsx || + yoff + ny <= ctx->woy || yoff >= lines + ctx->woy + ctx->wsy) return (0); return (1); } @@ -986,28 +986,28 @@ tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, if (!tty_is_visible(tty, ctx, px, py, nx, 1)) return (0); - *ry = ctx->yoff + py - ctx->oy; + *ry = ctx->yoff + py - ctx->woy; - if (xoff >= ctx->ox && xoff + nx <= ctx->ox + ctx->sx) { + if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) { /* All visible. */ *i = 0; - *x = ctx->xoff + px - ctx->ox; + *x = ctx->xoff + px - ctx->wox; *rx = nx; - } else if (xoff < ctx->ox && xoff + nx > ctx->ox + ctx->sx) { + } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) { /* Both left and right not visible. */ - *i = ctx->ox; + *i = ctx->wox; *x = 0; - *rx = ctx->sx; - } else if (xoff < ctx->ox) { + *rx = ctx->wsx; + } else if (xoff < ctx->wox) { /* Left not visible. */ - *i = ctx->ox - (ctx->xoff + px); + *i = ctx->wox - (ctx->xoff + px); *x = 0; *rx = nx - *i; } else { /* Right not visible. */ *i = 0; - *x = (ctx->xoff + px) - ctx->ox; - *rx = ctx->sx - *x; + *x = (ctx->xoff + px) - ctx->wox; + *rx = ctx->wsx - *x; } if (*rx > nx) fatalx("%s: x too big, %u > %u", __func__, *rx, nx); @@ -1083,50 +1083,50 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, if (!tty_is_visible(tty, ctx, px, py, nx, ny)) return (0); - if (xoff >= ctx->ox && xoff + nx <= ctx->ox + ctx->sx) { + if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) { /* All visible. */ *i = 0; - *x = ctx->xoff + px - ctx->ox; + *x = ctx->xoff + px - ctx->wox; *rx = nx; - } else if (xoff < ctx->ox && xoff + nx > ctx->ox + ctx->sx) { + } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) { /* Both left and right not visible. */ - *i = ctx->ox; + *i = ctx->wox; *x = 0; - *rx = ctx->sx; - } else if (xoff < ctx->ox) { + *rx = ctx->wsx; + } else if (xoff < ctx->wox) { /* Left not visible. */ - *i = ctx->ox - (ctx->xoff + px); + *i = ctx->wox - (ctx->xoff + px); *x = 0; *rx = nx - *i; } else { /* Right not visible. */ *i = 0; - *x = (ctx->xoff + px) - ctx->ox; - *rx = ctx->sx - *x; + *x = (ctx->xoff + px) - ctx->wox; + *rx = ctx->wsx - *x; } if (*rx > nx) fatalx("%s: x too big, %u > %u", __func__, *rx, nx); - if (yoff >= ctx->oy && yoff + ny <= ctx->oy + ctx->sy) { + if (yoff >= ctx->woy && yoff + ny <= ctx->woy + ctx->wsy) { /* All visible. */ *j = 0; - *y = ctx->yoff + py - ctx->oy; + *y = ctx->yoff + py - ctx->woy; *ry = ny; - } else if (yoff < ctx->oy && yoff + ny > ctx->oy + ctx->sy) { + } else if (yoff < ctx->woy && yoff + ny > ctx->woy + ctx->wsy) { /* Both top and bottom not visible. */ - *j = ctx->oy; + *j = ctx->woy; *y = 0; - *ry = ctx->sy; - } else if (yoff < ctx->oy) { + *ry = ctx->wsy; + } else if (yoff < ctx->woy) { /* Top not visible. */ - *j = ctx->oy - (ctx->yoff + py); + *j = ctx->woy - (ctx->yoff + py); *y = 0; *ry = ny - *j; } else { /* Bottom not visible. */ *j = 0; - *y = (ctx->yoff + py) - ctx->oy; - *ry = ctx->sy - *y; + *y = (ctx->yoff + py) - ctx->woy; + *ry = ctx->wsy - *y; } if (*ry > ny) fatalx("%s: y too big, %u > %u", __func__, *ry, ny); @@ -1495,8 +1495,8 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), break; } - ctx->bigger = tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, - &ctx->sx, &ctx->sy); + ctx->bigger = tty_window_offset(&c->tty, &ctx->wox, &ctx->woy, + &ctx->wsx, &ctx->wsy); ctx->xoff = wp->xoff; ctx->yoff = wp->yoff; @@ -1892,7 +1892,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1)) return; - if (ctx->xoff + ctx->ocx - ctx->ox > tty->sx - 1 && + if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 && ctx->ocy == ctx->orlower && tty_pane_full_width(tty, ctx)) tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); @@ -1912,8 +1912,8 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) return; if (ctx->bigger && - (ctx->xoff + ctx->ocx < ctx->ox || - ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) { + (ctx->xoff + ctx->ocx < ctx->wox || + ctx->xoff + ctx->ocx + ctx->num > ctx->wox + ctx->wsx)) { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || (tty->term->flags & TERM_NOXENL) || @@ -2052,8 +2052,8 @@ static void tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower) { - tty_region(tty, ctx->yoff + rupper - ctx->oy, - ctx->yoff + rlower - ctx->oy); + tty_region(tty, ctx->yoff + rupper - ctx->woy, + ctx->yoff + rlower - ctx->woy); } /* Set region at absolute position. */ @@ -2096,8 +2096,8 @@ tty_margin_off(struct tty *tty) static void tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) { - tty_margin(tty, ctx->xoff - ctx->ox, - ctx->xoff + ctx->wp->sx - 1 - ctx->ox); + tty_margin(tty, ctx->xoff - ctx->wox, + ctx->xoff + ctx->wp->sx - 1 - ctx->wox); } /* Set margin at absolute position. */ @@ -2145,7 +2145,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, static void tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) { - tty_cursor(tty, ctx->xoff + cx - ctx->ox, ctx->yoff + cy - ctx->oy); + tty_cursor(tty, ctx->xoff + cx - ctx->wox, ctx->yoff + cy - ctx->woy); } /* Move cursor to absolute position. */ From 93dca5ab3f001343eadc4b8ff2e0cfb3709bf5c7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 13:19:05 +0100 Subject: [PATCH 0291/1006] Move size to tty_ctx. --- screen-write.c | 3 +++ tmux.h | 2 ++ tty.c | 43 ++++++++++++++++++------------------------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/screen-write.c b/screen-write.c index e3e51020..32424b39 100644 --- a/screen-write.c +++ b/screen-write.c @@ -112,6 +112,9 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, ttyctx->wp = ctx->wp; + ttyctx->sx = screen_size_x(s); + ttyctx->sy = screen_size_y(s); + ttyctx->ocx = s->cx; ttyctx->ocy = s->cy; diff --git a/tmux.h b/tmux.h index e00a4f96..55f51361 100644 --- a/tmux.h +++ b/tmux.h @@ -1310,6 +1310,8 @@ struct tty_ctx { /* Target region (usually pane) offset and size. */ u_int xoff; u_int yoff; + u_int sx; + u_int sy; /* The background colour used for clearing (erasing). */ u_int bg; diff --git a/tty.c b/tty.c index 9f197ff3..ad3e97da 100644 --- a/tty.c +++ b/tty.c @@ -77,7 +77,7 @@ static void tty_default_attributes(struct tty *, struct window_pane *, (tty->term->flags & TERM_DECSLRM) #define tty_pane_full_width(tty, ctx) \ - ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) + ((ctx)->xoff == 0 && (ctx)->sx >= (tty)->sx) #define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */) #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8) @@ -897,9 +897,7 @@ tty_update_client_offset(struct client *c) static int tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - - return (ctx->orlower - ctx->orupper >= screen_size_y(wp->screen) / 2); + return (ctx->orlower - ctx->orupper >= ctx->sy / 2); } /* @@ -933,7 +931,6 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) { struct client *c = tty->client; struct window_pane *wp = ctx->wp; - struct screen *s = wp->screen; u_int i; /* @@ -947,7 +944,7 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) } if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { - for (i = ctx->ocy; i < screen_size_y(s); i++) + for (i = ctx->ocy; i < ctx->sy; i++) tty_draw_pane(tty, ctx, i); } else { for (i = ctx->orupper; i <= ctx->orlower; i++) @@ -1222,7 +1219,7 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; - u_int nx = screen_size_x(s), i, x, rx, ry; + u_int nx = ctx->sx, i, x, rx, ry; log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger); @@ -1621,23 +1618,20 @@ void tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - u_int nx; tty_default_attributes(tty, wp, ctx->bg); - nx = screen_size_x(wp->screen); - tty_clear_pane_line(tty, ctx, ctx->ocy, 0, nx, ctx->bg); + tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg); } void tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - u_int nx; + u_int nx = ctx->sx - ctx->ocx; tty_default_attributes(tty, wp, ctx->bg); - nx = screen_size_x(wp->screen) - ctx->ocx; tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg); } @@ -1801,18 +1795,18 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, wp, ctx->bg); - tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); + tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); px = 0; - nx = screen_size_x(wp->screen); + nx = ctx->sx; py = ctx->ocy + 1; - ny = screen_size_y(wp->screen) - ctx->ocy - 1; + ny = ctx->sy - ctx->ocy - 1; tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); px = ctx->ocx; - nx = screen_size_x(wp->screen) - ctx->ocx; + nx = ctx->sx - ctx->ocx; py = ctx->ocy; tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg); @@ -1826,11 +1820,11 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, wp, ctx->bg); - tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); + tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); px = 0; - nx = screen_size_x(wp->screen); + nx = ctx->sx; py = 0; ny = ctx->ocy; @@ -1851,13 +1845,13 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, wp, ctx->bg); - tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); + tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); px = 0; - nx = screen_size_x(wp->screen); + nx = ctx->sx; py = 0; - ny = screen_size_y(wp->screen); + ny = ctx->sy; tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); } @@ -1866,7 +1860,6 @@ void tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - struct screen *s = wp->screen; u_int i, j; if (ctx->bigger) { @@ -1876,12 +1869,12 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) tty_attributes(tty, &grid_default_cell, wp); - tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); + tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); - for (j = 0; j < screen_size_y(s); j++) { + for (j = 0; j < ctx->sy; j++) { tty_cursor_pane(tty, ctx, 0, j); - for (i = 0; i < screen_size_x(s); i++) + for (i = 0; i < ctx->sx; i++) tty_putc(tty, 'E'); } } From dbebdb2d364feea4df958528ee1dcacbb9933e37 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 13:22:08 +0100 Subject: [PATCH 0292/1006] Rename tty_pane_full_width to tty_full_width. --- tty.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/tty.c b/tty.c index ad3e97da..aaf4d639 100644 --- a/tty.c +++ b/tty.c @@ -75,8 +75,7 @@ static void tty_default_attributes(struct tty *, struct window_pane *, #define tty_use_margin(tty) \ (tty->term->flags & TERM_DECSLRM) - -#define tty_pane_full_width(tty, ctx) \ +#define tty_full_width(tty, ctx) \ ((ctx)->xoff == 0 && (ctx)->sx >= (tty)->sx) #define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */) @@ -1511,7 +1510,7 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; if (ctx->bigger || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || tty_fake_bce(tty, wp, ctx->bg) || (!tty_term_has(tty->term, TTYC_ICH) && !tty_term_has(tty->term, TTYC_ICH1))) { @@ -1532,7 +1531,7 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; if (ctx->bigger || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || tty_fake_bce(tty, wp, ctx->bg) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { @@ -1570,7 +1569,7 @@ void tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1) || @@ -1594,7 +1593,7 @@ void tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1) || @@ -1654,7 +1653,7 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) return; if (ctx->bigger || - (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || (!tty_term_has(tty->term, TTYC_RI) && @@ -1686,7 +1685,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) return; if (ctx->bigger || - (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || wp->sx == 1 || @@ -1725,7 +1724,7 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) u_int i; if (ctx->bigger || - (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || wp->sx == 1 || @@ -1762,7 +1761,7 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) u_int i; if (ctx->bigger || - (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || (!tty_term_has(tty->term, TTYC_RI) && @@ -1887,7 +1886,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 && ctx->ocy == ctx->orlower && - tty_pane_full_width(tty, ctx)) + tty_full_width(tty, ctx)) tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); @@ -1908,7 +1907,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) (ctx->xoff + ctx->ocx < ctx->wox || ctx->xoff + ctx->ocx + ctx->num > ctx->wox + ctx->wsx)) { if (!ctx->wrapped || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || (tty->term->flags & TERM_NOXENL) || ctx->xoff + ctx->ocx != 0 || ctx->yoff + ctx->ocy != tty->cy + 1 || @@ -2123,7 +2122,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) { if (!ctx->wrapped || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || (tty->term->flags & TERM_NOXENL) || ctx->xoff + cx != 0 || ctx->yoff + cy != tty->cy + 1 || From 8110c7a25f257b13f92f34559efedba204e6ea98 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 17:01:36 +0100 Subject: [PATCH 0293/1006] Do not hoke into struct window_pane from the tty code and instead set everything up in tty_ctx. Provide a way to initialize the tty_ctx from a callback and use it to let popups draw directly through input_parse in the same way as panes do, rather than forcing a full redraw on every change. --- cmd-display-panes.c | 8 +- format-draw.c | 8 +- input.c | 37 ++--- menu.c | 8 +- mode-tree.c | 2 +- popup.c | 61 ++++++-- screen-redraw.c | 18 ++- screen-write.c | 166 +++++++++++++++++--- server-fn.c | 2 +- status.c | 6 +- tmux.h | 51 ++++-- tty.c | 370 +++++++++++++++++++------------------------- window-clock.c | 2 +- window-copy.c | 24 +-- window.c | 20 --- 15 files changed, 445 insertions(+), 338 deletions(-) diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 0f12b14b..a13a06ae 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -131,9 +131,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, gc.bg = active_colour; else gc.bg = colour; - gc.flags |= GRID_FLAG_NOPALETTE; - - tty_attributes(tty, &gc, wp); + tty_attributes(tty, &gc, &grid_default_cell, NULL); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; @@ -160,9 +158,7 @@ draw_text: gc.fg = active_colour; else gc.fg = colour; - gc.flags |= GRID_FLAG_NOPALETTE; - - tty_attributes(tty, &gc, wp); + tty_attributes(tty, &gc, &grid_default_cell, NULL); tty_puts(tty, buf); tty_cursor(tty, 0, 0); diff --git a/format-draw.c b/format-draw.c index 4a4fc6bc..3751082e 100644 --- a/format-draw.c +++ b/format-draw.c @@ -242,7 +242,7 @@ format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx, /* If there is no list left, pass off to the no list function. */ if (width_list == 0) { - screen_write_start(&ctx, NULL, left); + screen_write_start(&ctx, left); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_stop(&ctx); @@ -334,7 +334,7 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx, /* If there is no list left, pass off to the no list function. */ if (width_list == 0) { - screen_write_start(&ctx, NULL, centre); + screen_write_start(&ctx, centre); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_stop(&ctx); @@ -431,7 +431,7 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, /* If there is no list left, pass off to the no list function. */ if (width_list == 0) { - screen_write_start(&ctx, NULL, right); + screen_write_start(&ctx, right); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_stop(&ctx); @@ -536,7 +536,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, */ for (i = 0; i < TOTAL; i++) { screen_init(&s[i], size, 1, 0); - screen_write_start(&ctx[i], NULL, &s[i]); + screen_write_start(&ctx[i], &s[i]); screen_write_clearendofline(&ctx[i], current_default.bg); width[i] = 0; } diff --git a/input.c b/input.c index 97dcbdb0..70681e61 100644 --- a/input.c +++ b/input.c @@ -842,9 +842,9 @@ input_reset(struct input_ctx *ictx, int clear) if (clear && wp != NULL) { if (TAILQ_EMPTY(&wp->modes)) - screen_write_start(sctx, wp, &wp->base); + screen_write_start_pane(sctx, wp, &wp->base); else - screen_write_start(sctx, NULL, &wp->base); + screen_write_start(sctx, &wp->base); screen_write_reset(sctx); screen_write_stop(sctx); } @@ -960,9 +960,9 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) /* NULL wp if there is a mode set as don't want to update the tty. */ if (TAILQ_EMPTY(&wp->modes)) - screen_write_start(sctx, wp, &wp->base); + screen_write_start_pane(sctx, wp, &wp->base); else - screen_write_start(sctx, NULL, &wp->base); + screen_write_start(sctx, &wp->base); log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, ictx->state->name, len, (int)len, buf); @@ -973,15 +973,15 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) /* Parse given input for screen. */ void -input_parse_screen(struct input_ctx *ictx, struct screen *s, u_char *buf, - size_t len) +input_parse_screen(struct input_ctx *ictx, struct screen *s, + screen_write_init_ctx_cb cb, void *arg, u_char *buf, size_t len) { struct screen_write_ctx *sctx = &ictx->ctx; if (len == 0) return; - screen_write_start(sctx, NULL, s); + screen_write_start_callback(sctx, s, cb, arg); input_parse(ictx, buf, len); screen_write_stop(sctx); } @@ -1630,7 +1630,6 @@ static void input_csi_dispatch_rm_private(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; - struct window_pane *wp = ictx->wp; struct grid_cell *gc = &ictx->cell.cell; u_int i; @@ -1675,16 +1674,10 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) break; case 47: case 1047: - if (wp != NULL) - window_pane_alternate_off(wp, gc, 0); - else - screen_alternate_off(sctx->s, gc, 0); + screen_write_alternateoff(sctx, gc, 0); break; case 1049: - if (wp != NULL) - window_pane_alternate_off(wp, gc, 1); - else - screen_alternate_off(sctx->s, gc, 1); + screen_write_alternateoff(sctx, gc, 1); break; case 2004: screen_write_mode_clear(sctx, MODE_BRACKETPASTE); @@ -1780,16 +1773,10 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) break; case 47: case 1047: - if (wp != NULL) - window_pane_alternate_on(wp, gc, 0); - else - screen_alternate_on(sctx->s, gc, 0); + screen_write_alternateon(sctx, gc, 0); break; case 1049: - if (wp != NULL) - window_pane_alternate_on(wp, gc, 1); - else - screen_alternate_on(sctx->s, gc, 1); + screen_write_alternateon(sctx, gc, 1); break; case 2004: screen_write_mode_set(sctx, MODE_BRACKETPASTE); @@ -2593,7 +2580,7 @@ input_osc_52(struct input_ctx *ictx, const char *p) return; } - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_setselection(&ctx, out, outlen); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); diff --git a/menu.c b/menu.c index e78999c6..27836694 100644 --- a/menu.c +++ b/menu.c @@ -153,13 +153,15 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) style_apply(&gc, c->session->curw->window->options, "mode-style", NULL); - screen_write_start(&ctx, NULL, s); + screen_write_start(&ctx, s); screen_write_clearscreen(&ctx, 8); screen_write_menu(&ctx, menu, md->choice, &gc); screen_write_stop(&ctx); - for (i = 0; i < screen_size_y(&md->s); i++) - tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i); + for (i = 0; i < screen_size_y(&md->s); i++) { + tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, + &grid_default_cell, NULL); + } } static void diff --git a/mode-tree.c b/mode-tree.c index 645e2ae9..8d210d72 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -562,7 +562,7 @@ mode_tree_draw(struct mode_tree_data *mtd) w = mtd->width; h = mtd->height; - screen_write_start(&ctx, NULL, s); + screen_write_start(&ctx, s); screen_write_clearscreen(&ctx, 8); if (mtd->line_size > 10) diff --git a/popup.c b/popup.c index 160b5aa3..c13a38cf 100644 --- a/popup.c +++ b/popup.c @@ -57,6 +57,44 @@ struct popup_data { u_int lb; }; +static void +popup_redraw_cb(const struct tty_ctx *ttyctx) +{ + struct popup_data *pd = ttyctx->arg; + + pd->c->flags |= CLIENT_REDRAWOVERLAY; +} + +static int +popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) +{ + struct popup_data *pd = ttyctx->arg; + + if (pd->c->flags & CLIENT_REDRAWOVERLAY) + return (-1); + + ttyctx->bigger = 0; + ttyctx->wox = 0; + ttyctx->woy = 0; + ttyctx->wsx = c->tty.sx; + ttyctx->wsy = c->tty.sy; + + ttyctx->xoff = pd->px + 1; + ttyctx->yoff = pd->py + 1; + + return (1); +} + +static void +popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) +{ + struct popup_data *pd = ctx->arg; + + ttyctx->redraw_cb = popup_redraw_cb; + ttyctx->set_client_cb = popup_set_client_cb; + ttyctx->arg = pd; +} + static void popup_write_screen(struct client *c, struct popup_data *pd) { @@ -72,7 +110,7 @@ popup_write_screen(struct client *c, struct popup_data *pd) else format_defaults(ft, c, NULL, NULL, NULL); - screen_write_start(&ctx, NULL, &pd->s); + screen_write_start(&ctx, &pd->s); screen_write_clearscreen(&ctx, 8); y = 0; @@ -132,7 +170,7 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) u_int i, px = pd->px, py = pd->py; screen_init(&s, pd->sx, pd->sy, 0); - screen_write_start(&ctx, NULL, &s); + screen_write_start(&ctx, &s); screen_write_clearscreen(&ctx, 8); screen_write_box(&ctx, pd->sx, pd->sy); screen_write_cursormove(&ctx, 1, 1, 0); @@ -140,8 +178,10 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) screen_write_stop(&ctx); c->overlay_check = NULL; - for (i = 0; i < pd->sy; i++) - tty_draw_line(tty, NULL, &s, 0, i, pd->sx, px, py + i); + for (i = 0; i < pd->sy; i++){ + tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, + &grid_default_cell, NULL); + } c->overlay_check = popup_check_cb; } @@ -331,11 +371,14 @@ popup_job_update_cb(struct job *job) void *data = EVBUFFER_DATA(evb); size_t size = EVBUFFER_LENGTH(evb); - if (size != 0) { - input_parse_screen(pd->ictx, s, data, size); - evbuffer_drain(evb, size); - pd->c->flags |= CLIENT_REDRAWOVERLAY; - } + if (size == 0) + return; + + pd->c->tty.flags &= ~TTY_FREEZE; + input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size); + pd->c->tty.flags |= TTY_FREEZE; + + evbuffer_drain(evb, size); } static void diff --git a/screen-redraw.c b/screen-redraw.c index cf6b7e69..dd5886b2 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -317,7 +317,7 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, screen_init(&wp->status_screen, width, 1, 0); wp->status_screen.mode = 0; - screen_write_start(&ctx, NULL, &wp->status_screen); + screen_write_start(&ctx, &wp->status_screen); gc.attr |= GRID_ATTR_CHARSET; for (i = 0; i < width; i++) @@ -394,7 +394,8 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) if (ctx->statustop) yoff += ctx->statuslines; - tty_draw_line(tty, NULL, s, i, 0, width, x, yoff - ctx->oy); + tty_draw_line(tty, s, i, 0, width, x, yoff - ctx->oy, + &grid_default_cell, NULL); } tty_cursor(tty, 0, 0); } @@ -586,7 +587,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) } } - tty_attributes(tty, gc, NULL); + tty_attributes(tty, gc, &grid_default_cell, NULL); if (ctx->statustop) tty_cursor(tty, i, ctx->statuslines + j); else @@ -647,8 +648,10 @@ screen_redraw_draw_status(struct screen_redraw_ctx *ctx) y = 0; else y = c->tty.sy - ctx->statuslines; - for (i = 0; i < ctx->statuslines; i++) - tty_draw_line(tty, NULL, s, 0, i, UINT_MAX, 0, y + i); + for (i = 0; i < ctx->statuslines; i++) { + tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i, + &grid_default_cell, NULL); + } } /* Draw one pane. */ @@ -659,6 +662,7 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) struct window *w = c->session->curw->window; struct tty *tty = &c->tty; struct screen *s; + struct grid_cell defaults; u_int i, j, top, x, y, width; log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id); @@ -702,6 +706,8 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u", __func__, c->name, wp->id, i, j, x, y, width); - tty_draw_line(tty, wp, s, i, j, width, x, y); + tty_default_colours(&defaults, wp); + tty_draw_line(tty, s, i, j, width, x, y, &defaults, + wp->palette); } } diff --git a/screen-write.c b/screen-write.c index 32424b39..c7996ec9 100644 --- a/screen-write.c +++ b/screen-write.c @@ -27,8 +27,8 @@ static void screen_write_collect_clear(struct screen_write_ctx *, u_int, u_int); static int screen_write_collect_clear_end(struct screen_write_ctx *, u_int, u_int, u_int); -static int screen_write_collect_clear_start(struct screen_write_ctx *, u_int, - u_int, u_int); +static int screen_write_collect_clear_start(struct screen_write_ctx *, + u_int, u_int, u_int); static void screen_write_collect_scroll(struct screen_write_ctx *); static void screen_write_collect_flush(struct screen_write_ctx *, int, const char *); @@ -101,6 +101,49 @@ screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy) evtimer_add(&w->offset_timer, &tv); } +/* Do a full redraw. */ +static void +screen_write_redraw_cb(const struct tty_ctx *ttyctx) +{ + struct window_pane *wp = ttyctx->arg; + + wp->flags |= PANE_REDRAW; +} + +/* Update context for client. */ +static int +screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c) +{ + struct window_pane *wp = ttyctx->arg; + + if (c->session->curw->window != wp->window) + return (0); + if (wp->layout_cell == NULL) + return (0); + + if (wp->flags & (PANE_REDRAW|PANE_DROP)) + return (-1); + if (c->flags & CLIENT_REDRAWPANES) { + /* + * Redraw is already deferred to redraw another pane - redraw + * this one also when that happens. + */ + log_debug("adding %%%u to deferred redraw", wp->id); + wp->flags |= PANE_REDRAW; + return (-1); + } + + ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, + &ttyctx->wsx, &ttyctx->wsy); + + ttyctx->xoff = wp->xoff; + ttyctx->yoff = wp->yoff; + if (status_at_line(c) == 0) + ttyctx->yoff += status_line_size(c); + + return (1); +} + /* Set up context for TTY command. */ static void screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, @@ -110,17 +153,35 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, memset(ttyctx, 0, sizeof *ttyctx); - ttyctx->wp = ctx->wp; + if (ctx->wp != NULL) { + tty_default_colours(&ttyctx->defaults, ctx->wp); + ttyctx->palette = ctx->wp->palette; + } else { + memcpy(&ttyctx->defaults, &grid_default_cell, + sizeof ttyctx->defaults); + ttyctx->palette = NULL; + } + ttyctx->s = s; ttyctx->sx = screen_size_x(s); ttyctx->sy = screen_size_y(s); ttyctx->ocx = s->cx; ttyctx->ocy = s->cy; - ttyctx->orlower = s->rlower; ttyctx->orupper = s->rupper; + if (ctx->init_ctx_cb != NULL) + ctx->init_ctx_cb(ctx, ttyctx); + else { + ttyctx->redraw_cb = screen_write_redraw_cb; + if (ctx->wp == NULL) + ttyctx->set_client_cb = NULL; + else + ttyctx->set_client_cb = screen_write_set_client_cb; + ttyctx->arg = ctx->wp; + } + if (ctx->wp != NULL && !ctx->sync && (sync || ctx->wp != ctx->wp->window->active)) { @@ -151,18 +212,13 @@ screen_write_free_list(struct screen *s) free(s->write_list); } -/* Initialize writing with a window. */ -void -screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, - struct screen *s) +/* Set up for writing. */ +static void +screen_write_init(struct screen_write_ctx *ctx, struct screen *s) { memset(ctx, 0, sizeof *ctx); - ctx->wp = wp; - if (wp != NULL && s == NULL) - ctx->s = wp->screen; - else - ctx->s = s; + ctx->s = s; if (ctx->s->write_list == NULL) screen_write_make_list(ctx->s); @@ -170,17 +226,51 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, ctx->scrolled = 0; ctx->bg = 8; +} + +/* Initialize writing with a pane. */ +void +screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp, + struct screen *s) +{ + if (s == NULL) + s = wp->screen; + screen_write_init(ctx, s); + ctx->wp = wp; if (log_get_level() != 0) { - if (wp != NULL) { - log_debug("%s: size %ux%u, pane %%%u (at %u,%u)", - __func__, screen_size_x(ctx->s), - screen_size_y(ctx->s), wp->id, wp->xoff, wp->yoff); - } else { - log_debug("%s: size %ux%u, no pane", - __func__, screen_size_x(ctx->s), - screen_size_y(ctx->s)); - } + log_debug("%s: size %ux%u, pane %%%u (at %u,%u)", + __func__, screen_size_x(ctx->s), screen_size_y(ctx->s), + wp->id, wp->xoff, wp->yoff); + } +} + +/* Initialize writing with a callback. */ +void +screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s, + screen_write_init_ctx_cb cb, void *arg) +{ + screen_write_init(ctx, s); + + ctx->init_ctx_cb = cb; + ctx->arg = arg; + + if (log_get_level() != 0) { + log_debug("%s: size %ux%u, with callback", __func__, + screen_size_x(ctx->s), screen_size_y(ctx->s)); + } +} + + +/* Initialize writing. */ +void +screen_write_start(struct screen_write_ctx *ctx, struct screen *s) +{ + screen_write_init(ctx, s); + + if (log_get_level() != 0) { + log_debug("%s: size %ux%u, no pane", __func__, + screen_size_x(ctx->s), screen_size_y(ctx->s)); } } @@ -1799,3 +1889,35 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) tty_write(tty_cmd_rawstring, &ttyctx); } + +/* Turn alternate screen on. */ +void +screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, + int cursor) +{ + struct tty_ctx ttyctx; + struct window_pane *wp = ctx->wp; + + if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) + return; + screen_alternate_on(ctx->s, gc, cursor); + + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.redraw_cb(&ttyctx); +} + +/* Turn alternate screen off. */ +void +screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc, + int cursor) +{ + struct tty_ctx ttyctx; + struct window_pane *wp = ctx->wp; + + if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) + return; + screen_alternate_off(ctx->s, gc, cursor); + + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.redraw_cb(&ttyctx); +} diff --git a/server-fn.c b/server-fn.c index 815d25e2..127afb6b 100644 --- a/server-fn.c +++ b/server-fn.c @@ -319,7 +319,7 @@ server_destroy_pane(struct window_pane *wp, int notify) if (notify) notify_pane("pane-died", wp); - screen_write_start(&ctx, wp, &wp->base); + screen_write_start_pane(&ctx, wp, &wp->base); screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1); screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1, 0); screen_write_linefeed(&ctx, 1, 8); diff --git a/status.c b/status.c index a4844294..b5442550 100644 --- a/status.c +++ b/status.c @@ -372,7 +372,7 @@ status_redraw(struct client *c) screen_resize(&sl->screen, width, lines, 0); changed = force = 1; } - screen_write_start(&ctx, NULL, &sl->screen); + screen_write_start(&ctx, &sl->screen); /* Write the status lines. */ o = options_get(s->options, "status-format"); @@ -509,7 +509,7 @@ status_message_redraw(struct client *c) style_apply(&gc, s->options, "message-style", ft); format_free(ft); - screen_write_start(&ctx, NULL, sl->active); + screen_write_start(&ctx, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); screen_write_cursormove(&ctx, 0, lines - 1, 0); for (offset = 0; offset < c->tty.sx; offset++) @@ -664,7 +664,7 @@ status_prompt_redraw(struct client *c) if (start > c->tty.sx) start = c->tty.sx; - screen_write_start(&ctx, NULL, sl->active); + screen_write_start(&ctx, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); screen_write_cursormove(&ctx, 0, lines - 1, 0); for (offset = 0; offset < c->tty.sx; offset++) diff --git a/tmux.h b/tmux.h index 55f51361..726fdb26 100644 --- a/tmux.h +++ b/tmux.h @@ -57,7 +57,11 @@ struct mouse_event; struct options; struct options_array_item; struct options_entry; +struct screen_write_collect_item; +struct screen_write_collect_line; +struct screen_write_ctx; struct session; +struct tty_ctx; struct tmuxpeer; struct tmuxproc; struct winlink; @@ -788,13 +792,16 @@ struct screen { }; /* Screen write context. */ -struct screen_write_collect_item; -struct screen_write_collect_line; +typedef void (*screen_write_init_ctx_cb)(struct screen_write_ctx *, + struct tty_ctx *); struct screen_write_ctx { struct window_pane *wp; struct screen *s; int sync; + screen_write_init_ctx_cb init_ctx_cb; + void *arg; + struct screen_write_collect_item *item; u_int scrolled; u_int bg; @@ -1254,8 +1261,6 @@ struct tty { struct termios tio; struct grid_cell cell; - - int last_wp; struct grid_cell last_cell; #define TTY_NOCURSOR 0x1 @@ -1287,8 +1292,14 @@ struct tty { }; /* TTY command context. */ +typedef void (*tty_ctx_redraw_cb)(const struct tty_ctx *); +typedef int (*tty_ctx_set_client_cb)(struct tty_ctx *, struct client *); struct tty_ctx { - struct window_pane *wp; + struct screen *s; + + tty_ctx_redraw_cb redraw_cb; + tty_ctx_set_client_cb set_client_cb; + void *arg; const struct grid_cell *cell; int wrapped; @@ -1316,6 +1327,10 @@ struct tty_ctx { /* The background colour used for clearing (erasing). */ u_int bg; + /* The default colours and palette. */ + struct grid_cell defaults; + int *palette; + /* Containing region (usually window) offset and size. */ int bigger; u_int wox; @@ -1971,7 +1986,7 @@ void tty_update_window_offset(struct window *); void tty_update_client_offset(struct client *); void tty_raw(struct tty *, const char *); void tty_attributes(struct tty *, const struct grid_cell *, - struct window_pane *); + const struct grid_cell *, int *); void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); @@ -1994,8 +2009,8 @@ void tty_send_requests(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); -void tty_draw_line(struct tty *, struct window_pane *, struct screen *, - u_int, u_int, u_int, u_int, u_int); +void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int, + u_int, u_int, const struct grid_cell *, int *); void tty_sync_start(struct tty *); void tty_sync_end(struct tty *); int tty_open(struct tty *, char **); @@ -2026,6 +2041,7 @@ void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); +void tty_default_colours(struct grid_cell *, struct window_pane *); /* tty-term.c */ extern struct tty_terms tty_terms; @@ -2344,8 +2360,8 @@ void input_reset(struct input_ctx *, int); struct evbuffer *input_pending(struct input_ctx *); void input_parse_pane(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); -void input_parse_screen(struct input_ctx *, struct screen *, u_char *, - size_t); +void input_parse_screen(struct input_ctx *, struct screen *, + screen_write_init_ctx_cb, void *, u_char *, size_t); /* input-key.c */ int input_key_pane(struct window_pane *, key_code, struct mouse_event *); @@ -2428,8 +2444,11 @@ char *grid_view_string_cells(struct grid *, u_int, u_int, u_int); /* screen-write.c */ void screen_write_make_list(struct screen *); void screen_write_free_list(struct screen *); -void screen_write_start(struct screen_write_ctx *, struct window_pane *, - struct screen *); +void screen_write_start_pane(struct screen_write_ctx *, + struct window_pane *, struct screen *); +void screen_write_start(struct screen_write_ctx *, struct screen *); +void screen_write_start_callback(struct screen_write_ctx *, struct screen *, + screen_write_init_ctx_cb, void *); void screen_write_stop(struct screen_write_ctx *); void screen_write_reset(struct screen_write_ctx *); size_t printflike(1, 2) screen_write_strlen(const char *, ...); @@ -2483,6 +2502,10 @@ void screen_write_collect_add(struct screen_write_ctx *, void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *); void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); +void screen_write_alternateon(struct screen_write_ctx *, + struct grid_cell *, int); +void screen_write_alternateoff(struct screen_write_ctx *, + struct grid_cell *, int); /* screen-redraw.c */ void screen_redraw_screen(struct client *); @@ -2571,10 +2594,6 @@ struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); int window_pane_destroy_ready(struct window_pane *); void window_pane_resize(struct window_pane *, u_int, u_int); -void window_pane_alternate_on(struct window_pane *, - struct grid_cell *, int); -void window_pane_alternate_off(struct window_pane *, - struct grid_cell *, int); void window_pane_set_palette(struct window_pane *, u_int, int); void window_pane_unset_palette(struct window_pane *, u_int); void window_pane_reset_palette(struct window_pane *); diff --git a/tty.c b/tty.c index aaf4d639..25a0cd02 100644 --- a/tty.c +++ b/tty.c @@ -34,7 +34,7 @@ static int tty_log_fd = -1; -static int tty_client_ready(struct client *, struct window_pane *); +static int tty_client_ready(struct client *); static void tty_set_italics(struct tty *); static int tty_try_colour(struct tty *, int, const char *); @@ -45,12 +45,9 @@ static void tty_cursor_pane_unless_wrap(struct tty *, const struct tty_ctx *, u_int, u_int); static void tty_invalidate(struct tty *); static void tty_colours(struct tty *, const struct grid_cell *); -static void tty_check_fg(struct tty *, struct window_pane *, - struct grid_cell *); -static void tty_check_bg(struct tty *, struct window_pane *, - struct grid_cell *); -static void tty_check_us(struct tty *, struct window_pane *, - struct grid_cell *); +static void tty_check_fg(struct tty *, int *, struct grid_cell *); +static void tty_check_bg(struct tty *, int *, struct grid_cell *); +static void tty_check_us(struct tty *, int *, struct grid_cell *); static void tty_colours_fg(struct tty *, const struct grid_cell *); static void tty_colours_bg(struct tty *, const struct grid_cell *); static void tty_colours_us(struct tty *, const struct grid_cell *); @@ -61,17 +58,17 @@ static void tty_region(struct tty *, u_int, u_int); static void tty_margin_pane(struct tty *, const struct tty_ctx *); static void tty_margin(struct tty *, u_int, u_int); static int tty_large_region(struct tty *, const struct tty_ctx *); -static int tty_fake_bce(const struct tty *, struct window_pane *, u_int); +static int tty_fake_bce(const struct tty *, const struct grid_cell *, + u_int); static void tty_redraw_region(struct tty *, const struct tty_ctx *); static void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, u_int); static void tty_repeat_space(struct tty *, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); static void tty_cell(struct tty *, const struct grid_cell *, - struct window_pane *); -static void tty_default_colours(struct grid_cell *, struct window_pane *); -static void tty_default_attributes(struct tty *, struct window_pane *, - u_int); + const struct grid_cell *, int *); +static void tty_default_attributes(struct tty *, const struct grid_cell *, + int *, u_int); #define tty_use_margin(tty) \ (tty->term->flags & TERM_DECSLRM) @@ -888,6 +885,27 @@ tty_update_client_offset(struct client *c) c->flags |= (CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS); } +/* Get a palette entry. */ +static int +tty_get_palette(int *palette, int c) +{ + int new; + + if (palette == NULL) + return (-1); + + new = -1; + if (c < 8) + new = palette[c]; + else if (c >= 90 && c <= 97) + new = palette[8 + c - 90]; + else if (c & COLOUR_FLAG_256) + new = palette[c & ~COLOUR_FLAG_256]; + if (new == 0) + return (-1); + return (new); +} + /* * Is the region large enough to be worth redrawing once later rather than * probably several times now? Currently yes if it is more than 50% of the @@ -904,18 +922,11 @@ tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) * emulated. */ static int -tty_fake_bce(const struct tty *tty, struct window_pane *wp, u_int bg) +tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg) { - struct grid_cell gc; - if (tty_term_flag(tty->term, TTYC_BCE)) return (0); - - memcpy(&gc, &grid_default_cell, sizeof gc); - if (wp != NULL) - tty_default_colours(&gc, wp); - - if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc.bg)) + if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc->bg)) return (1); return (0); } @@ -929,16 +940,15 @@ static void tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) { struct client *c = tty->client; - struct window_pane *wp = ctx->wp; u_int i; /* - * If region is large, schedule a window redraw. In most cases this is - * likely to be followed by some more scrolling. + * If region is large, schedule a redraw. In most cases this is likely + * to be followed by some more scrolling. */ if (tty_large_region(tty, ctx)) { - log_debug("%s: %s, large redraw of %%%u", __func__, c->name, wp->id); - wp->flags |= PANE_REDRAW; + log_debug("%s: %s large redraw", __func__, c->name); + ctx->redraw_cb(ctx); return; } @@ -977,8 +987,7 @@ static int tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry) { - struct window_pane *wp = ctx->wp; - u_int xoff = wp->xoff + px; + u_int xoff = ctx->xoff + px; if (!tty_is_visible(tty, ctx, px, py, nx, 1)) return (0); @@ -1013,8 +1022,8 @@ tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, /* Clear a line. */ static void -tty_clear_line(struct tty *tty, struct window_pane *wp, u_int py, u_int px, - u_int nx, u_int bg) +tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, + u_int px, u_int nx, u_int bg) { struct client *c = tty->client; @@ -1025,7 +1034,7 @@ tty_clear_line(struct tty *tty, struct window_pane *wp, u_int py, u_int px, return; /* If genuine BCE is available, can try escape sequences. */ - if (!tty_fake_bce(tty, wp, bg)) { + if (!tty_fake_bce(tty, defaults, bg)) { /* Off the end of the line, use EL if available. */ if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) { tty_cursor(tty, px, py); @@ -1064,7 +1073,7 @@ tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py, log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry)) - tty_clear_line(tty, ctx->wp, ry, x, rx, bg); + tty_clear_line(tty, &ctx->defaults, ry, x, rx, bg); } /* Clamp area position to visible part of pane. */ @@ -1073,8 +1082,7 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, u_int nx, u_int ny, u_int *i, u_int *j, u_int *x, u_int *y, u_int *rx, u_int *ry) { - struct window_pane *wp = ctx->wp; - u_int xoff = wp->xoff + px, yoff = wp->yoff + py; + u_int xoff = ctx->xoff + px, yoff = ctx->yoff + py; if (!tty_is_visible(tty, ctx, px, py, nx, ny)) return (0); @@ -1132,8 +1140,8 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, /* Clear an area, adjusting to visible part of pane. */ static void -tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, - u_int px, u_int nx, u_int bg) +tty_clear_area(struct tty *tty, const struct grid_cell *defaults, u_int py, + u_int ny, u_int px, u_int nx, u_int bg) { struct client *c = tty->client; u_int yy; @@ -1146,7 +1154,7 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, return; /* If genuine BCE is available, can try escape sequences. */ - if (!tty_fake_bce(tty, wp, bg)) { + if (!tty_fake_bce(tty, defaults, bg)) { /* Use ED if clearing off the bottom of the terminal. */ if (px == 0 && px + nx >= tty->sx && @@ -1199,7 +1207,7 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, /* Couldn't use an escape sequence, loop over the lines. */ for (yy = py; yy < py + ny; yy++) - tty_clear_line(tty, wp, yy, px, nx, bg); + tty_clear_line(tty, defaults, yy, px, nx, bg); } /* Clear an area in a pane. */ @@ -1210,24 +1218,26 @@ tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py, u_int i, j, x, y, rx, ry; if (tty_clamp_area(tty, ctx, px, py, nx, ny, &i, &j, &x, &y, &rx, &ry)) - tty_clear_area(tty, ctx->wp, y, ry, x, rx, bg); + tty_clear_area(tty, &ctx->defaults, y, ry, x, rx, bg); } static void tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) { - struct window_pane *wp = ctx->wp; - struct screen *s = wp->screen; - u_int nx = ctx->sx, i, x, rx, ry; + struct screen *s = ctx->s; + u_int nx = ctx->sx, i, x, rx, ry; log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger); if (!ctx->bigger) { - tty_draw_line(tty, wp, s, 0, py, nx, ctx->xoff, ctx->yoff + py); + tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py, + &ctx->defaults, ctx->palette); return; } - if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) - tty_draw_line(tty, wp, s, i, py, rx, x, ry); + if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) { + tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->defaults, + ctx->palette); + } } static const struct grid_cell * @@ -1265,8 +1275,8 @@ tty_check_overlay(struct tty *tty, u_int px, u_int py) } void -tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, - u_int px, u_int py, u_int nx, u_int atx, u_int aty) +tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, + u_int atx, u_int aty, const struct grid_cell *defaults, int *palette) { struct grid *gd = s->grid; struct grid_cell gc, last; @@ -1314,8 +1324,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, gl = NULL; else gl = grid_get_line(gd, gd->hsize + py - 1); - if (wp == NULL || - gl == NULL || + if (gl == NULL || (~gl->flags & GRID_LINE_WRAPPED) || atx != 0 || tty->cx < tty->sx || @@ -1324,8 +1333,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, atx == 0 && px + sx != nx && tty_term_has(tty->term, TTYC_EL1) && - !tty_fake_bce(tty, wp, 8)) { - tty_default_attributes(tty, wp, 8); + !tty_fake_bce(tty, defaults, 8)) { + tty_default_attributes(tty, defaults, palette, 8); tty_cursor(tty, nx - 1, aty); tty_putcode(tty, TTYC_EL1); cleared = 1; @@ -1352,11 +1361,11 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, gcp->us != last.us || ux + width + gcp->data.width > nx || (sizeof buf) - len < gcp->data.size)) { - tty_attributes(tty, &last, wp); + tty_attributes(tty, &last, defaults, palette); if (last.flags & GRID_FLAG_CLEARED) { log_debug("%s: %zu cleared", __func__, len); - tty_clear_line(tty, wp, aty, atx + ux, width, - last.bg); + tty_clear_line(tty, defaults, aty, atx + ux, + width, last.bg); } else { if (!wrapped || atx != 0 || ux != 0) tty_cursor(tty, atx + ux, aty); @@ -1376,7 +1385,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, if (!tty_check_overlay(tty, atx + ux, aty)) ux += gcp->data.width; else if (ux + gcp->data.width > nx) { - tty_attributes(tty, &last, wp); + tty_attributes(tty, &last, defaults, palette); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.width; j++) { if (ux + j > nx) @@ -1385,7 +1394,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, ux++; } } else if (gcp->attr & GRID_ATTR_CHARSET) { - tty_attributes(tty, &last, wp); + tty_attributes(tty, &last, defaults, palette); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.size; j++) tty_putc(tty, gcp->data.data[j]); @@ -1397,10 +1406,11 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, } } if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) { - tty_attributes(tty, &last, wp); + tty_attributes(tty, &last, defaults, palette); if (last.flags & GRID_FLAG_CLEARED) { log_debug("%s: %zu cleared (end)", __func__, len); - tty_clear_line(tty, wp, aty, atx + ux, width, last.bg); + tty_clear_line(tty, defaults, aty, atx + ux, width, + last.bg); } else { if (!wrapped || atx != 0 || ux != 0) tty_cursor(tty, atx + ux, aty); @@ -1412,8 +1422,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, if (!cleared && ux < nx) { log_debug("%s: %u to end of line (%zu cleared)", __func__, nx - ux, len); - tty_default_attributes(tty, wp, 8); - tty_clear_line(tty, wp, aty, atx + ux, nx - ux, 8); + tty_default_attributes(tty, defaults, palette, 8); + tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8); } tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; @@ -1451,7 +1461,7 @@ tty_sync_end(struct tty *tty) } static int -tty_client_ready(struct client *c, struct window_pane *wp) +tty_client_ready(struct client *c) { if (c->session == NULL || c->tty.term == NULL) return (0); @@ -1459,10 +1469,6 @@ tty_client_ready(struct client *c, struct window_pane *wp) return (0); if (c->tty.flags & TTY_FREEZE) return (0); - if (c->session->curw->window != wp->window) - return (0); - if (wp->layout_cell == NULL) - return (0); return (1); } @@ -1470,36 +1476,19 @@ void tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - struct client *c; + struct client *c; + int state; - if (wp == NULL) + if (ctx->set_client_cb == NULL) return; - if (wp->flags & (PANE_REDRAW|PANE_DROP)) - return; - TAILQ_FOREACH(c, &clients, entry) { - if (!tty_client_ready(c, wp)) + if (!tty_client_ready(c)) continue; - if (c->flags & CLIENT_REDRAWPANES) { - /* - * Redraw is already deferred to redraw another pane - - * redraw this one also when that happens. - */ - log_debug("adding %%%u to deferred redraw", wp->id); - wp->flags |= PANE_REDRAW; + state = ctx->set_client_cb(ctx, c); + if (state == -1) break; - } - - ctx->bigger = tty_window_offset(&c->tty, &ctx->wox, &ctx->woy, - &ctx->wsx, &ctx->wsy); - - ctx->xoff = wp->xoff; - ctx->yoff = wp->yoff; - - if (status_at_line(c) == 0) - ctx->yoff += status_line_size(c); - + if (state == 0) + continue; cmdfn(&c->tty, ctx); } } @@ -1507,18 +1496,16 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), void tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (ctx->bigger || !tty_full_width(tty, ctx) || - tty_fake_bce(tty, wp, ctx->bg) || + tty_fake_bce(tty, &ctx->defaults, ctx->bg) || (!tty_term_has(tty->term, TTYC_ICH) && !tty_term_has(tty->term, TTYC_ICH1))) { tty_draw_pane(tty, ctx, ctx->ocy); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -1528,18 +1515,16 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (ctx->bigger || !tty_full_width(tty, ctx) || - tty_fake_bce(tty, wp, ctx->bg) || + tty_fake_bce(tty, &ctx->defaults, ctx->bg) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { tty_draw_pane(tty, ctx, ctx->ocy); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -1554,12 +1539,12 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, ctx->wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_term_has(tty->term, TTYC_ECH) && - !tty_fake_bce(tty, ctx->wp, 8)) + !tty_fake_bce(tty, &ctx->defaults, 8)) tty_putcode1(tty, TTYC_ECH, ctx->num); else tty_repeat_space(tty, ctx->num); @@ -1570,16 +1555,16 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger || !tty_full_width(tty, ctx) || - tty_fake_bce(tty, ctx->wp, ctx->bg) || + tty_fake_bce(tty, &ctx->defaults, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1) || - ctx->wp->sx == 1 || - ctx->wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, ctx->wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); @@ -1594,16 +1579,16 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger || !tty_full_width(tty, ctx) || - tty_fake_bce(tty, ctx->wp, ctx->bg) || + tty_fake_bce(tty, &ctx->defaults, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1) || - ctx->wp->sx == 1 || - ctx->wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, ctx->wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); @@ -1616,9 +1601,7 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg); } @@ -1626,10 +1609,9 @@ tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int nx = ctx->sx - ctx->ocx; + u_int nx = ctx->sx - ctx->ocx; - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg); } @@ -1637,9 +1619,7 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg); } @@ -1647,24 +1627,22 @@ tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (ctx->ocy != ctx->orupper) return; if (ctx->bigger || (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || - tty_fake_bce(tty, wp, 8) || + tty_fake_bce(tty, &ctx->defaults, 8) || !tty_term_has(tty->term, TTYC_CSR) || (!tty_term_has(tty->term, TTYC_RI) && !tty_term_has(tty->term, TTYC_RIN)) || - ctx->wp->sx == 1 || - ctx->wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1679,22 +1657,20 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (ctx->ocy != ctx->orlower) return; if (ctx->bigger || (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || - tty_fake_bce(tty, wp, 8) || + tty_fake_bce(tty, &ctx->defaults, 8) || !tty_term_has(tty->term, TTYC_CSR) || - wp->sx == 1 || - wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1720,20 +1696,19 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int i; + u_int i; if (ctx->bigger || (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || - tty_fake_bce(tty, wp, 8) || + tty_fake_bce(tty, &ctx->defaults, 8) || !tty_term_has(tty->term, TTYC_CSR) || - wp->sx == 1 || - wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1757,22 +1732,21 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int i; + u_int i; if (ctx->bigger || (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || - tty_fake_bce(tty, wp, 8) || + tty_fake_bce(tty, &ctx->defaults, 8) || !tty_term_has(tty->term, TTYC_CSR) || (!tty_term_has(tty->term, TTYC_RI) && !tty_term_has(tty->term, TTYC_RIN)) || - wp->sx == 1 || - wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1789,10 +1763,9 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int px, py, nx, ny; + u_int px, py, nx, ny; - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1814,10 +1787,9 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int px, py, nx, ny; + u_int px, py, nx, ny; - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1839,10 +1811,9 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int px, py, nx, ny; + u_int px, py, nx, ny; - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1858,15 +1829,14 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int i, j; + u_int i, j; if (ctx->bigger) { - wp->flags |= PANE_REDRAW; + ctx->redraw_cb(ctx); return; } - tty_attributes(tty, &grid_default_cell, wp); + tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1892,14 +1862,12 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); - tty_cell(tty, ctx->cell, ctx->wp); + tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette); } void tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1)) return; @@ -1915,14 +1883,14 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) tty->cy == tty->rlower) tty_draw_pane(tty, ctx, ctx->ocy); else - wp->flags |= PANE_REDRAW; + ctx->redraw_cb(ctx); return; } tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); - tty_attributes(tty, ctx->cell, ctx->wp); + tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette); tty_putn(tty, ctx->ptr, ctx->num, ctx->num); } @@ -1958,7 +1926,8 @@ tty_cmd_syncstart(struct tty *tty, __unused const struct tty_ctx *ctx) } static void -tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) +tty_cell(struct tty *tty, const struct grid_cell *gc, + const struct grid_cell *defaults, int *palette) { const struct grid_cell *gcp; @@ -1973,7 +1942,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) return; /* Set the attributes. */ - tty_attributes(tty, gc, wp); + tty_attributes(tty, gc, defaults, palette); /* Get the cell and if ASCII write with putc to do ACS translation. */ gcp = tty_check_codeset(tty, gc); @@ -1999,21 +1968,16 @@ tty_reset(struct tty *tty) tty_putcode(tty, TTYC_SGR0); memcpy(gc, &grid_default_cell, sizeof *gc); } - memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); - tty->last_wp = -1; } static void tty_invalidate(struct tty *tty) { memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); - memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); - tty->last_wp = -1; tty->cx = tty->cy = UINT_MAX; - tty->rupper = tty->rleft = UINT_MAX; tty->rlower = tty->rright = UINT_MAX; @@ -2089,7 +2053,7 @@ static void tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) { tty_margin(tty, ctx->xoff - ctx->wox, - ctx->xoff + ctx->wp->sx - 1 - ctx->wox); + ctx->xoff + ctx->sx - 1 - ctx->wox); } /* Set margin at absolute position. */ @@ -2282,27 +2246,24 @@ out: void tty_attributes(struct tty *tty, const struct grid_cell *gc, - struct window_pane *wp) + const struct grid_cell *defaults, int *palette) { struct grid_cell *tc = &tty->cell, gc2; int changed; - /* Ignore cell if it is the same as the last one. */ - if (wp != NULL && - (int)wp->id == tty->last_wp && - ~(wp->flags & PANE_STYLECHANGED) && - gc->attr == tty->last_cell.attr && - gc->fg == tty->last_cell.fg && - gc->bg == tty->last_cell.bg && - gc->us == tty->last_cell.us) - return; - tty->last_wp = (wp != NULL ? (int)wp->id : -1); - memcpy(&tty->last_cell, gc, sizeof tty->last_cell); - /* Copy cell and update default colours. */ memcpy(&gc2, gc, sizeof gc2); - if (wp != NULL) - tty_default_colours(&gc2, wp); + if (gc2.fg == 8) + gc2.fg = defaults->fg; + if (gc2.bg == 8) + gc2.bg = defaults->bg; + + /* Ignore cell if it is the same as the last one. */ + if (gc2.attr == tty->last_cell.attr && + gc2.fg == tty->last_cell.fg && + gc2.bg == tty->last_cell.bg && + gc2.us == tty->last_cell.us) + return; /* * If no setab, try to use the reverse attribute as a best-effort for a @@ -2320,9 +2281,9 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, } /* Fix up the colours if necessary. */ - tty_check_fg(tty, wp, &gc2); - tty_check_bg(tty, wp, &gc2); - tty_check_us(tty, wp, &gc2); + tty_check_fg(tty, palette, &gc2); + tty_check_bg(tty, palette, &gc2); + tty_check_us(tty, palette, &gc2); /* * If any bits are being cleared or the underline colour is now default, @@ -2377,6 +2338,8 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, tty_putcode(tty, TTYC_SMOL); if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) tty_putcode(tty, TTYC_SMACS); + + memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell); } static void @@ -2441,7 +2404,7 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) } static void -tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) +tty_check_fg(struct tty *tty, int *palette, struct grid_cell *gc) { u_char r, g, b; u_int colours; @@ -2456,7 +2419,7 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) c = gc->fg; if (c < 8 && gc->attr & GRID_ATTR_BRIGHT) c += 90; - if ((c = window_pane_get_palette(wp, c)) != -1) + if ((c = tty_get_palette(palette, c)) != -1) gc->fg = c; } @@ -2497,7 +2460,7 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) } static void -tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) +tty_check_bg(struct tty *tty, int *palette, struct grid_cell *gc) { u_char r, g, b; u_int colours; @@ -2505,7 +2468,7 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Perform substitution if this pane has a palette. */ if (~gc->flags & GRID_FLAG_NOPALETTE) { - if ((c = window_pane_get_palette(wp, gc->bg)) != -1) + if ((c = tty_get_palette(palette, gc->bg)) != -1) gc->bg = c; } @@ -2548,14 +2511,13 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) } static void -tty_check_us(__unused struct tty *tty, struct window_pane *wp, - struct grid_cell *gc) +tty_check_us(__unused struct tty *tty, int *palette, struct grid_cell *gc) { int c; /* Perform substitution if this pane has a palette. */ if (~gc->flags & GRID_FLAG_NOPALETTE) { - if ((c = window_pane_get_palette(wp, gc->us)) != -1) + if ((c = tty_get_palette(palette, gc->us)) != -1) gc->us = c; } @@ -2686,11 +2648,12 @@ tty_window_default_style(struct grid_cell *gc, struct window_pane *wp) gc->bg = wp->bg; } -static void +void tty_default_colours(struct grid_cell *gc, struct window_pane *wp) { struct options *oo = wp->options; - int c; + + memcpy(gc, &grid_default_cell, sizeof *gc); if (wp->flags & PANE_STYLECHANGED) { wp->flags &= ~PANE_STYLECHANGED; @@ -2707,12 +2670,6 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) gc->fg = wp->cached_active_gc.fg; else gc->fg = wp->cached_gc.fg; - - if (gc->fg != 8) { - c = window_pane_get_palette(wp, gc->fg); - if (c != -1) - gc->fg = c; - } } if (gc->bg == 8) { @@ -2720,21 +2677,16 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) gc->bg = wp->cached_active_gc.bg; else gc->bg = wp->cached_gc.bg; - - if (gc->bg != 8) { - c = window_pane_get_palette(wp, gc->bg); - if (c != -1) - gc->bg = c; - } } } static void -tty_default_attributes(struct tty *tty, struct window_pane *wp, u_int bg) +tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, + int *palette, u_int bg) { - static struct grid_cell gc; + struct grid_cell gc; memcpy(&gc, &grid_default_cell, sizeof gc); gc.bg = bg; - tty_attributes(tty, &gc, wp); + tty_attributes(tty, &gc, defaults, palette); } diff --git a/window-clock.c b/window-clock.c index 45d4d47b..8cef3f9a 100644 --- a/window-clock.c +++ b/window-clock.c @@ -219,7 +219,7 @@ window_clock_draw_screen(struct window_mode_entry *wme) colour = options_get_number(wp->window->options, "clock-mode-colour"); style = options_get_number(wp->window->options, "clock-mode-style"); - screen_write_start(&ctx, NULL, s); + screen_write_start(&ctx, s); t = time(NULL); tm = localtime(&t); diff --git a/window-copy.c b/window-copy.c index f5a07a10..9f84ade9 100644 --- a/window-copy.c +++ b/window-copy.c @@ -406,7 +406,7 @@ window_copy_init(struct window_mode_entry *wme, data->screen.cx = data->cx; data->screen.cy = data->cy; - screen_write_start(&ctx, NULL, &data->screen); + screen_write_start(&ctx, &data->screen); for (i = 0; i < screen_size_y(&data->screen); i++) window_copy_write_line(wme, &ctx, i); screen_write_cursormove(&ctx, data->cx, data->cy, 0); @@ -473,7 +473,7 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); - screen_write_start(&back_ctx, NULL, backing); + screen_write_start(&back_ctx, backing); if (data->backing_written) { /* * On the second or later line, do a CRLF before writing @@ -489,7 +489,7 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) data->oy += screen_hsize(data->backing) - old_hsize; - screen_write_start(&ctx, wp, &data->screen); + screen_write_start_pane(&ctx, wp, &data->screen); /* * If the history has changed, draw the top line. @@ -713,7 +713,7 @@ window_copy_size_changed(struct window_mode_entry *wme) window_copy_clear_selection(wme); window_copy_clear_marks(wme); - screen_write_start(&ctx, NULL, s); + screen_write_start(&ctx, s); window_copy_write_lines(wme, &ctx, 0, screen_size_y(s)); screen_write_stop(&ctx); @@ -2822,7 +2822,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) fy = screen_hsize(data->backing) - data->oy + data->cy; screen_init(&ss, screen_write_strlen("%s", str), 1, 0); - screen_write_start(&ctx, NULL, &ss); + screen_write_start(&ctx, &ss); screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); screen_write_stop(&ctx); @@ -2867,7 +2867,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, if (ssp == NULL) { width = screen_write_strlen("%s", data->searchstr); screen_init(&ss, width, 1, 0); - screen_write_start(&ctx, NULL, &ss); + screen_write_start(&ctx, &ss); screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); screen_write_stop(&ctx); @@ -3207,7 +3207,7 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny) struct screen_write_ctx ctx; u_int i; - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); for (i = py; i < py + ny; i++) window_copy_write_line(wme, &ctx, i); screen_write_cursormove(&ctx, data->cx, data->cy, 0); @@ -3326,7 +3326,7 @@ window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) if (data->cx == screen_size_x(s)) window_copy_redraw_lines(wme, data->cy, 1); else { - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_cursormove(&ctx, data->cx, data->cy, 0); screen_write_stop(&ctx); } @@ -3579,7 +3579,7 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, struct screen_write_ctx ctx; if (options_get_number(global_options, "set-clipboard") != 0) { - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); @@ -3636,7 +3636,7 @@ window_copy_append_selection(struct window_mode_entry *wme) return; if (options_get_number(global_options, "set-clipboard") != 0) { - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); @@ -4399,7 +4399,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 0, 0); - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); screen_write_deleteline(&ctx, ny, 8); window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny); @@ -4435,7 +4435,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 0, 0); - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); screen_write_insertline(&ctx, ny, 8); window_copy_write_lines(wme, &ctx, 0, ny); diff --git a/window.c b/window.c index b1d812fc..8121ba2d 100644 --- a/window.c +++ b/window.c @@ -1005,26 +1005,6 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wp->flags |= (PANE_RESIZE|PANE_RESIZED); } -void -window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, - int cursor) -{ - if (!options_get_number(wp->options, "alternate-screen")) - return; - screen_alternate_on(&wp->base, gc, cursor); - wp->flags |= PANE_REDRAW; -} - -void -window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, - int cursor) -{ - if (!options_get_number(wp->options, "alternate-screen")) - return; - screen_alternate_off(&wp->base, gc, cursor); - wp->flags |= PANE_REDRAW; -} - void window_pane_set_palette(struct window_pane *wp, u_int n, int colour) { From 3f1fc9cde33aac1fedd3ed9110f0d3e31308e92a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 17:30:28 +0100 Subject: [PATCH 0294/1006] Get the whole overlay screen not just the mode so cursor changes are included. --- popup.c | 4 ++-- server-client.c | 18 +++++++----------- tmux.h | 2 +- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/popup.c b/popup.c index c13a38cf..826e1571 100644 --- a/popup.c +++ b/popup.c @@ -136,7 +136,7 @@ popup_write_screen(struct client *c, struct popup_data *pd) screen_write_stop(&ctx); } -static int +static struct screen * popup_mode_cb(struct client *c, u_int *cx, u_int *cy) { struct popup_data *pd = c->overlay_data; @@ -145,7 +145,7 @@ popup_mode_cb(struct client *c, u_int *cx, u_int *cy) return (0); *cx = pd->px + 1 + pd->s.cx; *cy = pd->py + 1 + pd->s.cy; - return (pd->s.mode); + return (&pd->s); } static int diff --git a/server-client.c b/server-client.c index 5f7ce6b8..23435b48 100644 --- a/server-client.c +++ b/server-client.c @@ -1542,7 +1542,7 @@ server_client_reset_state(struct client *c) struct window_pane *wp = w->active, *loop; struct screen *s; struct options *oo = c->session->options; - int mode, cursor, flags; + int mode = 0, cursor, flags; u_int cx = 0, cy = 0, ox, oy, sx, sy; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) @@ -1553,18 +1553,14 @@ server_client_reset_state(struct client *c) tty->flags &= ~TTY_BLOCK; /* Get mode from overlay if any, else from screen. */ - if (c->overlay_draw != NULL) { - s = NULL; - if (c->overlay_mode == NULL) - mode = 0; - else - mode = c->overlay_mode(c, &cx, &cy); - } else { + if (c->overlay_draw != NULL && c->overlay_mode != NULL) + s = c->overlay_mode(c, &cx, &cy); + else s = wp->screen; + if (s != NULL) mode = s->mode; - if (c->prompt_string != NULL || c->message_string != NULL) - mode &= ~MODE_CURSOR; - } + 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/tmux.h b/tmux.h index 726fdb26..15a8506f 100644 --- a/tmux.h +++ b/tmux.h @@ -1509,7 +1509,7 @@ RB_HEAD(client_files, client_file); typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); typedef int (*overlay_check_cb)(struct client *, u_int, u_int); -typedef int (*overlay_mode_cb)(struct client *, u_int *, u_int *); +typedef struct screen *(*overlay_mode_cb)(struct client *, u_int *, u_int *); typedef void (*overlay_draw_cb)(struct client *, struct screen_redraw_ctx *); typedef int (*overlay_key_cb)(struct client *, struct key_event *); typedef void (*overlay_free_cb)(struct client *); From 1fa9bcc18357af43954ddb64a51173d99d83dd54 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 17:55:08 +0100 Subject: [PATCH 0295/1006] Turn off overlay check when we know we are inside. --- popup.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/popup.c b/popup.c index 826e1571..81ae0347 100644 --- a/popup.c +++ b/popup.c @@ -367,6 +367,7 @@ popup_job_update_cb(struct job *job) { struct popup_data *pd = job_get_data(job); struct evbuffer *evb = job_get_event(job)->input; + struct client *c = pd->c; struct screen *s = &pd->s; void *data = EVBUFFER_DATA(evb); size_t size = EVBUFFER_LENGTH(evb); @@ -374,9 +375,13 @@ popup_job_update_cb(struct job *job) if (size == 0) return; - pd->c->tty.flags &= ~TTY_FREEZE; + c->overlay_check = NULL; + c->tty.flags &= ~TTY_FREEZE; + input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size); - pd->c->tty.flags |= TTY_FREEZE; + + c->tty.flags |= TTY_FREEZE; + c->overlay_check = popup_check_cb; evbuffer_drain(evb, size); } From bf84359dfb6ab241fc4e586fcbe8070a838f1f37 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 17:59:13 +0100 Subject: [PATCH 0296/1006] Use VIS_CSTYLE for paste buffers also. --- paste.c | 2 +- window-buffer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/paste.c b/paste.c index f1f3c9f7..51ae2b1d 100644 --- a/paste.c +++ b/paste.c @@ -311,7 +311,7 @@ paste_make_sample(struct paste_buffer *pb) { char *buf; size_t len, used; - const int flags = VIS_OCTAL|VIS_TAB|VIS_NL; + const int flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; const size_t width = 200; len = pb->size; diff --git a/window-buffer.c b/window-buffer.c index 4e17a7fa..16adbc61 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -231,7 +231,7 @@ window_buffer_draw(__unused void *modedata, void *itemdata, while (end != pdata + psize && *end != '\n') end++; buf = xreallocarray(buf, 4, end - start + 1); - utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_TAB); + utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_CSTYLE|VIS_TAB); if (*buf != '\0') { screen_write_cursormove(ctx, cx, cy + i, 0); screen_write_nputs(ctx, sx, &grid_default_cell, "%s", From e078f975c55fd42bf3c8396954038c9071cb5ec1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 1 May 2020 18:24:20 +0100 Subject: [PATCH 0297/1006] Update CHANGES. --- CHANGES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index 3d833407..e01c97a9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,11 @@ CHANGES FROM 3.1b TO 3.2 +* Add a key (e) in buffer mode to open the buffer in an editor. The buffer + contents is updated when the editor exits. + +* Add -e flag for new-session to set environment variables, like the same flag + for new-window. + * Improve search match marking in copy mode. Two new options copy-mode-match-style and copy-mode-current-match-style to set the style for matches and for the current match respectively. Also a change so that if a From cb1131a29481246c9cb9b68cb8591cc747fcdd9a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 2 May 2020 15:15:52 +0100 Subject: [PATCH 0298/1006] menu_mode_cb needs to return a screen also. --- menu.c | 8 ++++---- server-client.c | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/menu.c b/menu.c index 27836694..07fc8fa8 100644 --- a/menu.c +++ b/menu.c @@ -130,14 +130,12 @@ menu_free(struct menu *menu) free(menu); } -static int +static struct screen * menu_mode_cb(struct client *c, __unused u_int *cx, __unused u_int *cy) { struct menu_data *md = c->overlay_data; - if (~md->flags & MENU_NOMOUSE) - return (MODE_MOUSE_ALL); - return (0); + return (&md->s); } static void @@ -351,6 +349,8 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, if (fs != NULL) cmd_find_copy_state(&md->fs, fs); screen_init(&md->s, menu->width + 4, menu->count + 2, 0); + if (~md->flags & MENU_NOMOUSE) + md->s.mode |= MODE_MOUSE_ALL; md->px = px; md->py = py; diff --git a/server-client.c b/server-client.c index 23435b48..a9762c56 100644 --- a/server-client.c +++ b/server-client.c @@ -1540,7 +1540,7 @@ server_client_reset_state(struct client *c) struct tty *tty = &c->tty; struct window *w = c->session->curw->window; struct window_pane *wp = w->active, *loop; - struct screen *s; + struct screen *s = NULL; struct options *oo = c->session->options; int mode = 0, cursor, flags; u_int cx = 0, cy = 0, ox, oy, sx, sy; @@ -1553,9 +1553,10 @@ server_client_reset_state(struct client *c) tty->flags &= ~TTY_BLOCK; /* Get mode from overlay if any, else from screen. */ - if (c->overlay_draw != NULL && c->overlay_mode != NULL) - s = c->overlay_mode(c, &cx, &cy); - else + if (c->overlay_draw != NULL) { + if (c->overlay_mode != NULL) + s = c->overlay_mode(c, &cx, &cy); + } else s = wp->screen; if (s != NULL) mode = s->mode; From af69289e0ea2b4af80206bcde0574358fa1b9169 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 2 May 2020 16:17:44 +0100 Subject: [PATCH 0299/1006] Clamping to area needs to use the offset without the status line, since that is where the window offsets are based. --- popup.c | 4 ++-- screen-write.c | 5 +++-- tmux.h | 2 ++ tty.c | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/popup.c b/popup.c index 81ae0347..9937d586 100644 --- a/popup.c +++ b/popup.c @@ -79,8 +79,8 @@ popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) ttyctx->wsx = c->tty.sx; ttyctx->wsy = c->tty.sy; - ttyctx->xoff = pd->px + 1; - ttyctx->yoff = pd->py + 1; + ttyctx->xoff = ttyctx->rxoff = pd->px + 1; + ttyctx->yoff = ttyctx->ryoff = pd->py + 1; return (1); } diff --git a/screen-write.c b/screen-write.c index c7996ec9..909b8531 100644 --- a/screen-write.c +++ b/screen-write.c @@ -136,8 +136,9 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c) ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, &ttyctx->wsx, &ttyctx->wsy); - ttyctx->xoff = wp->xoff; - ttyctx->yoff = wp->yoff; + ttyctx->xoff = ttyctx->rxoff = wp->xoff; + ttyctx->yoff = ttyctx->ryoff = wp->yoff; + if (status_at_line(c) == 0) ttyctx->yoff += status_line_size(c); diff --git a/tmux.h b/tmux.h index 15a8506f..a1f6d924 100644 --- a/tmux.h +++ b/tmux.h @@ -1321,6 +1321,8 @@ struct tty_ctx { /* Target region (usually pane) offset and size. */ u_int xoff; u_int yoff; + u_int rxoff; + u_int ryoff; u_int sx; u_int sy; diff --git a/tty.c b/tty.c index 25a0cd02..388f6f5f 100644 --- a/tty.c +++ b/tty.c @@ -987,7 +987,7 @@ static int tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry) { - u_int xoff = ctx->xoff + px; + u_int xoff = ctx->rxoff + px; if (!tty_is_visible(tty, ctx, px, py, nx, 1)) return (0); @@ -1082,7 +1082,7 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, u_int nx, u_int ny, u_int *i, u_int *j, u_int *x, u_int *y, u_int *rx, u_int *ry) { - u_int xoff = ctx->xoff + px, yoff = ctx->yoff + py; + u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py; if (!tty_is_visible(tty, ctx, px, py, nx, ny)) return (0); From d9a44493078a3f19af0087b3bb308d1b9e660761 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 2 May 2020 16:44:31 +0100 Subject: [PATCH 0300/1006] Do not need to work out status line offset, we already have it. --- tty.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tty.c b/tty.c index 388f6f5f..c8efeac7 100644 --- a/tty.c +++ b/tty.c @@ -963,21 +963,16 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) /* Is this position visible in the pane? */ static int -tty_is_visible(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, - u_int nx, u_int ny) +tty_is_visible(__unused struct tty *tty, const struct tty_ctx *ctx, u_int px, + u_int py, u_int nx, u_int ny) { - u_int xoff = ctx->xoff + px, yoff = ctx->yoff + py, lines; + u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py; if (!ctx->bigger) return (1); - if (status_at_line(tty->client) == 0) - lines = status_line_size(tty->client); - else - lines = 0; - if (xoff + nx <= ctx->wox || xoff >= ctx->wox + ctx->wsx || - yoff + ny <= ctx->woy || yoff >= lines + ctx->woy + ctx->wsy) + yoff + ny <= ctx->woy || yoff >= ctx->woy + ctx->wsy) return (0); return (1); } From 846b99e0cb599653354cb316d9c1c8843981f5bf Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 10:49:16 +0100 Subject: [PATCH 0301/1006] Portable does not need sys/queue.h. --- file.c | 1 - 1 file changed, 1 deletion(-) diff --git a/file.c b/file.c index 2c06765c..e7d90fc7 100644 --- a/file.c +++ b/file.c @@ -17,7 +17,6 @@ */ #include -#include #include #include From 955d9d22b95eab235b85b9c47e801e54d1cbcdaa Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 11:09:36 +0100 Subject: [PATCH 0302/1006] Add static build. --- .github/travis/before-install.sh | 12 ++++++++++++ .github/travis/build.sh | 9 +++++++++ .travis.yml | 27 ++++++++++++++++++++------- 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 .github/travis/before-install.sh create mode 100644 .github/travis/build.sh diff --git a/.github/travis/before-install.sh b/.github/travis/before-install.sh new file mode 100644 index 00000000..3d9c91c4 --- /dev/null +++ b/.github/travis/before-install.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +if [ "$TRAVIS_OS_NAME" = "linux" ]; then + sudo apt-get update -qq + sudo apt-get -y install bison \ + autotools-dev \ + libncurses5-dev \ + libevent-dev \ + pkg-config \ + libutempter-dev \ + build-essential +fi diff --git a/.github/travis/build.sh b/.github/travis/build.sh new file mode 100644 index 00000000..cbd4e45b --- /dev/null +++ b/.github/travis/build.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +sh autogen.sh || exit 1 +if [ "$BUILD" = "static" ]; then + ./configure --enable-static || exit 1 +else + ./configure || exit 1 +fi +exec make diff --git a/.travis.yml b/.travis.yml index fd85bf61..6beb5799 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,29 @@ language: c os: - - linux - - osx + - linux + - osx compiler: - - gcc - - clang + - gcc + - clang + +env: + - BUILD= + - BUILD=static + +jobs: + exclude: + - os: osx + - compiler: gcc + - env: BUILD=static + exclude: + - os: osx + - compiler: clang + - env: BUILD=static before_install: - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq; fi - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -y install bison autotools-dev libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential; fi + - sh .github/travis/before-install.sh script: - - ./autogen.sh && ./configure && make + - sh .github/travis/build.sh From ff250aa30e2faa38e44a4a415dde2c87d9174415 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 11:12:51 +0100 Subject: [PATCH 0303/1006] Fix exclude. --- .travis.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6beb5799..f20a983f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,12 +15,11 @@ env: jobs: exclude: - os: osx - - compiler: gcc - - env: BUILD=static - exclude: + compiler: gcc + env: BUILD=static - os: osx - - compiler: clang - - env: BUILD=static + compiler: clang + env: BUILD=static before_install: - sh .github/travis/before-install.sh From 55901367d074c93f79b2ce192007e97793e8df9b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 11:16:30 +0100 Subject: [PATCH 0304/1006] Add more stuff. --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index f20a983f..b7d2021d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,16 @@ compiler: - gcc - clang +arch: + - amd64 + - arm64 + +dist: + - xenial + - bionic + - trusty + - precise + env: - BUILD= - BUILD=static From 97d490204b9dd2f3bc3b5d54a1321b084f2021d6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 11:47:28 +0100 Subject: [PATCH 0305/1006] Add build of everything. --- .github/travis/before-install.sh | 2 +- .github/travis/build-all.sh | 35 ++++++++++++++++++++++++++++++++ .github/travis/build.sh | 20 ++++++++++++------ .travis.yml | 7 +------ 4 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 .github/travis/build-all.sh diff --git a/.github/travis/before-install.sh b/.github/travis/before-install.sh index 3d9c91c4..22d93970 100644 --- a/.github/travis/before-install.sh +++ b/.github/travis/before-install.sh @@ -3,7 +3,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq sudo apt-get -y install bison \ - autotools-dev \ + autotools-dev \ libncurses5-dev \ libevent-dev \ pkg-config \ diff --git a/.github/travis/build-all.sh b/.github/travis/build-all.sh new file mode 100644 index 00000000..561f9e30 --- /dev/null +++ b/.github/travis/build-all.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +BUILD=$PWD/build + +LIBEVENT=https://github.com/libevent/libevent/releases/download/release-2.1.11-stable/libevent-2.1.11-stab\ +le.tar.gz +NCURSES=ftp://ftp.invisible-island.net/ncurses/ncurses-6.2.tar.gz + +wget -q $LIBEVENT || exit 1 +tar -zxf libevent-*.tar.gz || exit 1 +(cd libevent-*/ && + ./configure --prefix=$BUILD \ + --enable-shared \ + --disable-libevent-regress \ + --disable-samples && + make && make install) || exit 1 + +wget -q $NCURSES || exit 1 +tar -zxf ncurses-*.tar.gz || exit 1 +(cd ncurses-*/ && + CPPFLAGS=-P ./configure --prefix=$BUILD \ + --with-shared \ + --without-ada \ + --without-cxx \ + --without-manpages \ + --without-progs \ + --without-tests \ + --without-tack \ + --enable-pc-files \ + --with-pkg-config-libdir=$BUILD/lib/pkgconfig && + make && make install) || exit 1 + +sh autogen.sh || exit 1 +PKG_CONFIG_PATH=$BUILD/lib/pkgconfig ./configure --prefix=$BUILD "$@" +make && make install || exit 1 diff --git a/.github/travis/build.sh b/.github/travis/build.sh index cbd4e45b..c244c59c 100644 --- a/.github/travis/build.sh +++ b/.github/travis/build.sh @@ -1,9 +1,17 @@ #!/bin/sh sh autogen.sh || exit 1 -if [ "$BUILD" = "static" ]; then - ./configure --enable-static || exit 1 -else - ./configure || exit 1 -fi -exec make +case "$BUILD" in + static) + ./configure --enable-static || exit 1 + exec make + ;; + all) + sh $(dirname $0)/build-all.sh + exec make + ;; + *) + ./configure || exit 1 + exec make + ;; +esac diff --git a/.travis.yml b/.travis.yml index b7d2021d..d093574c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,15 +12,10 @@ arch: - amd64 - arm64 -dist: - - xenial - - bionic - - trusty - - precise - env: - BUILD= - BUILD=static + - BUILD=all jobs: exclude: From b203f7f19f972457fac840d95a662eb0e924f0c3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 12:03:50 +0100 Subject: [PATCH 0306/1006] Better ncurses URL and some other fixes. --- .github/travis/build-all.sh | 39 +++++++++++++++++++------------------ .travis.yml | 8 ++++++++ 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/.github/travis/build-all.sh b/.github/travis/build-all.sh index 561f9e30..d4ab35af 100644 --- a/.github/travis/build-all.sh +++ b/.github/travis/build-all.sh @@ -4,31 +4,32 @@ BUILD=$PWD/build LIBEVENT=https://github.com/libevent/libevent/releases/download/release-2.1.11-stable/libevent-2.1.11-stab\ le.tar.gz -NCURSES=ftp://ftp.invisible-island.net/ncurses/ncurses-6.2.tar.gz +NCURSES=https://ftp.gnu.org/gnu/ncurses/ncurses-6.2.tar.gz -wget -q $LIBEVENT || exit 1 +wget -4q $LIBEVENT || exit 1 tar -zxf libevent-*.tar.gz || exit 1 (cd libevent-*/ && - ./configure --prefix=$BUILD \ - --enable-shared \ - --disable-libevent-regress \ - --disable-samples && - make && make install) || exit 1 + ./configure --prefix=$BUILD \ + --enable-shared \ + --disable-libevent-regress \ + --disable-samples && + make && make install) || exit 1 -wget -q $NCURSES || exit 1 +wget -4q $NCURSES || exit 1 tar -zxf ncurses-*.tar.gz || exit 1 (cd ncurses-*/ && - CPPFLAGS=-P ./configure --prefix=$BUILD \ - --with-shared \ - --without-ada \ - --without-cxx \ - --without-manpages \ - --without-progs \ - --without-tests \ - --without-tack \ - --enable-pc-files \ - --with-pkg-config-libdir=$BUILD/lib/pkgconfig && - make && make install) || exit 1 + CPPFLAGS=-P ./configure --prefix=$BUILD \ + --with-shared \ + --with-termlib \ + --without-ada \ + --without-cxx \ + --without-manpages \ + --without-progs \ + --without-tests \ + --without-tack \ + --enable-pc-files \ + --with-pkg-config-libdir=$BUILD/lib/pkgconfig && + make && make install) || exit 1 sh autogen.sh || exit 1 PKG_CONFIG_PATH=$BUILD/lib/pkgconfig ./configure --prefix=$BUILD "$@" diff --git a/.travis.yml b/.travis.yml index d093574c..5a205523 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,14 @@ jobs: - os: osx compiler: clang env: BUILD=static + - os: linux + compiler: gcc + arch: arm64 + env: BUILD=all + - os: linux + compiler: clang + arch: arm64 + env: BUILD=all before_install: - sh .github/travis/before-install.sh From 1cfa6b0d5c52056cf71c58513b4ec7a0b4bfb319 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 12:12:47 +0100 Subject: [PATCH 0307/1006] Try w/o database. --- .github/travis/build-all.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/travis/build-all.sh b/.github/travis/build-all.sh index d4ab35af..2edd82b2 100644 --- a/.github/travis/build-all.sh +++ b/.github/travis/build-all.sh @@ -27,6 +27,8 @@ tar -zxf ncurses-*.tar.gz || exit 1 --without-progs \ --without-tests \ --without-tack \ + --disable-database \ + --with-fallbacks=ansi \ --enable-pc-files \ --with-pkg-config-libdir=$BUILD/lib/pkgconfig && make && make install) || exit 1 From fbc8fca067ad6c942577277305043b59695724c6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 12:18:04 +0100 Subject: [PATCH 0308/1006] Use termcap instead. --- .github/travis/build-all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/travis/build-all.sh b/.github/travis/build-all.sh index 2edd82b2..157db81f 100644 --- a/.github/travis/build-all.sh +++ b/.github/travis/build-all.sh @@ -28,7 +28,7 @@ tar -zxf ncurses-*.tar.gz || exit 1 --without-tests \ --without-tack \ --disable-database \ - --with-fallbacks=ansi \ + --with-termcap \ --enable-pc-files \ --with-pkg-config-libdir=$BUILD/lib/pkgconfig && make && make install) || exit 1 From ff5e3d1a882d7ac80f76da49418408e0e3bc2e45 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 12:20:08 +0100 Subject: [PATCH 0309/1006] Ugh, enable not with. --- .github/travis/build-all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/travis/build-all.sh b/.github/travis/build-all.sh index 157db81f..00f8f522 100644 --- a/.github/travis/build-all.sh +++ b/.github/travis/build-all.sh @@ -28,7 +28,7 @@ tar -zxf ncurses-*.tar.gz || exit 1 --without-tests \ --without-tack \ --disable-database \ - --with-termcap \ + --enable-termcap \ --enable-pc-files \ --with-pkg-config-libdir=$BUILD/lib/pkgconfig && make && make install) || exit 1 From 4fcbd6700f7030c93361c76a03337e632227da75 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 12:33:47 +0100 Subject: [PATCH 0310/1006] Add musl builds. --- .github/travis/before-install.sh | 5 +++++ .github/travis/build.sh | 8 ++++++++ .travis.yml | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/.github/travis/before-install.sh b/.github/travis/before-install.sh index 22d93970..31fc5868 100644 --- a/.github/travis/before-install.sh +++ b/.github/travis/before-install.sh @@ -9,4 +9,9 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then pkg-config \ libutempter-dev \ build-essential + + if [ "$BUILD" = "musl" ]; then + sudo apt-get -y install musl-dev \ + musl-tools + fi fi diff --git a/.github/travis/build.sh b/.github/travis/build.sh index c244c59c..f863d8ad 100644 --- a/.github/travis/build.sh +++ b/.github/travis/build.sh @@ -10,6 +10,14 @@ case "$BUILD" in sh $(dirname $0)/build-all.sh exec make ;; + musl) + CC=musl-gcc sh $(dirname $0)/build-all.sh + exec make + ;; + musl-static) + CC=musl-gcc sh $(dirname $0)/build-all.sh --enable-static + exec make + ;; *) ./configure || exit 1 exec make diff --git a/.travis.yml b/.travis.yml index 5a205523..16a04a16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,15 +16,32 @@ env: - BUILD= - BUILD=static - BUILD=all + - BUILD=musl + - BUILD=musl-static jobs: exclude: + # Static builds are broken on OS X (by Apple) - os: osx compiler: gcc env: BUILD=static - os: osx compiler: clang env: BUILD=static + # No musl on OS X + - os: osx + compiler: gcc + env: BUILD=musl + - os: osx + compiler: clang + env: BUILD=musl + - os: osx + compiler: gcc + env: BUILD=musl-static + - os: osx + compiler: clang + env: BUILD=musl-static + # arm64 doesn't link ncurses - os: linux compiler: gcc arch: arm64 @@ -33,6 +50,22 @@ jobs: compiler: clang arch: arm64 env: BUILD=all + - os: linux + compiler: gcc + arch: arm64 + env: BUILD=musl + - os: linux + compiler: clang + arch: arm64 + env: BUILD=musl + - os: linux + compiler: gcc + arch: arm64 + env: BUILD=musl-static + - os: linux + compiler: clang + arch: arm64 + env: BUILD=musl-static before_install: - sh .github/travis/before-install.sh From cb09705df32f2d0bf3bb8b7d93edec12bc07aef0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 12:37:38 +0100 Subject: [PATCH 0311/1006] Need musl-gcc for static also. --- .github/travis/before-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/travis/before-install.sh b/.github/travis/before-install.sh index 31fc5868..9954e583 100644 --- a/.github/travis/before-install.sh +++ b/.github/travis/before-install.sh @@ -10,7 +10,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then libutempter-dev \ build-essential - if [ "$BUILD" = "musl" ]; then + if [ "$BUILD" = "musl" -o "$BUILD" = "musl-static" ]; then sudo apt-get -y install musl-dev \ musl-tools fi From e7aeb77bd9ad8235aaa0e7efa17bcdcf11cfd09d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 15:44:38 +0100 Subject: [PATCH 0312/1006] Use the cursor position not the current position when working out which marks are current. --- window-copy.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/window-copy.c b/window-copy.c index 9f84ade9..ce6bcb72 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3057,25 +3057,28 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, const struct grid_cell *cgc) { struct window_copy_mode_data *data = wme->data; - u_int mark, at, start, end, cy; + u_int mark, start, end, cy, cursor, current; u_int sx = screen_size_x(data->backing); if (data->searchmark == NULL) return; - mark = data->searchmark[(fy * sx) + fx]; + + current = (fy * sx) + fx; + + mark = data->searchmark[current]; if (mark == 0) return; cy = screen_hsize(data->backing) - data->oy + data->cy; - at = (cy * sx) + data->cx; - if (data->searchmark[at] == mark) { - window_copy_match_start_end(data, at, &start, &end); - if (at >= start && at <= end) { + cursor = (cy * sx) + data->cx; + if (data->searchmark[cursor] == mark) { + window_copy_match_start_end(data, cursor, &start, &end); + if (current >= start && current <= end) { gc->attr = cgc->attr; gc->fg = cgc->fg; gc->bg = cgc->bg; + return; } - return; } gc->attr = mgc->attr; From c89ed7c0929a332ee0d267ed6458bcb7abdc079f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 3 May 2020 15:58:29 +0100 Subject: [PATCH 0313/1006] Try to search the entire history first for up to 200 ms so a search count can be shown. If it takes too long, search the visible text only. --- window-copy.c | 114 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 34 deletions(-) diff --git a/window-copy.c b/window-copy.c index ce6bcb72..38e87b67 100644 --- a/window-copy.c +++ b/window-copy.c @@ -258,7 +258,8 @@ struct window_copy_mode_data { int searchregex; char *searchstr; u_char *searchmark; - u_int searchcount; + int searchcount; + int searchmore; int searchthis; int searchx; int searchy; @@ -266,7 +267,8 @@ struct window_copy_mode_data { u_char searchgen; int timeout; /* search has timed out */ -#define WINDOW_COPY_SEARCH_TIMEOUT 10 +#define WINDOW_COPY_SEARCH_TIMEOUT 10000 +#define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200 int jumptype; char jumpchar; @@ -2847,6 +2849,15 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) return (found); } +static uint64_t +window_copy_get_time(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return ((tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000ULL)); +} + static int window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, int regex) @@ -2856,13 +2867,13 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, struct screen_write_ctx ctx; struct grid *gd = s->grid; const struct grid_line *gl; - int found, cis, which = -1; + int found, cis, which = -1, stopped = 0; int cflags = REG_EXTENDED; u_int px, py, i, b, nfound = 0, width; - u_int ssize = 1; + u_int ssize = 1, start, end; char *sbuf; regex_t reg; - time_t tstart, t; + uint64_t stop = 0, tstart, t; if (ssp == NULL) { width = screen_write_strlen("%s", data->searchstr); @@ -2877,10 +2888,6 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, cis = window_copy_is_lowercase(data->searchstr); - free(data->searchmark); - data->searchmark = xcalloc(gd->hsize + gd->sy, gd->sx); - data->searchgen = 1; - if (regex) { sbuf = xmalloc(ssize); sbuf[0] = '\0'; @@ -2893,13 +2900,18 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, return (0); } } - time(&tstart); - for (py = gd->hsize - data->oy; py > 0; py--) { - gl = grid_peek_line(gd, py - 1); - if (~gl->flags & GRID_LINE_WRAPPED) - break; - } - for (; py < gd->hsize - data->oy + gd->sy; py++) { + tstart = window_copy_get_time(); + + start = 0; + end = gd->hsize + gd->sy; + stop = window_copy_get_time() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; + +again: + free(data->searchmark); + data->searchmark = xcalloc(gd->hsize + gd->sy, gd->sx); + data->searchgen = 1; + + for (py = start; py < end; py++) { px = 0; for (;;) { if (regex) { @@ -2930,29 +2942,60 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, px++; } - time(&t); + t = window_copy_get_time(); if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { data->timeout = 1; break; } + if (stop != 0 && t > stop) { + stopped = 1; + break; + } } + if (data->timeout) { + window_copy_clear_marks(wme); + goto out; + } + + if (stopped && stop != 0) { + /* Try again but just the visible context. */ + for (start = gd->hsize - data->oy; start > 0; start--) { + gl = grid_peek_line(gd, start - 1); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + } + end = gd->hsize - data->oy + gd->sy; + stop = 0; + goto again; + } + + if (stopped) { + data->searchthis = -1; + if (nfound > 1000) + data->searchcount = 1000; + else if (nfound > 100) + data->searchcount = 100; + else if (nfound > 10) + data->searchcount = 10; + else + data->searchcount = -1; + data->searchmore = 1; + } else { + if (which != -1) + data->searchthis = 1 + nfound - which; + else + data->searchthis = -1; + data->searchcount = nfound; + data->searchmore = 0; + } + +out: + if (ssp == &ss) + screen_free(&ss); if (regex) { free(sbuf); regfree(®); } - if (data->timeout) { - window_copy_clear_marks(wme); - return (1); - } - - if (which != -1) - data->searchthis = 1 + nfound - which; - else - data->searchthis = -1; - data->searchcount = nfound; - - if (ssp == &ss) - screen_free(&ss); return (1); } @@ -3136,13 +3179,16 @@ window_copy_write_line(struct window_mode_entry *wme, "[%u/%u]", data->oy, hsize); } } else { - if (data->searchthis == -1) { + if (data->searchcount == -1) { size = xsnprintf(hdr, sizeof hdr, - "(%u results) [%d/%u]", data->searchcount, - data->oy, hsize); + "[%u/%u]", data->oy, hsize); + } else if (data->searchthis == -1) { + size = xsnprintf(hdr, sizeof hdr, + "(%d%s results) [%u/%u]", data->searchcount, + data->searchmore ? "+" : "", data->oy, hsize); } else { size = xsnprintf(hdr, sizeof hdr, - "(%u/%u results) [%d/%u]", data->searchthis, + "(%d/%d results) [%u/%u]", data->searchthis, data->searchcount, data->oy, hsize); } } From a10c4c60cb08a0e13e8c65b81a5c1328b1d4788d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 4 May 2020 09:06:57 +0100 Subject: [PATCH 0314/1006] Add to CHANGES. --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index ad215116..01007a95 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,7 @@ CHANGES FROM 3.1a TO 3.1b +* Fix build on systems without sys/queue.h. + * Fix crash when allow-rename is on and an empty name is set. CHANGES FROM 3.1 TO 3.1a From fc13e9bc2b09815a0b38741672d1c70a33500fac Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 4 May 2020 17:37:03 +0100 Subject: [PATCH 0315/1006] Turn off cursor in menus again. --- menu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/menu.c b/menu.c index 07fc8fa8..62010a58 100644 --- a/menu.c +++ b/menu.c @@ -351,6 +351,7 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, screen_init(&md->s, menu->width + 4, menu->count + 2, 0); if (~md->flags & MENU_NOMOUSE) md->s.mode |= MODE_MOUSE_ALL; + md->s.mode &= ~MODE_CURSOR; md->px = px; md->py = py; From 9991a14e8105b0b8e6f2baaec3ed50d1fb54f009 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 May 2020 06:19:29 +0100 Subject: [PATCH 0316/1006] Add formats for after hook command arguments. --- arguments.c | 54 +++++++++++++++++++++++++++++++++++++---------------- cmd-queue.c | 39 +++++++++++++++++++++++++++++++++++++- tmux.h | 2 ++ 3 files changed, 78 insertions(+), 17 deletions(-) diff --git a/arguments.c b/arguments.c index e1956ace..962f3c6a 100644 --- a/arguments.c +++ b/arguments.c @@ -55,11 +55,11 @@ args_cmp(struct args_entry *a1, struct args_entry *a2) /* Find a flag in the arguments tree. */ static struct args_entry * -args_find(struct args *args, u_char ch) +args_find(struct args *args, u_char flag) { struct args_entry entry; - entry.flag = ch; + entry.flag = flag; return (RB_FIND(args_tree, &args->tree, &entry)); } @@ -248,11 +248,11 @@ args_escape(const char *s) /* Return if an argument is present. */ int -args_has(struct args *args, u_char ch) +args_has(struct args *args, u_char flag) { struct args_entry *entry; - entry = args_find(args, ch); + entry = args_find(args, flag); if (entry == NULL) return (0); return (entry->count); @@ -260,15 +260,15 @@ args_has(struct args *args, u_char ch) /* Set argument value in the arguments tree. */ void -args_set(struct args *args, u_char ch, const char *s) +args_set(struct args *args, u_char flag, const char *s) { struct args_entry *entry; struct args_value *value; - entry = args_find(args, ch); + entry = args_find(args, flag); if (entry == NULL) { entry = xcalloc(1, sizeof *entry); - entry->flag = ch; + entry->flag = flag; entry->count = 1; TAILQ_INIT(&entry->values); RB_INSERT(args_tree, &args->tree, entry); @@ -284,22 +284,44 @@ args_set(struct args *args, u_char ch, const char *s) /* Get argument value. Will be NULL if it isn't present. */ const char * -args_get(struct args *args, u_char ch) +args_get(struct args *args, u_char flag) { struct args_entry *entry; - if ((entry = args_find(args, ch)) == NULL) + if ((entry = args_find(args, flag)) == NULL) + return (NULL); + if (TAILQ_EMPTY(&entry->values)) return (NULL); return (TAILQ_LAST(&entry->values, args_values)->value); } +/* Get first argument. */ +u_char +args_first(struct args *args, struct args_entry **entry) +{ + *entry = RB_MIN(args_tree, &args->tree); + if (*entry == NULL) + return (0); + return ((*entry)->flag); +} + +/* Get next argument. */ +u_char +args_next(struct args_entry **entry) +{ + *entry = RB_NEXT(args_tree, &args->tree, *entry); + if (*entry == NULL) + return (0); + return ((*entry)->flag); +} + /* Get first value in argument. */ const char * -args_first_value(struct args *args, u_char ch, struct args_value **value) +args_first_value(struct args *args, u_char flag, struct args_value **value) { struct args_entry *entry; - if ((entry = args_find(args, ch)) == NULL) + if ((entry = args_find(args, flag)) == NULL) return (NULL); *value = TAILQ_FIRST(&entry->values); @@ -322,15 +344,15 @@ args_next_value(struct args_value **value) /* Convert an argument value to a number. */ long long -args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, - char **cause) +args_strtonum(struct args *args, u_char flag, long long minval, + long long maxval, char **cause) { const char *errstr; long long ll; struct args_entry *entry; struct args_value *value; - if ((entry = args_find(args, ch)) == NULL) { + if ((entry = args_find(args, flag)) == NULL) { *cause = xstrdup("missing"); return (0); } @@ -348,13 +370,13 @@ args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, /* Convert an argument to a number which may be a percentage. */ long long -args_percentage(struct args *args, u_char ch, long long minval, +args_percentage(struct args *args, u_char flag, long long minval, long long maxval, long long curval, char **cause) { const char *value; struct args_entry *entry; - if ((entry = args_find(args, ch)) == NULL) { + if ((entry = args_find(args, flag)) == NULL) { *cause = xstrdup("missing"); return (0); } diff --git a/cmd-queue.c b/cmd-queue.c index 59f86c64..620a3e93 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -341,9 +341,15 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, struct cmd_find_state *current, const char *fmt, ...) { struct cmdq_state *state = item->state; + struct cmd *cmd = item->cmd; + struct args *args = cmd_get_args(cmd); + struct args_entry *entryp; + struct args_value *valuep; struct options *oo; va_list ap; - char *name; + char *name, tmp[32], flag, *arguments; + int i; + const char *value; struct cmdq_item *new_item; struct cmdq_state *new_state; struct options_entry *o; @@ -375,6 +381,37 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS); cmdq_add_format(new_state, "hook", "%s", name); + arguments = args_print(args); + cmdq_add_format(new_state, "hook_arguments", "%s", arguments); + free(arguments); + + for (i = 0; i < args->argc; i++) { + xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i); + cmdq_add_format(new_state, tmp, "%s", args->argv[i]); + } + flag = args_first(args, &entryp); + while (flag != 0) { + value = args_get(args, flag); + if (value == NULL) { + xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag); + cmdq_add_format(new_state, tmp, "1"); + } else { + xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag); + cmdq_add_format(new_state, tmp, "%s", value); + } + + i = 0; + value = args_first_value(args, flag, &valuep); + while (value != NULL) { + xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i); + cmdq_add_format(new_state, tmp, "%s", value); + i++; + value = args_next_value(&valuep); + } + + flag = args_next(&entryp); + } + a = options_array_first(o); while (a != NULL) { cmdlist = options_array_item_value(a)->cmdlist; diff --git a/tmux.h b/tmux.h index a1f6d924..57c8da17 100644 --- a/tmux.h +++ b/tmux.h @@ -2090,6 +2090,8 @@ char *args_print(struct args *); char *args_escape(const char *); int args_has(struct args *, u_char); const char *args_get(struct args *, u_char); +u_char args_first(struct args *, struct args_entry **); +u_char args_next(struct args_entry **); const char *args_first_value(struct args *, u_char, struct args_value **); const char *args_next_value(struct args_value **); long long args_strtonum(struct args *, u_char, long long, long long, From 63390d2dd65e3294d958b579fbdff365a5eabe76 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 May 2020 06:31:14 +0100 Subject: [PATCH 0317/1006] Export TERM_PROGRAM and TERM_PROGRAM_VERSION like various other terminals. --- environ.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/environ.c b/environ.c index 0ce717df..940109b0 100644 --- a/environ.c +++ b/environ.c @@ -252,6 +252,8 @@ environ_for_session(struct session *s, int no_TERM) if (!no_TERM) { value = options_get_string(global_options, "default-terminal"); environ_set(env, "TERM", 0, "%s", value); + environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux"); + environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion()); } if (s != NULL) From 1f6c00f8ef2faa462ab092f41ada8d44d7358596 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 May 2020 10:02:47 +0100 Subject: [PATCH 0318/1006] Only redraw popup on the client it belongs to. --- popup.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/popup.c b/popup.c index 9937d586..4b47df2b 100644 --- a/popup.c +++ b/popup.c @@ -70,8 +70,10 @@ popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) { struct popup_data *pd = ttyctx->arg; + if (c != pd->c) + return (0); if (pd->c->flags & CLIENT_REDRAWOVERLAY) - return (-1); + return (0); ttyctx->bigger = 0; ttyctx->wox = 0; From e810f1527252f49ff499343a2f7cf8897f4a78cd Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 May 2020 10:20:57 +0100 Subject: [PATCH 0319/1006] Store and restore cursor position when copy mode is resized, from Anindya Mukherjee. --- grid.c | 2 +- screen.c | 52 ++++++++++++++++++++++++++------------------------ tmux.h | 3 +-- window-copy.c | 53 +++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 70 insertions(+), 40 deletions(-) diff --git a/grid.c b/grid.c index 81f3709c..2437e2ea 100644 --- a/grid.c +++ b/grid.c @@ -1285,7 +1285,7 @@ grid_reflow(struct grid *gd, u_int sx) /* * If the line is exactly right or the first character is wider - * than the targe width, just move it across unchanged. + * than the target width, just move it across unchanged. */ if (width == sx || first > sx) { grid_reflow_move(target, gl); diff --git a/screen.c b/screen.c index dbd418a1..95299306 100644 --- a/screen.c +++ b/screen.c @@ -48,7 +48,7 @@ struct screen_title_entry { TAILQ_HEAD(screen_titles, screen_title_entry); static void screen_resize_y(struct screen *, u_int, int, u_int *); -static void screen_reflow(struct screen *, u_int, u_int *, u_int *); +static void screen_reflow(struct screen *, u_int, u_int *, u_int *, int); /* Free titles stack. */ static void @@ -219,27 +219,19 @@ screen_pop_title(struct screen *s) } } -/* Resize screen and return cursor position. */ +/* Resize screen with options. */ void screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, - int eat_empty, u_int *cx, u_int *cy) + int eat_empty, int cursor) { - u_int tcx, tcy; + u_int cx = s->cx, cy = s->grid->hsize + s->cy; if (s->write_list != NULL) screen_write_free_list(s); - if (cx == NULL) - cx = &tcx; - *cx = s->cx; - - if (cy == NULL) - cy = т - *cy = s->grid->hsize + s->cy; - log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)", __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy, - *cx, *cy); + cx, cy); if (sx < 1) sx = 1; @@ -253,20 +245,21 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, reflow = 0; if (sy != screen_size_y(s)) - screen_resize_y(s, sy, eat_empty, cy); + screen_resize_y(s, sy, eat_empty, &cy); if (reflow) - screen_reflow(s, sx, cx, cy); + screen_reflow(s, sx, &cx, &cy, cursor); - if (*cy >= s->grid->hsize) { - s->cx = *cx; - s->cy = (*cy) - s->grid->hsize; + if (cy >= s->grid->hsize) { + s->cx = cx; + s->cy = cy - s->grid->hsize; } else { s->cx = 0; s->cy = 0; } + log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx, - s->cy, *cx, *cy); + s->cy, cx, cy); if (s->write_list != NULL) screen_write_make_list(s); @@ -276,7 +269,7 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, void screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) { - screen_resize_cursor(s, sx, sy, reflow, 1, NULL, NULL); + screen_resize_cursor(s, sx, sy, reflow, 1, 1); } static void @@ -524,17 +517,26 @@ screen_select_cell(struct screen *s, struct grid_cell *dst, /* Reflow wrapped lines. */ static void -screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy) +screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor) { u_int wx, wy; - grid_wrap_position(s->grid, *cx, *cy, &wx, &wy); - log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx, wy); + if (cursor) { + grid_wrap_position(s->grid, *cx, *cy, &wx, &wy); + log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx, + wy); + } grid_reflow(s->grid, new_x); - grid_unwrap_position(s->grid, cx, cy, wx, wy); - log_debug("%s: new cursor is %u,%u", __func__, *cx,* cy); + if (cursor) { + grid_unwrap_position(s->grid, cx, cy, wx, wy); + log_debug("%s: new cursor is %u,%u", __func__, *cx, *cy); + } + else { + *cx = 0; + *cy = s->grid->hsize; + } } /* diff --git a/tmux.h b/tmux.h index 57c8da17..cc327d8b 100644 --- a/tmux.h +++ b/tmux.h @@ -2527,8 +2527,7 @@ void screen_set_path(struct screen *, const char *); void screen_push_title(struct screen *); void screen_pop_title(struct screen *); void screen_resize(struct screen *, u_int, u_int, int); -void screen_resize_cursor(struct screen *, u_int, u_int, int, int, u_int *, - u_int *); +void screen_resize_cursor(struct screen *, u_int, u_int, int, int, int); void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int, u_int, int, struct grid_cell *); void screen_clear_selection(struct screen *); diff --git a/window-copy.c b/window-copy.c index 38e87b67..f500a65e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -306,8 +306,9 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, u_int *cy, int trim) { struct screen *dst; - u_int sy; const struct grid_line *gl; + u_int sy, wx, wy; + int reflow; dst = xcalloc(1, sizeof *dst); @@ -324,6 +325,12 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, screen_size_x(src), sy, screen_size_x(hint), screen_hsize(src) + screen_size_y(src)); screen_init(dst, screen_size_x(src), sy, screen_hlimit(src)); + + /* + * Ensure history is on for the backing grid so lines are not deleted + * during resizing. + */ + dst->grid->flags |= GRID_HISTORY; grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy); dst->grid->sy = sy - screen_hsize(src); @@ -337,8 +344,19 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, dst->cy = src->cy; } + if (cx != NULL && cy != NULL) { + *cx = dst->cx; + *cy = screen_hsize(dst) + dst->cy; + reflow = (screen_size_x(hint) != screen_size_x(dst)); + } + else + reflow = 0; + if (reflow) + grid_wrap_position(dst->grid, *cx, *cy, &wx, &wy); screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1, - 0, cx, cy); + 0, 0); + if (reflow) + grid_unwrap_position(dst->grid, cx, cy, wx, wy); return (dst); } @@ -392,13 +410,12 @@ window_copy_init(struct window_mode_entry *wme, data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy, wme->swp != wme->wp); + data->cx = cx; if (cy < screen_hsize(data->backing)) { - data->cx = cx; data->cy = 0; data->oy = screen_hsize(data->backing) - cy; } else { - data->cx = data->backing->cx; - data->cy = data->backing->cy; + data->cy = cy - screen_hsize(data->backing); data->oy = 0; } @@ -731,16 +748,28 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; + struct grid *gd = data->backing->grid; + u_int cx, cy, wx, wy; + int reflow; screen_resize(s, sx, sy, 0); - screen_resize_cursor(data->backing, sx, sy, 1, 0, NULL, NULL); + cx = data->cx; + cy = gd->hsize + data->cy - data->oy; + reflow = (gd->sx != sx); + if (reflow) + grid_wrap_position(gd, cx, cy, &wx, &wy); + screen_resize_cursor(data->backing, sx, sy, 1, 0, 0); + if (reflow) + grid_unwrap_position(gd, &cx, &cy, wx, wy); - if (data->cy > sy - 1) - data->cy = sy - 1; - if (data->cx > sx) - data->cx = sx; - if (data->oy > screen_hsize(data->backing)) - data->oy = screen_hsize(data->backing); + data->cx = cx; + if (cy < gd->hsize) { + data->cy = 0; + data->oy = gd->hsize - cy; + } else { + data->cy = cy - gd->hsize; + data->oy = 0; + } window_copy_size_changed(wme); window_copy_redraw_screen(wme); From 23b4e1b9d830fa8b0a62c25d08453ae64e0deb9b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 May 2020 11:35:33 +0100 Subject: [PATCH 0320/1006] pane_path is not #T, from Chris Rawnsley. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 2b460519..e1e51ae4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4553,7 +4553,7 @@ The following variables are available, where appropriate: .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" .It Li "pane_mode" Ta "" Ta "Name of pane mode, if any" -.It Li "pane_path" Ta "#T" Ta "Path of pane (can be set by application)" +.It Li "pane_path" Ta "" Ta "Path of pane (can be set by application)" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" .It Li "pane_right" Ta "" Ta "Right of pane" From 32c134f5a9780ba86635a48814d7fa0e4fd72974 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 May 2020 12:39:20 +0100 Subject: [PATCH 0321/1006] Wrap a line. --- tty-keys.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index f5a3418f..dc064a17 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1064,10 +1064,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, /* Add terminal features. */ switch (p[0]) { case 41: /* VT420 */ - tty_add_features(&c->term_features, - "margins," - "rectfill", - ","); + tty_add_features(&c->term_features, "margins,rectfill", ","); break; case 'M': /* mintty */ tty_default_features(&c->term_features, "mintty", 0); From deacfedc65e6de6b3f84348d6723ae6f14d097df Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 May 2020 13:34:53 +0100 Subject: [PATCH 0322/1006] Remove an extra space in clients output. --- cmd-list-clients.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 75118c8e..3f691890 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -31,7 +31,7 @@ #define LIST_CLIENTS_TEMPLATE \ "#{client_name}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}]" \ - "#{?client_utf8, (utf8),} #{?client_readonly, (ro),}" + "#{?client_utf8, (utf8),}#{?client_readonly, (ro),}" static enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmdq_item *); From 2f89d2e7d816918e152aacfee85fab45ee2057b9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 May 2020 15:42:20 +0100 Subject: [PATCH 0323/1006] Change the existing client flags for control mode to apply for any client, use the same mechanism for the read-only flag and add an ignore-size flag. refresh-client -F has become -f (-F stays for backwards compatibility) and attach-session and switch-client now have -f flags also. A new format "client_flags" lists the flags and is shown by list-clients by default. This separates the read-only flag from "ignore size" behaviour (new ignore-size) flag - both behaviours are useful in different circumstances. attach -r and switchc -r remain and set or toggle both flags together. --- cmd-attach-session.c | 15 +++++++----- cmd-list-clients.c | 8 +++--- cmd-new-session.c | 8 +++--- cmd-refresh-client.c | 25 ++++++------------- cmd-switch-client.c | 10 +++++--- format.c | 2 +- resize.c | 8 +++--- server-client.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ tmux.1 | 46 +++++++++++++++++++++++++++-------- tmux.h | 6 +++-- 10 files changed, 136 insertions(+), 50 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 8c30c767..38d9c024 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -37,8 +37,9 @@ const struct cmd_entry cmd_attach_session_entry = { .name = "attach-session", .alias = "attach", - .args = { "c:dErt:x", 0, 0 }, - .usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE, + .args = { "c:dEf:rt:x", 0, 0 }, + .usage = "[-dErx] [-c working-directory] [-f flags] " + CMD_TARGET_SESSION_USAGE, /* -t is special */ @@ -48,7 +49,7 @@ const struct cmd_entry cmd_attach_session_entry = { enum cmd_retval cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, - int xflag, int rflag, const char *cflag, int Eflag) + int xflag, int rflag, const char *cflag, int Eflag, const char *fflag) { struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state target; @@ -101,6 +102,10 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, free((void *)s->cwd); s->cwd = format_single(item, cflag, c, s, wl, wp); } + if (fflag) + server_client_set_flags(c, fflag); + if (rflag) + c->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE); c->last_session = c->session; if (c->session != NULL) { @@ -135,8 +140,6 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, free(cause); return (CMD_RETURN_ERROR); } - if (rflag) - c->flags |= CLIENT_READONLY; if (dflag || xflag) { if (xflag) @@ -182,5 +185,5 @@ cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item) return (cmd_attach_session(item, args_get(args, 't'), args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'), - args_get(args, 'c'), args_has(args, 'E'))); + args_get(args, 'c'), args_has(args, 'E'), args_get(args, 'f'))); } diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 3f691890..d450f017 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -28,10 +28,10 @@ * List all clients. */ -#define LIST_CLIENTS_TEMPLATE \ - "#{client_name}: #{session_name} " \ - "[#{client_width}x#{client_height} #{client_termname}]" \ - "#{?client_utf8, (utf8),}#{?client_readonly, (ro),}" +#define LIST_CLIENTS_TEMPLATE \ + "#{client_name}: #{session_name} " \ + "[#{client_width}x#{client_height} #{client_termname}] " \ + "#{?client_flags,(,}#{client_flags}#{?client_flags,),}" static enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmdq_item *); diff --git a/cmd-new-session.c b/cmd-new-session.c index 9815e1e1..a9a0376b 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -39,9 +39,9 @@ const struct cmd_entry cmd_new_session_entry = { .name = "new-session", .alias = "new", - .args = { "Ac:dDe:EF:n:Ps:t:x:Xy:", 0, -1 }, + .args = { "Ac:dDe:EF:f:n:Ps:t:x:Xy:", 0, -1 }, .usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] " - "[-n window-name] [-s session-name] " + "[-f flags] [-n window-name] [-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -112,7 +112,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (as != NULL) { retval = cmd_attach_session(item, as->name, args_has(args, 'D'), args_has(args, 'X'), 0, NULL, - args_has(args, 'E')); + args_has(args, 'E'), args_get(args, 'f')); free(newname); return (retval); } @@ -306,6 +306,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) * taking this session and needs to get MSG_READY and stay around. */ if (!detached) { + if (args_has(args, 'f')) + server_client_set_flags(c, args_get(args, 'f')); if (!already_attached) { if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 5514ff73..c53a6a78 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "cC:DF:lLRSt:U", 0, 1 }, - .usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE + .args = { "cC:Df:F:lLRSt:U", 0, 1 }, + .usage = "[-cDlLRSU] [-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, @@ -50,7 +50,6 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) struct tty *tty = &tc->tty; struct window *w; const char *size, *errstr; - char *copy, *next, *s; u_int x, y, adjust; if (args_has(args, 'c') || @@ -108,7 +107,12 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'C') || args_has(args, 'F')) { + if (args_has(args, 'F')) /* -F is an alias for -f */ + server_client_set_flags(tc, args_get(args, 'F')); + if (args_has(args, 'f')) + server_client_set_flags(tc, args_get(args, 'f')); + + if (args_has(args, 'C')) { if (args_has(args, 'C')) { if (!(tc->flags & CLIENT_CONTROL)) { cmdq_error(item, "not a control client"); @@ -129,19 +133,6 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) tc->flags |= CLIENT_SIZECHANGED; recalculate_sizes(); } - if (args_has(args, 'F')) { - if (!(tc->flags & CLIENT_CONTROL)) { - cmdq_error(item, "not a control client"); - return (CMD_RETURN_ERROR); - } - s = copy = xstrdup(args_get(args, 'F')); - while ((next = strsep(&s, ",")) != NULL) { - /* Unknown flags are ignored. */ - if (strcmp(next, "no-output") == 0) - tc->flags |= CLIENT_CONTROL_NOOUTPUT; - } - free(copy); - } return (CMD_RETURN_NORMAL); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 82510ce6..d062b946 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_switch_client_entry = { .name = "switch-client", .alias = "switchc", - .args = { "lc:Enpt:rT:Z", 0, 0 }, + .args = { "lc:EFnpt:rT:Z", 0, 0 }, .usage = "[-ElnprZ] [-c target-client] [-t target-session] " "[-T key-table]", @@ -74,8 +74,12 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) wl = target.wl; wp = target.wp; - if (args_has(args, 'r')) - tc->flags ^= CLIENT_READONLY; + if (args_has(args, 'r')) { + if (tc->flags & CLIENT_READONLY) + tc->flags &= ~(CLIENT_READONLY|CLIENT_IGNORESIZE); + else + tc->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE); + } tablename = args_get(args, 'T'); if (tablename != NULL) { diff --git a/format.c b/format.c index 2e599c60..f6adb51d 100644 --- a/format.c +++ b/format.c @@ -2671,11 +2671,11 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_utf8", "%d", 1); else format_add(ft, "client_utf8", "%d", 0); - if (c->flags & CLIENT_READONLY) format_add(ft, "client_readonly", "%d", 1); else format_add(ft, "client_readonly", "%d", 0); + format_add(ft, "client_flags", "%s", server_client_get_flags(c)); s = c->session; if (s != NULL) diff --git a/resize.c b/resize.c index 96d733f0..15d146d8 100644 --- a/resize.c +++ b/resize.c @@ -72,17 +72,17 @@ ignore_client_size(struct client *c) return (1); if (c->flags & CLIENT_NOSIZEFLAGS) return (1); - if (c->flags & CLIENT_READONLY) { + if (c->flags & CLIENT_IGNORESIZE) { /* - * Ignore readonly clients if there are any attached clients - * that aren't readonly. + * Ignore flagged clients if there are any attached clients + * that aren't flagged. */ TAILQ_FOREACH (loop, &clients, entry) { if (loop->session == NULL) continue; if (loop->flags & CLIENT_NOSIZEFLAGS) continue; - if (~loop->flags & CLIENT_READONLY) + if (~loop->flags & CLIENT_IGNORESIZE) return (1); } } diff --git a/server-client.c b/server-client.c index a9762c56..6de6e97d 100644 --- a/server-client.c +++ b/server-client.c @@ -2255,3 +2255,61 @@ server_client_get_cwd(struct client *c, struct session *s) return (home); return ("/"); } + +/* Set client flags. */ +void +server_client_set_flags(struct client *c, const char *flags) +{ + char *s, *copy, *next; + int flag, not; + + s = copy = xstrdup (flags); + while ((next = strsep(&s, ",")) != NULL) { + not = (*next == '!'); + if (not) + next++; + + if (strcmp(next, "no-output") == 0) + flag = CLIENT_CONTROL_NOOUTPUT; + else if (strcmp(next, "read-only") == 0) + flag = CLIENT_READONLY; + else if (strcmp(next, "ignore-size") == 0) + flag = CLIENT_IGNORESIZE; + else + continue; + + log_debug("client %s set flag %s", c->name, next); + if (not) + c->flags &= ~flag; + else + c->flags |= flag; + } + free(copy); + +} + +/*Get client flags. This is only flags useful to show to users. */ +const char * +server_client_get_flags(struct client *c) +{ + static char s[256]; + + *s = '\0'; + if (c->flags & CLIENT_ATTACHED) + strlcat(s, "attached,", sizeof s); + if (c->flags & CLIENT_CONTROL) + strlcat(s, "control-mode,", sizeof s); + if (c->flags & CLIENT_IGNORESIZE) + strlcat(s, "ignore-size,", sizeof s); + if (c->flags & CLIENT_CONTROL_NOOUTPUT) + strlcat(s, "no-output,", sizeof s); + if (c->flags & CLIENT_READONLY) + strlcat(s, "read-only,", sizeof s); + if (c->flags & CLIENT_SUSPENDED) + strlcat(s, "suspended,", sizeof s); + if (c->flags & CLIENT_UTF8) + strlcat(s, "UTF-8,", sizeof s); + if (*s != '\0') + s[strlen(s) - 1] = '\0'; + return (s); +} diff --git a/tmux.1 b/tmux.1 index e1e51ae4..083b04b6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -947,6 +947,7 @@ The following commands are available to manage clients and sessions: .It Xo Ic attach-session .Op Fl dErx .Op Fl c Ar working-directory +.Op Fl f Ar flags .Op Fl t Ar target-session .Xc .D1 (alias: Ic attach ) @@ -964,12 +965,30 @@ is given, send .Dv SIGHUP to the parent process of the client as well as detaching the client, typically causing it to exit. +.Fl f +sets a comma-separated list of client flags. +The flags are: +.Bl -tag -width Ds +.It read-only +the client is read-only +.It ignore-size +the client does not affect the size of other clients +.It no-output +the client does not receive pane output in control mode +.El +.Pp +A leading +.Ql ! +turns a flag off if the client is already attached. .Fl r -signifies the client is read-only (only keys bound to the +is an alias for +.Fl f +.Ar read-only,ignore-size . +When a client is read-only, only keys bound to the .Ic detach-client or .Ic switch-client -commands have any effect) +commands have any effect. .Pp If no server is started, .Ic attach-session @@ -1095,6 +1114,7 @@ Lock all clients attached to .Op Fl AdDEPX .Op Fl c Ar start-directory .Op Fl e Ar environment +.Op Fl f Ar flags .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name @@ -1132,6 +1152,9 @@ or is given, the .Ic default-size option is set for the session. +.Fl f +sets a comma-separated list of client flags (see +.Ic attach-session ) . .Pp If run from a terminal, any .Xr termios 4 @@ -1209,7 +1232,7 @@ specified multiple times. .It Xo Ic refresh-client .Op Fl cDlLRSU .Op Fl C Ar XxY -.Op Fl F Ar flags +.Op Fl f Ar flags .Op Fl t Ar target-client .Op Ar adjustment .Xc @@ -1252,12 +1275,10 @@ window, changing the current window in the attached session will reset it. .Pp .Fl C -sets the width and height of a control client and -.Fl F -sets a comma-separated list of flags. -Currently the only flag available is -.Ql no-output -to disable receiving pane output. +sets the width and height of a control client. +.Fl f +sets a comma-separated list of client flags, see +.Ic attach-session . .Pp .Fl l requests the clipboard from the client using the @@ -1377,7 +1398,11 @@ or is used, the client is moved to the last, next or previous session respectively. .Fl r -toggles whether a client is read-only (see the +toggles the client +.Ic read-only +and +.Ic ignore-size +flags (see the .Ic attach-session command). .Pp @@ -4480,6 +4505,7 @@ The following variables are available, where appropriate: .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_created" Ta "" Ta "Time client created" .It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind" +.It Li "client_flags" Ta "" Ta "List of client flags" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_key_table" Ta "" Ta "Current key table" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" diff --git a/tmux.h b/tmux.h index cc327d8b..3da914c7 100644 --- a/tmux.h +++ b/tmux.h @@ -1570,7 +1570,7 @@ struct client { #define CLIENT_CONTROLCONTROL 0x4000 #define CLIENT_FOCUSED 0x8000 #define CLIENT_UTF8 0x10000 -/* 0x20000 unused */ +#define CLIENT_IGNORESIZE 0x20000 #define CLIENT_IDENTIFIED 0x40000 #define CLIENT_STATUSFORCE 0x80000 #define CLIENT_DOUBLECLICK 0x100000 @@ -2165,7 +2165,7 @@ char *cmd_template_replace(const char *, const char *, int); /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int, - int, const char *, int); + int, const char *, int, const char *); /* cmd-parse.c */ void cmd_parse_empty(struct cmd_parse_input *); @@ -2304,6 +2304,8 @@ void server_client_push_stderr(struct client *); void printflike(2, 3) server_client_add_message(struct client *, const char *, ...); const char *server_client_get_cwd(struct client *, struct session *); +void server_client_set_flags(struct client *, const char *); +const char *server_client_get_flags(struct client *); /* server-fn.c */ void server_redraw_client(struct client *); From c80fc6bf9e44baaad1e6f4a4c83ec47823e96c1b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 May 2020 16:33:58 +0100 Subject: [PATCH 0324/1006] Add M-+ and M-- to expand and collapse all items in tree mode. --- mode-tree.c | 12 +++++++++++- tmux.1 | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mode-tree.c b/mode-tree.c index 8d210d72..c08c802d 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -887,7 +887,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, struct mouse_event *m, u_int *xp, u_int *yp) { struct mode_tree_line *line; - struct mode_tree_item *current, *parent; + struct mode_tree_item *current, *parent, *mti; u_int i, x, y; int choice; key_code tmp; @@ -1058,6 +1058,16 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_build(mtd); } break; + case '-'|KEYC_ESCAPE: + TAILQ_FOREACH(mti, &mtd->children, entry) + mti->expanded = 0; + mode_tree_build(mtd); + break; + case '+'|KEYC_ESCAPE: + TAILQ_FOREACH(mti, &mtd->children, entry) + mti->expanded = 1; + mode_tree_build(mtd); + break; case '?': case '/': case '\023': /* C-s */ diff --git a/tmux.1 b/tmux.1 index 083b04b6..475cc20f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1903,6 +1903,10 @@ The following keys may be used in tree mode: .It Li "Enter" Ta "Choose selected item" .It Li "Up" Ta "Select previous item" .It Li "Down" Ta "Select next item" +.It Li "+" Ta "Expand selected item" +.It Li "-" Ta "Collapse selected item" +.It Li "M-+" Ta "Expand all items" +.It Li "M--" Ta "Collapse all items" .It Li "x" Ta "Kill selected item" .It Li "X" Ta "Kill tagged items" .It Li "<" Ta "Scroll list of previews left" From 7a95e9bf7ec80c7b87dda73749e59f359371d78a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 May 2020 13:43:22 +0100 Subject: [PATCH 0325/1006] Change message log to be per server rather than per client and include every command that is run. --- cmd-queue.c | 24 ++++++++++++++++++++++ cmd-show-messages.c | 25 +++++++++++++++-------- format.c | 4 +--- options-table.c | 2 +- server-client.c | 49 +-------------------------------------------- server.c | 35 ++++++++++++++++++++++++++++++++ status.c | 2 +- tmux.1 | 9 ++------- tmux.h | 18 +++++++++-------- 9 files changed, 92 insertions(+), 76 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 620a3e93..077599fb 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -534,6 +534,27 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs, return (CMD_RETURN_NORMAL); } +/* Add message with command. */ +static void +cmdq_add_message(struct cmdq_item *item) +{ + struct client *c = item->client; + struct cmdq_state *state = item->state; + const char *name = c->name, *key; + char *tmp; + + tmp = cmd_print(item->cmd); + if (c != NULL) { + if (c->session != NULL && state->event.key != KEYC_NONE) { + key = key_string_lookup_key(state->event.key); + server_add_message("%s key %s: %s", name, key, tmp); + } else + server_add_message("%s command: %s", name, tmp); + } else + server_add_message("command: %s", tmp); + free(tmp); +} + /* Fire command on command queue. */ static enum cmd_retval cmdq_fire_command(struct cmdq_item *item) @@ -549,6 +570,8 @@ cmdq_fire_command(struct cmdq_item *item) int flags, quiet = 0; char *tmp; + if (cfg_finished) + cmdq_add_message(item); if (log_get_level() > 1) { tmp = cmd_print(cmd); log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp); @@ -819,6 +842,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) cmd_get_source(cmd, &file, &line); cfg_add_cause("%s:%u: %s", file, line, msg); } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + server_add_message("%s message: %s", c->name, msg); if (~c->flags & CLIENT_UTF8) { tmp = msg; msg = utf8_sanitize(tmp); diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 02fdb9cd..d2f8289f 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -18,16 +18,19 @@ #include +#include #include -#include #include #include "tmux.h" /* - * Show client message log. + * Show message log. */ +#define SHOW_MESSAGES_TEMPLATE \ + "#{t/p:message_time}: #{message_text}" + static enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmdq_item *); @@ -71,10 +74,10 @@ static enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct client *tc = cmdq_get_target_client(item); struct message_entry *msg; - char *tim; + char *s; int done, blank; + struct format_tree *ft; done = blank = 0; if (args_has(args, 'T')) { @@ -88,11 +91,17 @@ cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) if (done) return (CMD_RETURN_NORMAL); - TAILQ_FOREACH(msg, &tc->message_log, entry) { - tim = ctime(&msg->msg_time); - *strchr(tim, '\n') = '\0'; - cmdq_print(item, "%s %s", tim, msg->msg); + ft = format_create_from_target(item); + TAILQ_FOREACH_REVERSE(msg, &message_log, message_list, entry) { + format_add(ft, "message_text", "%s", msg->msg); + format_add(ft, "message_number", "%u", msg->msg_num); + format_add_tv(ft, "message_time", &msg->msg_time); + + s = format_expand(ft, SHOW_MESSAGES_TEMPLATE); + cmdq_print(item, "%s", s); + free(s); } + format_free(ft); return (CMD_RETURN_NORMAL); } diff --git a/format.c b/format.c index f6adb51d..79c35875 100644 --- a/format.c +++ b/format.c @@ -46,8 +46,6 @@ static void format_job_timer(int, short, void *); static char *format_find(struct format_tree *, const char *, int); static void format_add_cb(struct format_tree *, const char *, format_cb); -static void format_add_tv(struct format_tree *, const char *, - struct timeval *); static int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); static void format_defaults_session(struct format_tree *, @@ -1260,7 +1258,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) } /* Add a key and time. */ -static void +void format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) { struct format_entry *fe, *fe_now; diff --git a/options-table.c b/options-table.c index b4aa47e2..be657694 100644 --- a/options-table.c +++ b/options-table.c @@ -252,7 +252,7 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, - .default_num = 100 + .default_num = 1000 }, { .name = "set-clipboard", diff --git a/server-client.c b/server-client.c index 6de6e97d..794cb41e 100644 --- a/server-client.c +++ b/server-client.c @@ -211,23 +211,12 @@ server_client_create(int fd) c->queue = cmdq_new(); c->tty.fd = -1; - c->title = NULL; - - c->session = NULL; - c->last_session = NULL; c->tty.sx = 80; c->tty.sy = 24; status_init(c); - c->message_string = NULL; - TAILQ_INIT(&c->message_log); - - c->prompt_string = NULL; - c->prompt_buffer = NULL; - c->prompt_index = 0; - RB_INIT(&c->files); c->flags |= CLIENT_FOCUSED; @@ -270,7 +259,6 @@ server_client_open(struct client *c, char **cause) void server_client_lost(struct client *c) { - struct message_entry *msg, *msg1; struct client_file *cf; c->flags |= CLIENT_DEAD; @@ -311,11 +299,6 @@ server_client_lost(struct client *c) free(c->message_string); if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); - TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { - free(msg->msg); - TAILQ_REMOVE(&c->message_log, msg, entry); - free(msg); - } free(c->prompt_saved); free(c->prompt_string); @@ -1126,6 +1109,7 @@ server_client_key_callback(struct cmdq_item *item, void *data) c->tty.mouse_drag_update(c, m); goto out; } + event->key = key; } /* Find affected pane. */ @@ -2206,37 +2190,6 @@ server_client_dispatch_read_done(struct client *c, struct imsg *imsg) file_fire_done(cf); } -/* Add to client message log. */ -void -server_client_add_message(struct client *c, const char *fmt, ...) -{ - struct message_entry *msg, *msg1; - char *s; - va_list ap; - u_int limit; - - va_start(ap, fmt); - xvasprintf(&s, fmt, ap); - va_end(ap); - - log_debug("message %s (client %p)", s, c); - - msg = xcalloc(1, sizeof *msg); - msg->msg_time = time(NULL); - msg->msg_num = c->message_next++; - msg->msg = s; - TAILQ_INSERT_TAIL(&c->message_log, msg, entry); - - limit = options_get_number(global_options, "message-limit"); - TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { - if (msg->msg_num + limit >= c->message_next) - break; - free(msg->msg); - TAILQ_REMOVE(&c->message_log, msg, entry); - free(msg); - } -} - /* Get client working directory. */ const char * server_client_get_cwd(struct client *c, struct session *s) diff --git a/server.c b/server.c index 1077377b..2f262f3c 100644 --- a/server.c +++ b/server.c @@ -50,6 +50,9 @@ static struct event server_ev_accept; struct cmd_find_state marked_pane; +static u_int message_next; +struct message_list message_log; + static int server_loop(void); static void server_send_exit(void); static void server_accept(int, short, void *); @@ -194,6 +197,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, TAILQ_INIT(&clients); RB_INIT(&sessions); key_bindings_init(); + TAILQ_INIT(&message_log); gettimeofday(&start_time, NULL); @@ -482,3 +486,34 @@ server_child_stopped(pid_t pid, int status) } job_check_died(pid, status); } + +/* Add to message log. */ +void +server_add_message(const char *fmt, ...) +{ + struct message_entry *msg, *msg1; + char *s; + va_list ap; + u_int limit; + + va_start(ap, fmt); + xvasprintf(&s, fmt, ap); + va_end(ap); + + log_debug("message: %s", s); + + msg = xcalloc(1, sizeof *msg); + gettimeofday(&msg->msg_time, NULL); + msg->msg_num = message_next++; + msg->msg = s; + TAILQ_INSERT_TAIL(&message_log, msg, entry); + + limit = options_get_number(global_options, "message-limit"); + TAILQ_FOREACH_SAFE(msg, &message_log, entry, msg1) { + if (msg->msg_num + limit >= message_next) + break; + free(msg->msg); + TAILQ_REMOVE(&message_log, msg, entry); + free(msg); + } +} diff --git a/status.c b/status.c index b5442550..375cad4d 100644 --- a/status.c +++ b/status.c @@ -436,7 +436,7 @@ status_message_set(struct client *c, const char *fmt, ...) xvasprintf(&c->message_string, fmt, ap); va_end(ap); - server_client_add_message(c, "%s", c->message_string); + server_add_message("%s message: %s", c->name, c->message_string); delay = options_get_number(c->session->options, "display-time"); if (delay > 0) { diff --git a/tmux.1 b/tmux.1 index 475cc20f..2859062e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1311,15 +1311,10 @@ Rename the session to .Op Fl t Ar target-client .Xc .D1 (alias: Ic showmsgs ) -Show client messages or server information. -Any messages displayed on the status line are saved in a per-client message -log, up to a maximum of the limit set by the +Show server messages or information. +Messages are stored, up to a maximum of the limit set by the .Ar message-limit server option. -With -.Fl t , -display the log for -.Ar target-client . .Fl J and .Fl T diff --git a/tmux.h b/tmux.h index 3da914c7..d038e24b 100644 --- a/tmux.h +++ b/tmux.h @@ -1343,11 +1343,13 @@ struct tty_ctx { /* Saved message entry. */ struct message_entry { - char *msg; - u_int msg_num; - time_t msg_time; - TAILQ_ENTRY(message_entry) entry; + char *msg; + u_int msg_num; + struct timeval msg_time; + + TAILQ_ENTRY(message_entry) entry; }; +TAILQ_HEAD(message_list, message_entry); /* Parsed arguments structures. */ struct args_entry; @@ -1605,8 +1607,6 @@ struct client { char *message_string; struct event message_timer; - u_int message_next; - TAILQ_HEAD(, message_entry) message_log; char *prompt_string; struct utf8_data *prompt_buffer; @@ -1848,6 +1848,8 @@ void format_free(struct format_tree *); void format_merge(struct format_tree *, struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); +void format_add_tv(struct format_tree *, const char *, + struct timeval *); void format_each(struct format_tree *, void (*)(const char *, const char *, void *), void *); char *format_expand_time(struct format_tree *, const char *); @@ -2271,6 +2273,7 @@ void file_push(struct client_file *); extern struct tmuxproc *server_proc; extern struct clients clients; extern struct cmd_find_state marked_pane; +extern struct message_list message_log; void server_set_marked(struct session *, struct winlink *, struct window_pane *); void server_clear_marked(void); @@ -2280,6 +2283,7 @@ int server_check_marked(void); int server_start(struct tmuxproc *, int, struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); +void printflike(1, 2) server_add_message(const char *, ...); /* server-client.c */ u_int server_client_how_many(void); @@ -2301,8 +2305,6 @@ void server_client_exec(struct client *, const char *); void server_client_loop(void); void server_client_push_stdout(struct client *); void server_client_push_stderr(struct client *); -void printflike(2, 3) server_client_add_message(struct client *, const char *, - ...); const char *server_client_get_cwd(struct client *, struct session *); void server_client_set_flags(struct client *, const char *); const char *server_client_get_flags(struct client *); From 66ecb1dff47952cdd0c4e1b36f4ad5fcc4f43c83 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 May 2020 15:48:27 +0100 Subject: [PATCH 0326/1006] Update CHANGES. --- CHANGES | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/CHANGES b/CHANGES index aeae71b1..e5e966ed 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,40 @@ CHANGES FROM 3.1b TO 3.2 +* Change message log (C-b ~) so there is one for the server rather than one per + client and it remains after detach, and make it useful by logging every + command. + +* Add M-+ and M-- to tree mode to expand and collapse all. + +* Change the existing client flags for control mode to apply for any client, + use the same mechanism for the read-only flag and add an ignore-size flag. + + refresh-client -F has become -f (-F stays for backwards compatibility) and + attach-session and switch-client now have -f flags also. A new format + "client_flags" lists the flags and is shown by list-clients by default. + + This separates the read-only flag from "ignore size" behaviour (new + ignore-size) flag - both behaviours are useful in different circumstances. + + attach -r and switchc -r remain and set or toggle both flags together. + +* Store and restore cursor position when copy mode is resized. + +* Export TERM_PROGRAM and TERM_PROGRAM_VERSION like various other terminals. + +* Add formats for after hook command arguments: hook_arguments with all the + arguments together; hook_argument_0, hook_argument_1 and so on with + individual arguments; hook_flag_X if flag -X is present; hook_flag_X_0, + hook_flag_X_1 and so on if -X appears multiple times. + +* Try to search the entire history first for up to 200 ms so a search count can + be shown. If it takes too long, search the visible text only. + +* Use VIS_CSTYLE for paste buffers also (show \012 as \n). + +* Change default formats for tree mode, client mode and buffer mode to be more + compact and remove some clutter. + * Add a key (e) in buffer mode to open the buffer in an editor. The buffer contents is updated when the editor exits. From 63e17d8cad7be6932ac82703e632f9a91d89b959 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 7 May 2020 05:55:42 +0100 Subject: [PATCH 0327/1006] Do not use client if NULL, from Thomas Adam. --- cmd-queue.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd-queue.c b/cmd-queue.c index 077599fb..26e2f2f9 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -540,11 +540,12 @@ cmdq_add_message(struct cmdq_item *item) { struct client *c = item->client; struct cmdq_state *state = item->state; - const char *name = c->name, *key; + const char *name, *key; char *tmp; tmp = cmd_print(item->cmd); if (c != NULL) { + name = c->name; if (c->session != NULL && state->event.key != KEYC_NONE) { key = key_string_lookup_key(state->event.key); server_add_message("%s key %s: %s", name, key, tmp); From b0fa36734e2da81a7f575f41aae399ab6a9e45a0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 7 May 2020 09:55:06 +0100 Subject: [PATCH 0328/1006] Fix pretty time function to actually work and allow time format to be applied to any string that is suitable. --- format.c | 117 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 62 insertions(+), 55 deletions(-) diff --git a/format.c b/format.c index 79c35875..c3a655dd 100644 --- a/format.c +++ b/format.c @@ -1326,45 +1326,44 @@ static char * format_pretty_time(time_t t) { struct tm now_tm, tm; - time_t now; + time_t now, age; char s[6]; - int y, m, d; + int m; time(&now); if (now < t) now = t; + age = now - t; + localtime_r(&now, &now_tm); localtime_r(&t, &tm); - y = now_tm.tm_year - 1; - if (tm.tm_year < y || - (tm.tm_year == y && - (tm.tm_mon <= now_tm.tm_mon || tm.tm_mday <= now_tm.tm_mday))) { - /* Last year. */ - strftime(s, sizeof s, "%h%y", &tm); + /* Last 24 hours. */ + if (age < 24 * 3600) { + strftime(s, sizeof s, "%H:%M", &tm); return (xstrdup(s)); } + + /* This month or last 28 days. */ + if ((tm.tm_year == now_tm.tm_year && tm.tm_mon == now_tm.tm_mon) || + age < 28 * 24 * 3600) { + strftime(s, sizeof s, "%a%d", &tm); + return (xstrdup(s)); + } + + /* Last 12 months. */ if (now_tm.tm_mon == 0) m = 11; else m = now_tm.tm_mon - 1; - if (tm.tm_mon < m || (tm.tm_mon == m && tm.tm_mday < now_tm.tm_mday)) { - /* Last month. */ + 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); return (xstrdup(s)); } - if (now_tm.tm_mday == 0) - d = 31; - else - d = now_tm.tm_mday - 1; - if (tm.tm_mday < d || - (tm.tm_mday == d && tm.tm_mday < now_tm.tm_mday)) { - /* This day. */ - strftime(s, sizeof s, "%a%d", &tm); - return (xstrdup(s)); - } - /* Today. */ - strftime(s, sizeof s, "%H:%M", &tm); + + /* Older than that. */ + strftime(s, sizeof s, "%h%y", &tm); return (xstrdup(s)); } @@ -1377,44 +1376,31 @@ format_find(struct format_tree *ft, const char *key, int modifiers) static char s[64]; struct options_entry *o; int idx; - char *found, *saved; + char *found = NULL, *saved; + const char *errstr; + time_t t = 0; - if (~modifiers & FORMAT_TIMESTRING) { - o = options_parse_get(global_options, key, &idx, 0); - if (o == NULL && ft->wp != NULL) - o = options_parse_get(ft->wp->options, key, &idx, 0); - if (o == NULL && ft->w != NULL) - o = options_parse_get(ft->w->options, key, &idx, 0); - if (o == NULL) - o = options_parse_get(global_w_options, key, &idx, 0); - if (o == NULL && ft->s != NULL) - o = options_parse_get(ft->s->options, key, &idx, 0); - if (o == NULL) - o = options_parse_get(global_s_options, key, &idx, 0); - if (o != NULL) { - found = options_tostring(o, idx, 1); - goto found; - } + o = options_parse_get(global_options, key, &idx, 0); + if (o == NULL && ft->wp != NULL) + o = options_parse_get(ft->wp->options, key, &idx, 0); + if (o == NULL && ft->w != NULL) + o = options_parse_get(ft->w->options, key, &idx, 0); + if (o == NULL) + o = options_parse_get(global_w_options, key, &idx, 0); + if (o == NULL && ft->s != NULL) + o = options_parse_get(ft->s->options, key, &idx, 0); + if (o == NULL) + o = options_parse_get(global_s_options, key, &idx, 0); + if (o != NULL) { + found = options_tostring(o, idx, 1); + goto found; } - found = NULL; - fe_find.key = (char *) key; + fe_find.key = (char *)key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe != NULL) { - if (modifiers & FORMAT_TIMESTRING) { - if (fe->t == 0) - return (NULL); - if (modifiers & FORMAT_PRETTY) - found = format_pretty_time(fe->t); - else { - ctime_r(&fe->t, s); - s[strcspn(s, "\n")] = '\0'; - found = xstrdup(s); - } - goto found; - } if (fe->t != 0) { - xasprintf(&found, "%lld", (long long)fe->t); + t = fe->t; goto found; } if (fe->value == NULL && fe->cb != NULL) @@ -1440,7 +1426,28 @@ format_find(struct format_tree *ft, const char *key, int modifiers) return (NULL); found: - if (found == NULL) + if (modifiers & FORMAT_TIMESTRING) { + if (t == 0 && found != NULL) { + t = strtonum(found, 0, INT64_MAX, &errstr); + if (errstr != NULL) + t = 0; + free(found); + } + if (t == 0) + return (NULL); + if (modifiers & FORMAT_PRETTY) + found = format_pretty_time(t); + else { + ctime_r(&t, s); + s[strcspn(s, "\n")] = '\0'; + found = xstrdup(s); + } + return (found); + } + + if (t != 0) + xasprintf(&found, "%lld", (long long)t); + else if (found == NULL) return (NULL); if (modifiers & FORMAT_BASENAME) { saved = found; From 708e9bc072eddc2a3d83bc5df45de2825b011ed1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 7 May 2020 11:04:43 +0100 Subject: [PATCH 0329/1006] Allow a custom time format to be given to the t format modifier. --- format.c | 55 ++++++++++++++++++++++++++++++++++++++++++++----------- tmux.1 | 15 +++++++++++++++ 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/format.c b/format.c index c3a655dd..2c3e9046 100644 --- a/format.c +++ b/format.c @@ -44,7 +44,6 @@ typedef void (*format_cb)(struct format_tree *, struct format_entry *); static char *format_job_get(struct format_tree *, const char *); static void format_job_timer(int, short, void *); -static char *format_find(struct format_tree *, const char *, int); static void format_add_cb(struct format_tree *, const char *, format_cb); static int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); @@ -122,8 +121,8 @@ struct format_tree { struct cmdq_item *item; struct client *client; - u_int tag; int flags; + u_int tag; time_t time; u_int loop; @@ -1369,16 +1368,17 @@ format_pretty_time(time_t t) /* Find a format entry. */ static char * -format_find(struct format_tree *ft, const char *key, int modifiers) +format_find(struct format_tree *ft, const char *key, int modifiers, + const char *time_format) { struct format_entry *fe, fe_find; struct environ_entry *envent; - static char s[64]; struct options_entry *o; int idx; - char *found = NULL, *saved; + char *found = NULL, *saved, s[512]; const char *errstr; time_t t = 0; + struct tm tm; o = options_parse_get(global_options, key, &idx, 0); if (o == NULL && ft->wp != NULL) @@ -1438,8 +1438,13 @@ found: if (modifiers & FORMAT_PRETTY) found = format_pretty_time(t); else { - ctime_r(&t, s); - s[strcspn(s, "\n")] = '\0'; + if (time_format != NULL) { + localtime_r(&t, &tm); + strftime(s, sizeof s, time_format, &tm); + } else { + ctime_r(&t, s); + s[strcspn(s, "\n")] = '\0'; + } found = xstrdup(s); } return (found); @@ -1467,6 +1472,30 @@ found: return (found); } +/* Remove escaped characters from string. */ +static char * +format_strip(const char *s) +{ + char *out, *cp; + int brackets = 0; + + cp = out = xmalloc(strlen(s) + 1); + for (; *s != '\0'; s++) { + if (*s == '#' && s[1] == '{') + brackets++; + if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { + if (brackets != 0) + *cp++ = *s; + continue; + } + if (*s == '}') + brackets--; + *cp++ = *s; + } + *cp = '\0'; + return (out); +} + /* Skip until end. */ const char * format_skip(const char *s, const char *end) @@ -1476,7 +1505,7 @@ format_skip(const char *s, const char *end) for (; *s != '\0'; s++) { if (*s == '#' && s[1] == '{') brackets++; - if (*s == '#' && strchr(",#{}", s[1]) != NULL) { + if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { s++; continue; } @@ -1584,7 +1613,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) *count = 0; while (*cp != '\0' && *cp != ':') { - /* Skip and separator character. */ + /* Skip any separator character. */ if (*cp == ';') cp++; @@ -1975,6 +2004,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, { 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; @@ -2052,6 +2082,9 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, break; if (strchr(fm->argv[0], 'p') != NULL) modifiers |= FORMAT_PRETTY; + else if (fm->argc >= 2 && + strchr(fm->argv[0], 'f') != NULL) + time_format = format_strip(fm->argv[1]); break; case 'q': modifiers |= FORMAT_QUOTE; @@ -2178,7 +2211,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, condition = xstrndup(copy + 1, cp - (copy + 1)); format_log(ft, "condition is: %s", condition); - found = format_find(ft, condition, modifiers); + found = format_find(ft, condition, modifiers, time_format); if (found == NULL) { /* * If the condition not found, try to expand it. If @@ -2223,7 +2256,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, value = xstrdup(""); } else { /* Neither: look up directly. */ - value = format_find(ft, copy, modifiers); + value = format_find(ft, copy, modifiers, time_format); if (value == NULL) { format_log(ft, "format '%s' not found", copy); value = xstrdup(""); diff --git a/tmux.1 b/tmux.1 index 2859062e..867d24e7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4407,6 +4407,21 @@ Adding .Ql p ( .Ql `t/p` ) will use shorter but less accurate time format for times in the past. +A custom format may be given using an +.Ql f +suffix (note that +.Ql % +must be escaped as +.Ql %% +if the format is separately being passed through +.Xr strftime 3 , +for example in the +.Ic status-left +option): +.Ql #{t/f/%%H#:%%M:window_activity} , +see +.Xr strftime 3 . +.Pp The .Ql b:\& and From aa7dccf8e12c33223717d10551567959a9f22b97 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2020 14:15:11 +0000 Subject: [PATCH 0330/1006] imsg.h needs uio.h, pointed out by deraadt --- client.c | 1 + file.c | 1 + 2 files changed, 2 insertions(+) diff --git a/client.c b/client.c index b3519690..841e581e 100644 --- a/client.c +++ b/client.c @@ -18,6 +18,7 @@ #include #include +#include #include #include diff --git a/file.c b/file.c index 439d3464..69dbdee4 100644 --- a/file.c +++ b/file.c @@ -18,6 +18,7 @@ #include #include +#include #include #include From a61cbf1c33d8d61b564f95053cbf96443126dd87 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 8 May 2020 19:10:09 +0100 Subject: [PATCH 0331/1006] Add a customize mode where options may be browsed and changed, includes adding a brief description of each option. Bound to "C" by default. --- Makefile.am | 1 + cmd-choose-tree.c | 17 +- cmd-set-option.c | 212 +-------- cmd-show-options.c | 8 +- cmd.c | 2 + format-draw.c | 2 +- format.c | 2 +- key-bindings.c | 1 + mode-tree.c | 68 ++- options-table.c | 358 +++++++++++----- options.c | 251 ++++++++++- screen-write.c | 74 +++- style.c | 7 +- tmux.1 | 47 ++ tmux.h | 28 +- window-buffer.c | 4 +- window-client.c | 2 +- window-customize.c | 1013 ++++++++++++++++++++++++++++++++++++++++++++ window-tree.c | 2 +- 19 files changed, 1754 insertions(+), 345 deletions(-) create mode 100644 window-customize.c diff --git a/Makefile.am b/Makefile.am index a395ecdb..fdd39107 100644 --- a/Makefile.am +++ b/Makefile.am @@ -180,6 +180,7 @@ dist_tmux_SOURCES = \ window-client.c \ window-clock.c \ window-copy.c \ + window-customize.c \ window-tree.c \ window.c \ xmalloc.c \ diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 0ada8fd4..a58469ac 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -68,6 +68,19 @@ const struct cmd_entry cmd_choose_buffer_entry = { .exec = cmd_choose_tree_exec }; +const struct cmd_entry cmd_customize_mode_entry = { + .name = "customize-mode", + .alias = NULL, + + .args = { "F:f:Nt:Z", 0, 0 }, + .usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE, + + .target = { 't', CMD_FIND_PANE, 0 }, + + .flags = 0, + .exec = cmd_choose_tree_exec +}; + static enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) { @@ -84,7 +97,9 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) if (server_client_how_many() == 0) return (CMD_RETURN_NORMAL); mode = &window_client_mode; - } else + } else if (cmd_get_entry(self) == &cmd_customize_mode_entry) + mode = &window_customize_mode; + else mode = &window_tree_mode; window_pane_set_mode(wp, NULL, mode, target, args); diff --git a/cmd-set-option.c b/cmd-set-option.c index e04aa7ff..6f4637d7 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -30,15 +30,6 @@ static enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmdq_item *); -static int cmd_set_option_set(struct cmd *, struct cmdq_item *, - struct options *, struct options_entry *, const char *); -static int cmd_set_option_flag(struct cmdq_item *, - const struct options_table_entry *, struct options *, - const char *); -static int cmd_set_option_choice(struct cmdq_item *, - const struct options_table_entry *, struct options *, - const char *); - const struct cmd_entry cmd_set_option_entry = { .name = "set-option", .alias = "set", @@ -138,7 +129,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) parent = options_get(oo, name); /* Check that array options and indexes match up. */ - if (idx != -1 && (*name == '@' || !options_isarray(parent))) { + if (idx != -1 && (*name == '@' || !options_is_array(parent))) { cmdq_error(item, "not an array: %s", argument); goto fail; } @@ -185,10 +176,15 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) goto fail; } options_set_string(oo, name, append, "%s", value); - } else if (idx == -1 && !options_isarray(parent)) { - error = cmd_set_option_set(self, item, oo, parent, value); - if (error != 0) + } else if (idx == -1 && !options_is_array(parent)) { + error = options_from_string(oo, options_table_entry(parent), + options_table_entry(parent)->name, value, + args_has(args, 'a'), &cause); + if (error != 0) { + cmdq_error(item, "%s", cause); + free(cause); goto fail; + } } else { if (value == NULL) { cmdq_error(item, "empty value"); @@ -212,51 +208,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) } } - /* Update timers and so on for various options. */ - if (strcmp(name, "automatic-rename") == 0) { - RB_FOREACH(w, windows, &windows) { - if (w->active == NULL) - continue; - if (options_get_number(w->options, "automatic-rename")) - w->active->flags |= PANE_CHANGED; - } - } - if (strcmp(name, "key-table") == 0) { - TAILQ_FOREACH(loop, &clients, entry) - server_client_set_key_table(loop, NULL); - } - if (strcmp(name, "user-keys") == 0) { - TAILQ_FOREACH(loop, &clients, entry) { - if (loop->tty.flags & TTY_OPENED) - tty_keys_build(&loop->tty); - } - } - if (strcmp(name, "status") == 0 || - strcmp(name, "status-interval") == 0) - status_timer_start_all(); - if (strcmp(name, "monitor-silence") == 0) - alerts_reset_all(); - if (strcmp(name, "window-style") == 0 || - strcmp(name, "window-active-style") == 0) { - RB_FOREACH(wp, window_pane_tree, &all_window_panes) - wp->flags |= PANE_STYLECHANGED; - } - if (strcmp(name, "pane-border-status") == 0) { - RB_FOREACH(w, windows, &windows) - layout_fix_panes(w); - } - RB_FOREACH(s, sessions, &sessions) - status_update_cache(s); - - /* - * Update sizes and redraw. May not always be necessary but do it - * anyway. - */ - recalculate_sizes(); - TAILQ_FOREACH(loop, &clients, entry) { - if (loop->session != NULL) - server_redraw_client(loop); - } + options_push_changes(name); out: free(argument); @@ -270,147 +222,3 @@ fail: free(name); return (CMD_RETURN_ERROR); } - -static int -cmd_set_option_check_string(const struct options_table_entry *oe, - const char *value, char **cause) -{ - struct style sy; - - if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) { - xasprintf(cause, "not a suitable shell: %s", value); - return (-1); - } - if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) { - xasprintf(cause, "value is invalid: %s", value); - return (-1); - } - if ((oe->flags & OPTIONS_TABLE_IS_STYLE) && - strstr(value, "#{") == NULL && - style_parse(&sy, &grid_default_cell, value) != 0) { - xasprintf(cause, "invalid style: %s", value); - return (-1); - } - return (0); -} - -static int -cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, - struct options_entry *parent, const char *value) -{ - const struct options_table_entry *oe; - struct args *args = cmd_get_args(self); - int append = args_has(args, 'a'); - long long number; - const char *errstr, *new; - char *old, *cause; - key_code key; - - oe = options_table_entry(parent); - if (value == NULL && - oe->type != OPTIONS_TABLE_FLAG && - oe->type != OPTIONS_TABLE_CHOICE) { - cmdq_error(item, "empty value"); - return (-1); - } - - switch (oe->type) { - case OPTIONS_TABLE_STRING: - old = xstrdup(options_get_string(oo, oe->name)); - options_set_string(oo, oe->name, append, "%s", value); - new = options_get_string(oo, oe->name); - if (cmd_set_option_check_string(oe, new, &cause) != 0) { - cmdq_error(item, "%s", cause); - free(cause); - - options_set_string(oo, oe->name, 0, "%s", old); - free(old); - return (-1); - } - free(old); - return (0); - case OPTIONS_TABLE_NUMBER: - number = strtonum(value, oe->minimum, oe->maximum, &errstr); - if (errstr != NULL) { - cmdq_error(item, "value is %s: %s", errstr, value); - return (-1); - } - options_set_number(oo, oe->name, number); - return (0); - case OPTIONS_TABLE_KEY: - key = key_string_lookup_string(value); - if (key == KEYC_UNKNOWN) { - cmdq_error(item, "bad key: %s", value); - return (-1); - } - options_set_number(oo, oe->name, key); - return (0); - case OPTIONS_TABLE_COLOUR: - if ((number = colour_fromstring(value)) == -1) { - cmdq_error(item, "bad colour: %s", value); - return (-1); - } - options_set_number(oo, oe->name, number); - return (0); - case OPTIONS_TABLE_FLAG: - return (cmd_set_option_flag(item, oe, oo, value)); - case OPTIONS_TABLE_CHOICE: - return (cmd_set_option_choice(item, oe, oo, value)); - case OPTIONS_TABLE_COMMAND: - break; - } - return (-1); -} - -static int -cmd_set_option_flag(struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - int flag; - - if (value == NULL || *value == '\0') - flag = !options_get_number(oo, oe->name); - else if (strcmp(value, "1") == 0 || - strcasecmp(value, "on") == 0 || - strcasecmp(value, "yes") == 0) - flag = 1; - else if (strcmp(value, "0") == 0 || - strcasecmp(value, "off") == 0 || - strcasecmp(value, "no") == 0) - flag = 0; - else { - cmdq_error(item, "bad value: %s", value); - return (-1); - } - options_set_number(oo, oe->name, flag); - return (0); -} - -static int -cmd_set_option_choice(struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - const char **cp; - int n, choice = -1; - - if (value == NULL) { - choice = options_get_number(oo, oe->name); - if (choice < 2) - choice = !choice; - } else { - n = 0; - for (cp = oe->choices; *cp != NULL; cp++) { - if (strcmp(*cp, value) == 0) - choice = n; - n++; - } - if (choice == -1) { - cmdq_error(item, "unknown value: %s", value); - return (-1); - } - } - options_set_number(oo, oe->name, choice); - return (0); -} diff --git a/cmd-show-options.c b/cmd-show-options.c index 1286037c..ed93311a 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -150,7 +150,7 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, xasprintf(&tmp, "%s[%d]", name, idx); name = tmp; } else { - if (options_isarray(o)) { + if (options_is_array(o)) { a = options_array_first(o); if (a == NULL) { if (!args_has(args, 'v')) @@ -167,10 +167,10 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, } } - value = options_tostring(o, idx, 0); + value = options_to_string(o, idx, 0); if (args_has(args, 'v')) cmdq_print(item, "%s", value); - else if (options_isstring(o)) { + else if (options_is_string(o)) { escaped = args_escape(value); if (parent) cmdq_print(item, "%s* %s", name, escaped); @@ -228,7 +228,7 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, } else parent = 0; - if (!options_isarray(o)) + if (!options_is_array(o)) cmd_show_options_print(self, item, o, -1, parent); else if ((a = options_array_first(o)) == NULL) { if (!args_has(args, 'v')) { diff --git a/cmd.c b/cmd.c index 9cd5ab49..09483680 100644 --- a/cmd.c +++ b/cmd.c @@ -39,6 +39,7 @@ extern const struct cmd_entry cmd_clock_mode_entry; extern const struct cmd_entry cmd_command_prompt_entry; extern const struct cmd_entry cmd_confirm_before_entry; extern const struct cmd_entry cmd_copy_mode_entry; +extern const struct cmd_entry cmd_customize_mode_entry; extern const struct cmd_entry cmd_delete_buffer_entry; extern const struct cmd_entry cmd_detach_client_entry; extern const struct cmd_entry cmd_display_menu_entry; @@ -129,6 +130,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_command_prompt_entry, &cmd_confirm_before_entry, &cmd_copy_mode_entry, + &cmd_customize_mode_entry, &cmd_delete_buffer_entry, &cmd_detach_client_entry, &cmd_display_menu_entry, diff --git a/format-draw.c b/format-draw.c index 3751082e..bd32b2a8 100644 --- a/format-draw.c +++ b/format-draw.c @@ -547,7 +547,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, */ cp = expanded; while (*cp != '\0') { - if (cp[0] != '#' || cp[1] != '[') { + if (cp[0] != '#' || cp[1] != '[' || sy.ignore) { /* See if this is a UTF-8 character. */ if ((more = utf8_open(ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) diff --git a/format.c b/format.c index 2c3e9046..3af7ae69 100644 --- a/format.c +++ b/format.c @@ -1392,7 +1392,7 @@ format_find(struct format_tree *ft, const char *key, int modifiers, if (o == NULL) o = options_parse_get(global_s_options, key, &idx, 0); if (o != NULL) { - found = options_tostring(o, idx, 1); + found = options_to_string(o, idx, 1); goto found; } diff --git a/key-bindings.c b/key-bindings.c index 85bfb788..1847b6e6 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -278,6 +278,7 @@ key_bindings_init(void) "bind -N 'Toggle the marked pane' m select-pane -m", "bind -N 'Select the next window' n next-window", "bind -N 'Select the next pane' o select-pane -t:.+", + "bind -N 'Customizeoptions' C customize-mode", "bind -N 'Select the previous pane' p previous-window", "bind -N 'Display pane numbers' q display-panes", "bind -N 'Redraw the current client' r refresh-client", diff --git a/mode-tree.c b/mode-tree.c index c08c802d..8f7ba85f 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -45,6 +45,7 @@ struct mode_tree_data { mode_tree_draw_cb drawcb; mode_tree_search_cb searchcb; mode_tree_menu_cb menucb; + mode_tree_height_cb heightcb; struct mode_tree_list children; struct mode_tree_list saved; @@ -210,7 +211,7 @@ mode_tree_clear_tagged(struct mode_tree_list *mtl) } } -static void +void mode_tree_up(struct mode_tree_data *mtd, int wrap) { if (mtd->current == 0) { @@ -343,7 +344,8 @@ mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb, struct mode_tree_data * mode_tree_start(struct window_pane *wp, struct args *args, mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb, - mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, void *modedata, + mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, + mode_tree_height_cb heightcb, void *modedata, const struct menu_item *menu, const char **sort_list, u_int sort_size, struct screen **s) { @@ -381,6 +383,7 @@ mode_tree_start(struct window_pane *wp, struct args *args, mtd->drawcb = drawcb; mtd->searchcb = searchcb; mtd->menucb = menucb; + mtd->heightcb = heightcb; TAILQ_INIT(&mtd->children); @@ -404,6 +407,27 @@ mode_tree_zoom(struct mode_tree_data *mtd, struct args *args) mtd->zoomed = -1; } +static void +mode_tree_set_height(struct mode_tree_data *mtd) +{ + struct screen *s = &mtd->screen; + u_int height; + + if (mtd->heightcb != NULL) { + height = mtd->heightcb(mtd, screen_size_y(s)); + if (height < screen_size_y(s)) + mtd->height = screen_size_y(s) - height; + } else { + mtd->height = (screen_size_y(s) / 3) * 2; + if (mtd->height > mtd->line_size) + mtd->height = screen_size_y(s) / 2; + } + if (mtd->height < 10) + mtd->height = screen_size_y(s); + if (screen_size_y(s) - mtd->height < 2) + mtd->height = screen_size_y(s); +} + void mode_tree_build(struct mode_tree_data *mtd) { @@ -434,15 +458,9 @@ mode_tree_build(struct mode_tree_data *mtd) mode_tree_set_current(mtd, tag); mtd->width = screen_size_x(s); - if (mtd->preview) { - mtd->height = (screen_size_y(s) / 3) * 2; - if (mtd->height > mtd->line_size) - mtd->height = screen_size_y(s) / 2; - if (mtd->height < 10) - mtd->height = screen_size_y(s); - if (screen_size_y(s) - mtd->height < 2) - mtd->height = screen_size_y(s); - } else + if (mtd->preview) + mode_tree_set_height(mtd); + else mtd->height = screen_size_y(s); mode_tree_check_selected(mtd); } @@ -502,7 +520,8 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, mti->tag = tag; mti->name = xstrdup(name); - mti->text = xstrdup(text); + if (text != NULL) + mti->text = xstrdup(text); saved = mode_tree_find_item(&mtd->saved, tag); if (saved != NULL) { @@ -621,8 +640,8 @@ mode_tree_draw(struct mode_tree_data *mtd) tag = "*"; else tag = ""; - xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name, - tag); + xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name, + tag, (mti->text != NULL) ? ": " : "" ); width = utf8_cstrwidth(text); if (width > w) width = w; @@ -636,11 +655,17 @@ mode_tree_draw(struct mode_tree_data *mtd) if (i != mtd->current) { screen_write_clearendofline(&ctx, 8); screen_write_nputs(&ctx, w, &gc0, "%s", text); - format_draw(&ctx, &gc0, w - width, mti->text, NULL); + if (mti->text != NULL) { + format_draw(&ctx, &gc0, w - width, mti->text, + NULL); + } } else { screen_write_clearendofline(&ctx, gc.bg); screen_write_nputs(&ctx, w, &gc, "%s", text); - format_draw(&ctx, &gc, w - width, mti->text, NULL); + if (mti->text != NULL) { + format_draw(&ctx, &gc, w - width, mti->text, + NULL); + } } free(text); @@ -662,9 +687,12 @@ mode_tree_draw(struct mode_tree_data *mtd) screen_write_cursormove(&ctx, 0, h, 0); screen_write_box(&ctx, w, sy - h); - xasprintf(&text, " %s (sort: %s%s)", mti->name, - mtd->sort_list[mtd->sort_crit.field], - mtd->sort_crit.reversed ? ", reversed" : ""); + if (mtd->sort_list != NULL) { + xasprintf(&text, " %s (sort: %s%s)", mti->name, + mtd->sort_list[mtd->sort_crit.field], + mtd->sort_crit.reversed ? ", reversed" : ""); + } else + xasprintf(&text, " %s", mti->name); if (w - 2 >= strlen(text)) { screen_write_cursormove(&ctx, 1, h, 0); screen_write_puts(&ctx, &gc0, "%s", text); @@ -1027,7 +1055,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, break; case 'O': mtd->sort_crit.field++; - if (mtd->sort_crit.field == mtd->sort_size) + if (mtd->sort_crit.field >= mtd->sort_size) mtd->sort_crit.field = 0; mode_tree_build(mtd); break; diff --git a/options-table.c b/options-table.c index be657694..c3a9f23b 100644 --- a/options-table.c +++ b/options-table.c @@ -102,9 +102,9 @@ static const char *options_table_window_size_list[] = { "," \ "#[range=window|#{window_index} list=focus " \ "#{?#{!=:#{window-status-current-style},default}," \ - "#{window-status-current-style}," \ - "#{window-status-style}" \ - "}" \ + "#{window-status-current-style}," \ + "#{window-status-style}" \ + "}" \ "#{?#{&&:#{window_last_flag}," \ "#{!=:#{window-status-last-style},default}}, " \ "#{window-status-last-style}," \ @@ -174,6 +174,7 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_KEY, .scope = OPTIONS_TABLE_SERVER, .default_num = '\177', + .text = "The key to send for backspace." }, { .name = "buffer-limit", @@ -181,7 +182,9 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .minimum = 1, .maximum = INT_MAX, - .default_num = 50 + .default_num = 50, + .text = "The maximum number of automatic buffers. " + "When this is reached, the oldest buffer is deleted." }, { .name = "command-alias", @@ -194,25 +197,31 @@ const struct options_table_entry options_table[] = { "info=show-messages -JT," "choose-window=choose-tree -w," "choose-session=choose-tree -s", - .separator = "," + .separator = ",", + .text = "Array of command aliases. " + "Each entry is an alias and a command separated by '='." }, { .name = "copy-command", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "" + .default_str = "", + .text = "Shell command run when text is copied. " + "If empty, no command is run." }, { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "screen" + .default_str = "screen", + .text = "Default for the 'TERM' environment variable." }, { .name = "editor", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = _PATH_VI + .default_str = _PATH_VI, + .text = "Editor run to edit files." }, { .name = "escape-time", @@ -220,31 +229,38 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, - .default_num = 500 + .default_num = 500, + .text = "Time to wait before assuming a key is Escape." }, { .name = "exit-empty", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, - .default_num = 1 + .default_num = 1, + .text = "Whether the server should exit if there are no sessions." }, { .name = "exit-unattached", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, - .default_num = 0 + .default_num = 0, + .text = "Whether the server should exit if there are no attached " + "clients." }, { .name = "focus-events", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, - .default_num = 0 + .default_num = 0, + .text = "Whether to send focus events to applications." }, { .name = "history-file", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "" + .default_str = "", + .text = "Location of the command prompt history file. " + "Empty does not write a history file." }, { .name = "message-limit", @@ -252,14 +268,18 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, - .default_num = 1000 + .default_num = 1000, + .text = "Maximum number of server messages to keep." }, { .name = "set-clipboard", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SERVER, .choices = options_table_set_clipboard_list, - .default_num = 1 + .default_num = 1, + .text = "Whether to attempt to set the system clipboard ('on' or " + "'external') and whether to allow applications to create " + "paste buffers with an escape sequence ('on' only)." }, { .name = "terminal-overrides", @@ -267,7 +287,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "", - .separator = "," + .separator = ",", + .text = "List of terminal capabilities overrides." }, { .name = "terminal-features", @@ -275,8 +296,10 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "xterm*:clipboard:ccolour:cstyle:title," - "screen*:title", - .separator = "," + "screen*:title", + .separator = ",", + .text = "List of terminal features, used if they cannot be " + "automatically detected." }, { .name = "user-keys", @@ -284,7 +307,10 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "", - .separator = "," + .separator = ",", + .text = "User key assignments. " + "Each sequence in the list is translated into a key: " + "'User0', 'User1' and so on." }, /* Session options. */ @@ -292,7 +318,8 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, - .default_num = ALERT_OTHER + .default_num = ALERT_OTHER, + .text = "Action to take on an activity alert." }, { .name = "assume-paste-time", @@ -301,6 +328,9 @@ const struct options_table_entry options_table[] = { .minimum = 0, .maximum = INT_MAX, .default_num = 1, + .unit = "milliseconds", + .text = "Maximum time between input to assume it pasting rather " + "than typing." }, { .name = "base-index", @@ -308,57 +338,69 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, - .default_num = 0 + .default_num = 0, + .text = "Default index of the first window in each session." }, { .name = "bell-action", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, - .default_num = ALERT_ANY + .default_num = ALERT_ANY, + .text = "Action to take on a bell alert." }, { .name = "default-command", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "" + .default_str = "", + .text = "Default command to run in new panes. If empty, a shell is " + "started." }, { .name = "default-shell", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = _PATH_BSHELL + .default_str = _PATH_BSHELL, + .text = "Location of default shell." }, { .name = "default-size", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .pattern = "[0-9]*x[0-9]*", - .default_str = "80x24" + .default_str = "80x24", + .text = "Initial size of new sessions." }, { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, - .default_num = 0 + .default_num = 0, + .text = "Whether to destroy sessions when they have no attached " + "clients." }, { .name = "detach-on-destroy", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, - .default_num = 1 + .default_num = 1, + .text = "Whether to detach when a session is destroyed, or switch " + "the client to another session if any exist." }, { .name = "display-panes-active-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, - .default_num = 1 + .default_num = 1, + .text = "Colour of the active pane for 'display-panes'." }, { .name = "display-panes-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, - .default_num = 4 + .default_num = 4, + .text = "Colour of not active panes for 'display-panes'." }, { .name = "display-panes-time", @@ -366,7 +408,9 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, - .default_num = 1000 + .default_num = 1000, + .unit = "milliseconds", + .text = "Time for which 'display-panes' should show pane numbers." }, { .name = "display-time", @@ -374,7 +418,9 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, - .default_num = 750 + .default_num = 750, + .unit = "milliseconds", + .text = "Time for which status line messages should appear." }, { .name = "history-limit", @@ -382,13 +428,19 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, - .default_num = 2000 + .default_num = 2000, + .unit = "lines", + .text = "Maximum number of lines to keep in the history for each " + "pane. " + "If changed, the new value applies only to new panes." }, { .name = "key-table", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "root" + .default_str = "root", + .text = "Default key table. " + "Key presses are first looked up in this table." }, { .name = "lock-after-time", @@ -396,13 +448,16 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, - .default_num = 0 + .default_num = 0, + .unit = "seconds", + .text = "Time after which a client is locked if not used." }, { .name = "lock-command", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "lock -np" + .default_str = "lock -np", + .text = "Shell command to run to lock a client." }, { .name = "message-command-style", @@ -410,7 +465,9 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=black,fg=yellow", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the command prompt when in command mode, if " + "'mode-keys' is set to 'vi'." }, { .name = "message-style", @@ -418,31 +475,39 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=yellow,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the command prompt." }, { .name = "mouse", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, - .default_num = 0 + .default_num = 0, + .text = "Whether the mouse is recognised and mouse key bindings are " + "executed. " + "Applications inside panes can use the mouse even when 'off'." }, { .name = "prefix", .type = OPTIONS_TABLE_KEY, .scope = OPTIONS_TABLE_SESSION, .default_num = '\002', + .text = "The prefix key." }, { .name = "prefix2", .type = OPTIONS_TABLE_KEY, .scope = OPTIONS_TABLE_SESSION, .default_num = KEYC_NONE, + .text = "A second prefix key." }, { .name = "renumber-windows", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, - .default_num = 0 + .default_num = 0, + .text = "Whether windows are automatically renumbered rather than " + "leaving gaps." }, { .name = "repeat-time", @@ -450,45 +515,56 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, - .default_num = 500 + .default_num = 500, + .unit = "milliseconds", + .text = "Time to wait for a key binding to repeat, if it is bound " + "with the '-r' flag." }, { .name = "set-titles", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, - .default_num = 0 + .default_num = 0, + .text = "Whether to set the terminal title, if supported." }, { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "#S:#I:#W - \"#T\" #{session_alerts}" + .default_str = "#S:#I:#W - \"#T\" #{session_alerts}", + .text = "Format of the terminal title to set." }, { .name = "silence-action", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, - .default_num = ALERT_OTHER + .default_num = ALERT_OTHER, + .text = "Action to take on a silence alert." }, { .name = "status", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_list, - .default_num = 1 + .default_num = 1, + .text = "Number of lines in the status line." }, { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 8, + .text = "Background colour of the status line. This option is " + "deprecated, use 'status-style' instead." }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 8, + .text = "Foreground colour of the status line. This option is " + "deprecated, use 'status-style' instead." }, { .name = "status-format", @@ -496,6 +572,11 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .flags = OPTIONS_TABLE_IS_ARRAY, .default_arr = options_table_status_format_default, + .text = "Formats for the status lines. " + "Each array member is the format for one status line. " + "The default status line is made up of several components " + "which may be configured individually with other option such " + "as 'status-left'." }, { .name = "status-interval", @@ -503,27 +584,32 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, - .default_num = 15 + .default_num = 15, + .unit = "seconds", + .text = "Number of seconds between status line updates." }, { .name = "status-justify", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_justify_list, - .default_num = 0 + .default_num = 0, + .text = "Position of the window list in the status line." }, { .name = "status-keys", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_keys_list, - .default_num = MODEKEY_EMACS + .default_num = MODEKEY_EMACS, + .text = "Key set to use at the command prompt." }, { .name = "status-left", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "[#S] " + .default_str = "[#{session_name}] ", + .text = "Contents of the left side of the status line." }, { .name = "status-left-length", @@ -531,7 +617,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, - .default_num = 10 + .default_num = 10, + .text = "Maximum width of the left side of the status line." }, { .name = "status-left-style", @@ -539,22 +626,26 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the left side of the status line." }, { .name = "status-position", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_position_list, - .default_num = 1 + .default_num = 1, + .text = "Position of the status line." }, { .name = "status-right", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "#{?window_bigger," - "[#{window_offset_x}#,#{window_offset_y}] ,}" - "\"#{=21:pane_title}\" %H:%M %d-%b-%y" + "[#{window_offset_x}#,#{window_offset_y}] ,}" + "\"#{=21:pane_title}\" %H:%M %d-%b-%y", + .text = "Contents of the right side of the status line." + }, { .name = "status-right-length", @@ -562,7 +653,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, - .default_num = 40 + .default_num = 40, + .text = "Maximum width of the right side of the status line." }, { .name = "status-right-style", @@ -570,7 +662,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the right side of the status line." }, { .name = "status-style", @@ -578,7 +671,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=green,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the status line." }, { .name = "update-environment", @@ -586,79 +680,100 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK " - "SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY" + "SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY", + .text = "List of environment variables to update in the session " + "environment when a client is attached." }, { .name = "visual-activity", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_visual_bell_list, - .default_num = VISUAL_OFF + .default_num = VISUAL_OFF, + .text = "How activity alerts should be shown: a message ('on'), " + "a message and a bell ('both') or nothing ('off')." }, { .name = "visual-bell", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_visual_bell_list, - .default_num = VISUAL_OFF + .default_num = VISUAL_OFF, + .text = "How bell alerts should be shown: a message ('on'), " + "a message and a bell ('both') or nothing ('off')." }, { .name = "visual-silence", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_visual_bell_list, - .default_num = VISUAL_OFF + .default_num = VISUAL_OFF, + .text = "How silence alerts should be shown: a message ('on'), " + "a message and a bell ('both') or nothing ('off')." }, { .name = "word-separators", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = " " + .default_str = " ", + .text = "Characters considered to separate words." }, /* Window options. */ { .name = "aggressive-resize", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 0 + .default_num = 0, + .text = "When 'window-size' is 'smallest', whether the maximum size " + "of a window is the smallest attached session where it is " + "the current window ('on') or the smallest session it is " + "linked to ('off')." }, { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_num = 0 + .default_num = 0, + .text = "Whether applications are allowed to use the escape sequence " + "to rename windows." }, { .name = "alternate-screen", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_num = 1 + .default_num = 1, + .text = "Whether applications are allowed to use the alternate " + "screen." }, { .name = "automatic-rename", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 1 + .default_num = 1, + .text = "Whether windows are automatically renamed." }, { .name = "automatic-rename-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}" - "#{?pane_dead,[dead],}" + "#{?pane_dead,[dead],}", + .text = "Format used to automatically rename windows." }, { .name = "clock-mode-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 4 + .default_num = 4, + .text = "Colour of the clock in clock mode." }, { .name = "clock-mode-style", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_clock_mode_style_list, - .default_num = 1 + .default_num = 1, + .text = "Time format of the clock in clock mode." }, { .name = "copy-mode-match-style", @@ -666,7 +781,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "bg=cyan,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of search matches in copy mode." }, { .name = "copy-mode-current-match-style", @@ -674,26 +790,32 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "bg=magenta,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the current search match in copy mode." }, { .name = "main-pane-height", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "24" + .default_str = "24", + .text = "Height of the main pane in the 'main-horizontal' layout. " + "This may be a percentage, for example '10%'." }, { .name = "main-pane-width", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "80" + .default_str = "80", + .text = "Width of the main pane in the 'main-vertical' layout. " + "This may be a percentage, for example '10%'." }, { .name = "mode-keys", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_mode_keys_list, - .default_num = MODEKEY_EMACS + .default_num = MODEKEY_EMACS, + .text = "Key set used in copy mode." }, { .name = "mode-style", @@ -701,19 +823,22 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "bg=yellow,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of indicators and highlighting in modes." }, { .name = "monitor-activity", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 0 + .default_num = 0, + .text = "Whether an alert is triggered by activity." }, { .name = "monitor-bell", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 1 + .default_num = 1, + .text = "Whether an alert is triggered by a bell." }, { .name = "monitor-silence", @@ -721,19 +846,26 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, - .default_num = 0 + .default_num = 0, + .text = "Time after which an alert is triggered by silence. " + "Zero means no alert." + }, { .name = "other-pane-height", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "0" + .default_str = "0", + .text = "Height of the other panes in the 'main-horizontal' layout. " + "This may be a percentage, for example '10%'." }, { .name = "other-pane-width", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "0" + .default_str = "0", + .text = "Height of the other panes in the 'main-vertical' layout. " + "This may be a percentage, for example '10%'." }, { .name = "pane-active-border-style", @@ -741,7 +873,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the active pane border." }, { .name = "pane-base-index", @@ -749,21 +882,24 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = USHRT_MAX, - .default_num = 0 + .default_num = 0, + .text = "Index of the first pane in each window." }, { .name = "pane-border-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] " - "\"#{pane_title}\"" + "\"#{pane_title}\"", + .text = "Format of text in the pane status lines." }, { .name = "pane-border-status", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_pane_status_list, - .default_num = PANE_STATUS_OFF + .default_num = PANE_STATUS_OFF, + .text = "Position of the pane status lines." }, { .name = "pane-border-style", @@ -771,19 +907,23 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the pane status lines." }, { .name = "remain-on-exit", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_num = 0 + .default_num = 0, + .text = "Whether panes should remain ('on') or be automatically " + "killed ('off') when the program inside exits." }, { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 0 + .default_num = 0, + .text = "Whether typing should be sent to all panes simultaneously." }, { .name = "window-active-style", @@ -791,14 +931,20 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Default style of the active pane." }, { .name = "window-size", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_window_size_list, - .default_num = WINDOW_SIZE_LATEST + .default_num = WINDOW_SIZE_LATEST, + .text = "How window size is calculated. " + "'latest' uses the size of the most recently used client, " + "'largest' the largest client, 'smallest' the smallest " + "client and 'manual' a size set by the 'resize-window' " + "command." }, { .name = "window-style", @@ -806,7 +952,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Default style of panes that are not the active pane." }, { .name = "window-status-activity-style", @@ -814,7 +961,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of windows in the status line with an activity alert." }, { .name = "window-status-bell-style", @@ -822,13 +970,15 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of windows in the status line with a bell alert." }, { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "#I:#W#{?window_flags,#{window_flags}, }" + .default_str = "#I:#W#{?window_flags,#{window_flags}, }", + .text = "Format of the current window in the status line." }, { .name = "window-status-current-style", @@ -836,13 +986,16 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the current window in the status line." }, { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "#I:#W#{?window_flags,#{window_flags}, }" + .default_str = "#I:#W#{?window_flags,#{window_flags}, }", + .text = "Format of windows in the status line, except the current " + "window." }, { .name = "window-status-last-style", @@ -850,13 +1003,15 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the last window in the status line." }, { .name = "window-status-separator", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = " " + .default_str = " ", + .text = "Separator between windows in the status line." }, { .name = "window-status-style", @@ -864,19 +1019,24 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of windows in the status line, except the current and " + "last windows." }, { .name = "wrap-search", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 1 + .default_num = 1, + .text = "Whether searching in copy mode should wrap at the top or " + "bottom." }, { .name = "xterm-keys", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 1 + .default_num = 1, + .text = "Whether xterm-style function key sequences should be sent." }, /* Hook options. */ diff --git a/options.c b/options.c index 39a0d08f..22e1be7e 100644 --- a/options.c +++ b/options.c @@ -116,7 +116,7 @@ options_value_free(struct options_entry *o, union options_value *ov) } static char * -options_value_tostring(struct options_entry *o, union options_value *ov, +options_value_to_string(struct options_entry *o, union options_value *ov, int numeric) { char *s; @@ -175,6 +175,12 @@ options_free(struct options *oo) free(oo); } +struct options * +options_get_parent(struct options *oo) +{ + return (oo->parent); +} + void options_set_parent(struct options *oo, struct options *parent) { @@ -262,6 +268,35 @@ options_default(struct options *oo, const struct options_table_entry *oe) return (o); } +char * +options_default_to_string(const struct options_table_entry *oe) +{ + char *s; + + switch (oe->type) { + case OPTIONS_TABLE_STRING: + case OPTIONS_TABLE_COMMAND: + s = xstrdup(oe->default_str); + break; + case OPTIONS_TABLE_NUMBER: + xasprintf(&s, "%lld", oe->default_num); + break; + case OPTIONS_TABLE_KEY: + s = xstrdup(key_string_lookup_key(oe->default_num)); + break; + case OPTIONS_TABLE_COLOUR: + s = xstrdup(colour_tostring(oe->default_num)); + break; + case OPTIONS_TABLE_FLAG: + s = xstrdup(oe->default_num ? "on" : "off"); + break; + case OPTIONS_TABLE_CHOICE: + s = xstrdup(oe->choices[oe->default_num]); + break; + } + return (s); +} + static struct options_entry * options_add(struct options *oo, const char *name) { @@ -299,6 +334,12 @@ options_name(struct options_entry *o) return (o->name); } +struct options * +options_owner(struct options_entry *o) +{ + return (o->owner); +} + const struct options_table_entry * options_table_entry(struct options_entry *o) { @@ -492,19 +533,19 @@ options_array_item_value(struct options_array_item *a) } int -options_isarray(struct options_entry *o) +options_is_array(struct options_entry *o) { return (OPTIONS_IS_ARRAY(o)); } int -options_isstring(struct options_entry *o) +options_is_string(struct options_entry *o) { return (OPTIONS_IS_STRING(o)); } char * -options_tostring(struct options_entry *o, int idx, int numeric) +options_to_string(struct options_entry *o, int idx, int numeric) { struct options_array_item *a; @@ -514,9 +555,9 @@ options_tostring(struct options_entry *o, int idx, int numeric) a = options_array_item(o, idx); if (a == NULL) return (xstrdup("")); - return (options_value_tostring(o, &a->value, numeric)); + return (options_value_to_string(o, &a->value, numeric)); } - return (options_value_tostring(o, &o->value, numeric)); + return (options_value_to_string(o, &o->value, numeric)); } char * @@ -866,3 +907,201 @@ options_string_to_style(struct options *oo, const char *name, } return (&o->style); } + +static int +options_from_string_check(const struct options_table_entry *oe, + const char *value, char **cause) +{ + struct style sy; + + if (oe == NULL) + return (0); + if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) { + xasprintf(cause, "not a suitable shell: %s", value); + return (-1); + } + if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) { + xasprintf(cause, "value is invalid: %s", value); + return (-1); + } + if ((oe->flags & OPTIONS_TABLE_IS_STYLE) && + strstr(value, "#{") == NULL && + style_parse(&sy, &grid_default_cell, value) != 0) { + xasprintf(cause, "invalid style: %s", value); + return (-1); + } + return (0); +} + +static int +options_from_string_flag(struct options *oo, const char *name, + const char *value, char **cause) +{ + int flag; + + if (value == NULL || *value == '\0') + flag = !options_get_number(oo, name); + else if (strcmp(value, "1") == 0 || + strcasecmp(value, "on") == 0 || + strcasecmp(value, "yes") == 0) + flag = 1; + else if (strcmp(value, "0") == 0 || + strcasecmp(value, "off") == 0 || + strcasecmp(value, "no") == 0) + flag = 0; + else { + xasprintf(cause, "bad value: %s", value); + return (-1); + } + options_set_number(oo, name, flag); + return (0); +} + +static int +options_from_string_choice(const struct options_table_entry *oe, + struct options *oo, const char *name, const char *value, char **cause) +{ + const char **cp; + int n, choice = -1; + + if (value == NULL) { + choice = options_get_number(oo, name); + if (choice < 2) + choice = !choice; + } else { + n = 0; + for (cp = oe->choices; *cp != NULL; cp++) { + if (strcmp(*cp, value) == 0) + choice = n; + n++; + } + if (choice == -1) { + xasprintf(cause, "unknown value: %s", value); + return (-1); + } + } + options_set_number(oo, name, choice); + return (0); +} + +int +options_from_string(struct options *oo, const struct options_table_entry *oe, + const char *name, const char *value, int append, char **cause) +{ + enum options_table_type type; + long long number; + const char *errstr, *new; + char *old; + key_code key; + + if (oe != NULL) { + if (value == NULL && + oe->type != OPTIONS_TABLE_FLAG && + oe->type != OPTIONS_TABLE_CHOICE) { + xasprintf(cause, "empty value"); + return (-1); + } + type = oe->type; + } else { + if (*name != '@') { + xasprintf(cause, "bad option name"); + return (-1); + } + type = OPTIONS_TABLE_STRING; + } + + switch (type) { + case OPTIONS_TABLE_STRING: + old = xstrdup(options_get_string(oo, name)); + options_set_string(oo, name, append, "%s", value); + + new = options_get_string(oo, name); + if (options_from_string_check(oe, new, cause) != 0) { + options_set_string(oo, name, 0, "%s", old); + free(old); + return (-1); + } + free(old); + return (0); + case OPTIONS_TABLE_NUMBER: + number = strtonum(value, oe->minimum, oe->maximum, &errstr); + if (errstr != NULL) { + xasprintf(cause, "value is %s: %s", errstr, value); + return (-1); + } + options_set_number(oo, name, number); + return (0); + case OPTIONS_TABLE_KEY: + key = key_string_lookup_string(value); + if (key == KEYC_UNKNOWN) { + xasprintf(cause, "bad key: %s", value); + return (-1); + } + options_set_number(oo, name, key); + return (0); + case OPTIONS_TABLE_COLOUR: + if ((number = colour_fromstring(value)) == -1) { + xasprintf(cause, "bad colour: %s", value); + return (-1); + } + options_set_number(oo, name, number); + return (0); + case OPTIONS_TABLE_FLAG: + return (options_from_string_flag(oo, name, value, cause)); + case OPTIONS_TABLE_CHOICE: + return (options_from_string_choice(oe, oo, name, value, cause)); + case OPTIONS_TABLE_COMMAND: + break; + } + return (-1); +} + +void +options_push_changes(const char *name) +{ + struct client *loop; + struct session *s; + struct window *w; + struct window_pane *wp; + + if (strcmp(name, "automatic-rename") == 0) { + RB_FOREACH(w, windows, &windows) { + if (w->active == NULL) + continue; + if (options_get_number(w->options, "automatic-rename")) + w->active->flags |= PANE_CHANGED; + } + } + if (strcmp(name, "key-table") == 0) { + TAILQ_FOREACH(loop, &clients, entry) + server_client_set_key_table(loop, NULL); + } + if (strcmp(name, "user-keys") == 0) { + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->tty.flags & TTY_OPENED) + tty_keys_build(&loop->tty); + } + } + if (strcmp(name, "status") == 0 || + strcmp(name, "status-interval") == 0) + status_timer_start_all(); + if (strcmp(name, "monitor-silence") == 0) + alerts_reset_all(); + if (strcmp(name, "window-style") == 0 || + strcmp(name, "window-active-style") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) + wp->flags |= PANE_STYLECHANGED; + } + if (strcmp(name, "pane-border-status") == 0) { + RB_FOREACH(w, windows, &windows) + layout_fix_panes(w); + } + RB_FOREACH(s, sessions, &sessions) + status_update_cache(s); + + recalculate_sizes(); + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->session != NULL) + server_redraw_client(loop); + } +} diff --git a/screen-write.c b/screen-write.c index 909b8531..adb53307 100644 --- a/screen-write.c +++ b/screen-write.c @@ -360,7 +360,79 @@ screen_write_strlen(const char *fmt, ...) return (size); } -/* Write simple string (no UTF-8 or maximum length). */ +/* Write string wrapped over lines. */ +void +screen_write_text(struct screen_write_ctx *ctx, u_int width, u_int lines, + const struct grid_cell *gcp, const char *fmt, ...) +{ + struct screen *s = ctx->s; + u_int cx = s->cx, cy = s->cy; + va_list ap; + char *tmp; + u_int i, end, next, idx = 0, at; + struct utf8_data *text; + struct grid_cell gc; + + memcpy(&gc, gcp, sizeof gc); + + va_start(ap, fmt); + xvasprintf(&tmp, fmt, ap); + va_end(ap); + + text = utf8_fromcstr(tmp); + free(tmp); + + while (text[idx].size != 0) { + /* Find the end of what can fit on the line. */ + at = 0; + for (end = idx; text[end].size != 0; end++) { + if (text[end].size == 1 && text[end].data[0] == '\n') + break; + if (at + text[end].width > width) + break; + at += text[end].width; + } + + /* + * If we're on a space, that's the end. If not, walk back to + * try and find one. + */ + if (text[end].size == 0) + next = end; + else if (text[end].size == 1 && text[end].data[0] == '\n') + next = end + 1; + else if (text[end].size == 1 && text[end].data[0] == ' ') + next = end + 1; + else { + for (i = end; i > idx; i--) { + if (text[i].size == 1 && text[i].data[0] == ' ') + break; + } + if (i != idx) { + next = i + 1; + end = i; + } else + next = end; + } + + /* Print the line. */ + for (i = idx; i < end; i++) { + utf8_copy(&gc.data, &text[i]); + screen_write_cell(ctx, &gc); + } + + /* If at the bottom, stop. */ + if (s->cy == cy + lines - 1) + break; + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + idx = next; + } + + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + free(text); +} + +/* Write simple string (no maximum length). */ void screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, const char *fmt, ...) diff --git a/style.c b/style.c index 3c615852..08614f9c 100644 --- a/style.c +++ b/style.c @@ -31,6 +31,7 @@ /* Default style. */ static struct style style_default = { { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 }, + 0, 8, STYLE_ALIGN_DEFAULT, @@ -78,7 +79,11 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->gc.bg = base->bg; sy->gc.attr = base->attr; sy->gc.flags = base->flags; - } else if (strcasecmp(tmp, "push-default") == 0) + } else if (strcasecmp(tmp, "ignore") == 0) + sy->ignore = 1; + else if (strcasecmp(tmp, "noignore") == 0) + sy->ignore = 0; + else if (strcasecmp(tmp, "push-default") == 0) sy->default_type = STYLE_DEFAULT_PUSH; else if (strcasecmp(tmp, "pop-default") == 0) sy->default_type = STYLE_DEFAULT_POP; diff --git a/tmux.1 b/tmux.1 index 867d24e7..56322d85 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1952,6 +1952,53 @@ includes all sessions in any session groups in the tree rather than only the first. This command works only if at least one client is attached. .It Xo +.Ic customize-mode +.Op Fl NZ +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl t Ar target-pane +.Op Ar template +.Xc +Put a pane into customize mode, where options may be browsed and modified from a +list. +Option values in the list are shown for the active pane in the current window. +.Fl Z +zooms the pane. +The following keys may be used in customize mode: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Set pane, window, session or global option value" +.It Li "Up" Ta "Select previous item" +.It Li "Down" Ta "Select next item" +.It Li "+" Ta "Expand selected item" +.It Li "-" Ta "Collapse selected item" +.It Li "M-+" Ta "Expand all items" +.It Li "M--" Ta "Collapse all items" +.It Li "s" Ta "Set pane, window, session or global option value" +.It Li "S" Ta "Set global option value" +.It Li "w" Ta "Set window option value, if option is for pane and window" +.It Li "u" Ta "Unset an option (set to default value if global)" +.It Li "U" Ta "Unset tagged options" +.It Li "C-s" Ta "Search by name" +.It Li "n" Ta "Repeat last search" +.It Li "t" Ta "Toggle if item is tagged" +.It Li "T" Ta "Tag no items" +.It Li "C-t" Ta "Tag all items" +.It Li "f" Ta "Enter a format to filter items" +.It Li "v" Ta "Toggle option information" +.It Li "q" Ta "Exit mode" +.El +.Pp +.Fl f +specifies an initial filter: the filter is a format - if it evaluates to zero, +the item in the list is not shown, otherwise it is shown. +If a filter would lead to an empty list, it is ignored. +.Fl F +specifies the format for each item in the tree. +.Fl N +starts without the option information. +This command works only if at least one client is attached. +.It Xo .Ic display-panes .Op Fl b .Op Fl d Ar duration diff --git a/tmux.h b/tmux.h index d038e24b..b3b7cb33 100644 --- a/tmux.h +++ b/tmux.h @@ -747,6 +747,7 @@ enum style_default_type { /* Style option. */ struct style { struct grid_cell gc; + int ignore; int fill; enum style_align align; @@ -1720,6 +1721,9 @@ struct options_table_entry { const char *separator; const char *pattern; + + const char *text; + const char *unit; }; /* Common command usages. */ @@ -1897,6 +1901,7 @@ void notify_pane(const char *, struct window_pane *); /* options.c */ struct options *options_create(struct options *); void options_free(struct options *); +struct options *options_get_parent(struct options *); void options_set_parent(struct options *, struct options *); struct options_entry *options_first(struct options *); struct options_entry *options_next(struct options_entry *); @@ -1904,7 +1909,9 @@ struct options_entry *options_empty(struct options *, const struct options_table_entry *); struct options_entry *options_default(struct options *, const struct options_table_entry *); +char *options_default_to_string(const struct options_table_entry *); const char *options_name(struct options_entry *); +struct options *options_owner(struct options_entry *); const struct options_table_entry *options_table_entry(struct options_entry *); struct options_entry *options_get_only(struct options *, const char *); struct options_entry *options_get(struct options *, const char *); @@ -1919,9 +1926,9 @@ struct options_array_item *options_array_first(struct options_entry *); struct options_array_item *options_array_next(struct options_array_item *); u_int options_array_item_index(struct options_array_item *); union options_value *options_array_item_value(struct options_array_item *); -int options_isarray(struct options_entry *); -int options_isstring(struct options_entry *); -char *options_tostring(struct options_entry *, int, int); +int options_is_array(struct options_entry *); +int options_is_string(struct options_entry *); +char *options_to_string(struct options_entry *, int, int); char *options_parse(const char *, int *); struct options_entry *options_parse_get(struct options *, const char *, int *, int); @@ -1941,6 +1948,10 @@ int options_scope_from_flags(struct args *, int, struct cmd_find_state *, struct options **, char **); struct style *options_string_to_style(struct options *, const char *, struct format_tree *); +int options_from_string(struct options *, + const struct options_table_entry *, const char *, + const char *, int, char **); +void options_push_changes(const char *); /* options-table.c */ extern const struct options_table_entry options_table[]; @@ -2460,6 +2471,8 @@ void screen_write_start_callback(struct screen_write_ctx *, struct screen *, void screen_write_stop(struct screen_write_ctx *); void screen_write_reset(struct screen_write_ctx *); size_t printflike(1, 2) screen_write_strlen(const char *, ...); +void printflike(5, 6) screen_write_text(struct screen_write_ctx *, u_int, u_int, + const struct grid_cell *, const char *, ...); void printflike(3, 4) screen_write_puts(struct screen_write_ctx *, const struct grid_cell *, const char *, ...); void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *, @@ -2678,6 +2691,7 @@ typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *, u_int, u_int); typedef int (*mode_tree_search_cb)(void *, void *, const char *); typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code); +typedef u_int (*mode_tree_height_cb)(void *, u_int); typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code); u_int mode_tree_count_tagged(struct mode_tree_data *); void *mode_tree_get_current(struct mode_tree_data *); @@ -2686,11 +2700,12 @@ void mode_tree_expand(struct mode_tree_data *, uint64_t); int mode_tree_set_current(struct mode_tree_data *, uint64_t); void mode_tree_each_tagged(struct mode_tree_data *, mode_tree_each_cb, struct client *, key_code, int); +void mode_tree_up(struct mode_tree_data *, int); void mode_tree_down(struct mode_tree_data *, int); struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *, mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb, - mode_tree_menu_cb, void *, const struct menu_item *, const char **, - u_int, struct screen **); + mode_tree_menu_cb, mode_tree_height_cb, void *, + const struct menu_item *, const char **, u_int, struct screen **); void mode_tree_zoom(struct mode_tree_data *, struct args *); void mode_tree_build(struct mode_tree_data *); void mode_tree_free(struct mode_tree_data *); @@ -2728,6 +2743,9 @@ void window_copy_start_drag(struct client *, struct mouse_event *); char *window_copy_get_word(struct window_pane *, u_int, u_int); char *window_copy_get_line(struct window_pane *, u_int); +/* window-option.c */ +extern const struct window_mode window_customize_mode; + /* names.c */ void check_window_name(struct window *); char *default_window_name(struct window *); diff --git a/window-buffer.c b/window-buffer.c index 16adbc61..b3b8e27a 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -296,8 +296,8 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->command = xstrdup(args->argv[0]); data->data = mode_tree_start(wp, args, window_buffer_build, - window_buffer_draw, window_buffer_search, window_buffer_menu, data, - window_buffer_menu_items, window_buffer_sort_list, + window_buffer_draw, window_buffer_search, window_buffer_menu, NULL, + data, window_buffer_menu_items, window_buffer_sort_list, nitems(window_buffer_sort_list), &s); mode_tree_zoom(data->data, args); diff --git a/window-client.c b/window-client.c index cd424dd7..159efa5a 100644 --- a/window-client.c +++ b/window-client.c @@ -271,7 +271,7 @@ window_client_init(struct window_mode_entry *wme, data->command = xstrdup(args->argv[0]); data->data = mode_tree_start(wp, args, window_client_build, - window_client_draw, NULL, window_client_menu, data, + window_client_draw, NULL, window_client_menu, NULL, data, window_client_menu_items, window_client_sort_list, nitems(window_client_sort_list), &s); mode_tree_zoom(data->data, args); diff --git a/window-customize.c b/window-customize.c new file mode 100644 index 00000000..93b2e840 --- /dev/null +++ b/window-customize.c @@ -0,0 +1,1013 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2017 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 +#include + +#include "tmux.h" + +static struct screen *window_customize_init(struct window_mode_entry *, + struct cmd_find_state *, struct args *); +static void window_customize_free(struct window_mode_entry *); +static void window_customize_resize(struct window_mode_entry *, + u_int, u_int); +static void window_customize_key(struct window_mode_entry *, + struct client *, struct session *, + struct winlink *, key_code, struct mouse_event *); + +#define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \ + "#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \ + "#[ignore]" \ + "#{option_value}#{?option_unit, #{option_unit},}" + +static const struct menu_item window_customize_menu_items[] = { + { "Select", '\r', NULL }, + { "Expand", KEYC_RIGHT, NULL }, + { "", KEYC_NONE, NULL }, + { "Tag", 't', NULL }, + { "Tag All", '\024', NULL }, + { "Tag None", 'T', NULL }, + { "", KEYC_NONE, NULL }, + { "Cancel", 'q', NULL }, + + { NULL, KEYC_NONE, NULL } +}; + +const struct window_mode window_customize_mode = { + .name = "options-mode", + .default_format = WINDOW_CUSTOMIZE_DEFAULT_FORMAT, + + .init = window_customize_init, + .free = window_customize_free, + .resize = window_customize_resize, + .key = window_customize_key, +}; + +enum window_customize_scope { + WINDOW_CUSTOMIZE_NONE, + WINDOW_CUSTOMIZE_SERVER, + WINDOW_CUSTOMIZE_GLOBAL_SESSION, + WINDOW_CUSTOMIZE_SESSION, + WINDOW_CUSTOMIZE_GLOBAL_WINDOW, + WINDOW_CUSTOMIZE_WINDOW, + WINDOW_CUSTOMIZE_PANE +}; + +struct window_customize_itemdata { + struct window_customize_modedata *data; + enum window_customize_scope scope; + struct options *oo; + char *name; + int idx; +}; + +struct window_customize_modedata { + struct window_pane *wp; + int dead; + int references; + + struct mode_tree_data *data; + char *format; + int hide_global; + + struct window_customize_itemdata **item_list; + u_int item_size; + + struct cmd_find_state fs; +}; + +static uint64_t +window_customize_get_tag(struct options_entry *o, int idx, + const struct options_table_entry *oe) +{ + uint64_t offset; + + if (oe == NULL) + return ((uint64_t)o); + offset = ((char *)oe - (char *)options_table) / sizeof *options_table; + return ((offset << 32) | ((idx + 1) << 1) | 1); +} + +static struct options * +window_customize_get_tree(enum window_customize_scope scope, + struct cmd_find_state *fs) +{ + switch (scope) { + case WINDOW_CUSTOMIZE_NONE: + return (NULL); + case WINDOW_CUSTOMIZE_SERVER: + return (global_options); + case WINDOW_CUSTOMIZE_GLOBAL_SESSION: + return (global_s_options); + case WINDOW_CUSTOMIZE_SESSION: + return (fs->s->options); + case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: + return (global_w_options); + case WINDOW_CUSTOMIZE_WINDOW: + return (fs->w->options); + case WINDOW_CUSTOMIZE_PANE: + return (fs->wp->options); + } + return (NULL); +} + +static int +window_customize_check_item(struct window_customize_modedata *data, + struct window_customize_itemdata *item, struct cmd_find_state *fsp) +{ + struct cmd_find_state fs; + + if (fsp == NULL) + fsp = &fs; + + if (cmd_find_valid_state(&data->fs)) + cmd_find_copy_state(fsp, &data->fs); + else + cmd_find_from_pane(fsp, data->wp, 0); + return (item->oo == window_customize_get_tree(item->scope, fsp)); +} + +static char * +window_customize_scope_text(enum window_customize_scope scope, + struct cmd_find_state *fs) +{ + char *s; + u_int idx; + + switch (scope) { + case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_SERVER: + case WINDOW_CUSTOMIZE_GLOBAL_SESSION: + case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: + s = xstrdup(""); + break; + case WINDOW_CUSTOMIZE_PANE: + window_pane_index(fs->wp, &idx); + xasprintf(&s, "pane %u", idx); + break; + case WINDOW_CUSTOMIZE_SESSION: + xasprintf(&s, "session %s", fs->s->name); + break; + case WINDOW_CUSTOMIZE_WINDOW: + xasprintf(&s, "window %u", fs->wl->idx); + break; + } + return (s); +} + +static struct window_customize_itemdata * +window_customize_add_item(struct window_customize_modedata *data) +{ + struct window_customize_itemdata *item; + + data->item_list = xreallocarray(data->item_list, data->item_size + 1, + sizeof *data->item_list); + item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); + return (item); +} + +static void +window_customize_free_item(struct window_customize_itemdata *item) +{ + free(item->name); + free(item); +} + +static void +window_customize_build_array(struct window_customize_modedata *data, + struct mode_tree_item *top, enum window_customize_scope scope, + struct options_entry *o, struct format_tree *ft) +{ + const struct options_table_entry *oe = options_table_entry(o); + struct options *oo = options_owner(o); + struct window_customize_itemdata *item; + struct options_array_item *ai; + char *name, *value, *text; + u_int idx; + uint64_t tag; + + ai = options_array_first(o); + while (ai != NULL) { + idx = options_array_item_index(ai); + + xasprintf(&name, "%s[%u]", options_name(o), idx); + format_add(ft, "option_name", "%s", name); + value = options_to_string(o, idx, 0); + format_add(ft, "option_value", "%s", value); + + item = window_customize_add_item(data); + item->scope = scope; + item->oo = oo; + item->name = xstrdup(options_name(o)); + item->idx = idx; + + text = format_expand(ft, data->format); + tag = window_customize_get_tag(o, idx, oe); + mode_tree_add(data->data, top, item, tag, name, text, -1); + free(text); + + free(name); + free(value); + + ai = options_array_next(ai); + } +} + +static void +window_customize_build_option(struct window_customize_modedata *data, + struct mode_tree_item *top, enum window_customize_scope scope, + struct options_entry *o, struct format_tree *ft, + const char *filter, struct cmd_find_state *fs) +{ + const struct options_table_entry *oe = options_table_entry(o); + struct options *oo = options_owner(o); + const char *name = options_name(o); + struct window_customize_itemdata *item; + char *text, *expanded, *value; + int global = 0, array = 0; + uint64_t tag; + + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK)) + return; + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) + array = 1; + + if (scope == WINDOW_CUSTOMIZE_SERVER || + scope == WINDOW_CUSTOMIZE_GLOBAL_SESSION || + scope == WINDOW_CUSTOMIZE_GLOBAL_WINDOW) + global = 1; + if (data->hide_global && global) + return; + + format_add(ft, "option_name", "%s", name); + format_add(ft, "option_is_global", "%d", global); + format_add(ft, "option_is_array", "%d", array); + + text = window_customize_scope_text(scope, fs); + format_add(ft, "option_scope", "%s", text); + free(text); + + if (oe != NULL && oe->unit != NULL) + format_add(ft, "option_unit", "%s", oe->unit); + else + format_add(ft, "option_unit", ""); + + if (!array) { + value = options_to_string(o, -1, 0); + format_add(ft, "option_value", "%s", value); + free(value); + } + + if (filter != NULL) { + expanded = format_expand(ft, filter); + if (!format_true(expanded)) { + free(expanded); + return; + } + free(expanded); + } + item = window_customize_add_item(data); + item->oo = oo; + item->scope = scope; + item->name = xstrdup(name); + item->idx = -1; + + if (array) + text = NULL; + else + text = format_expand(ft, data->format); + tag = window_customize_get_tag(o, -1, oe); + top = mode_tree_add(data->data, top, item, tag, name, text, 0); + free(text); + + if (array) + window_customize_build_array(data, top, scope, o, ft); +} + +static void +window_customize_find_user_options(struct options *oo, const char ***list, + u_int *size) +{ + struct options_entry *o; + const char *name; + u_int i; + + o = options_first(oo); + while (o != NULL) { + name = options_name(o); + if (*name != '@') { + o = options_next(o); + continue; + } + for (i = 0; i < *size; i++) { + if (strcmp((*list)[i], name) == 0) + break; + } + if (i != *size) { + o = options_next(o); + continue; + } + *list = xreallocarray(*list, (*size) + 1, sizeof **list); + (*list)[(*size)++] = name; + + o = options_next(o); + } +} + +static void +window_customize_build_options(struct window_customize_modedata *data, + const char *title, uint64_t tag, + enum window_customize_scope scope0, struct options *oo0, + enum window_customize_scope scope1, struct options *oo1, + enum window_customize_scope scope2, struct options *oo2, + struct format_tree *ft, const char *filter, struct cmd_find_state *fs) +{ + struct mode_tree_item *top; + struct options_entry *o, *loop; + const char **list = NULL, *name; + u_int size = 0, i; + enum window_customize_scope scope; + + top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0); + + /* + * We get the options from the first tree, but build it using the + * values from the other two. Any tree can have user options so we need + * to build a separate list of them. + */ + + window_customize_find_user_options(oo0, &list, &size); + if (oo1 != NULL) + window_customize_find_user_options(oo1, &list, &size); + if (oo2 != NULL) + window_customize_find_user_options(oo2, &list, &size); + + for (i = 0; i < size; i++) { + if (oo2 != NULL) + o = options_get(oo0, list[i]); + else if (oo1 != NULL) + o = options_get(oo1, list[i]); + else + o = options_get(oo2, list[i]); + if (options_owner(o) == oo0) + scope = scope0; + else if (options_owner(o) == oo1) + scope = scope1; + else if (options_owner(o) == oo2) + scope = scope2; + window_customize_build_option(data, top, scope, o, ft, filter, + fs); + } + free(list); + + loop = options_first(oo0); + while (loop != NULL) { + name = options_name(loop); + if (*name == '@') { + loop = options_next(loop); + continue; + } + if (oo2 != NULL) + o = options_get(oo2, name); + else if (oo1 != NULL) + o = options_get(oo1, name); + else + o = loop; + if (options_owner(o) == oo0) + scope = scope0; + else if (options_owner(o) == oo1) + scope = scope1; + else if (options_owner(o) == oo2) + scope = scope2; + window_customize_build_option(data, top, scope, o, ft, filter, + fs); + loop = options_next(loop); + } +} + +static void +window_customize_build(void *modedata, + __unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag, + const char *filter) +{ + struct window_customize_modedata *data = modedata; + struct cmd_find_state fs; + struct format_tree *ft; + u_int i; + + for (i = 0; i < data->item_size; i++) + window_customize_free_item(data->item_list[i]); + free(data->item_list); + data->item_list = NULL; + data->item_size = 0; + + if (cmd_find_valid_state(&data->fs)) + cmd_find_copy_state(&fs, &data->fs); + else + cmd_find_from_pane(&fs, data->wp, 0); + + ft = format_create_from_state(NULL, NULL, &fs); + + window_customize_build_options(data, "Server Options", + (1ULL << 63)|OPTIONS_TABLE_SERVER, + WINDOW_CUSTOMIZE_SERVER, global_options, + WINDOW_CUSTOMIZE_NONE, NULL, + WINDOW_CUSTOMIZE_NONE, NULL, + ft, filter, &fs); + window_customize_build_options(data, "Session Options", + (1ULL << 63)|OPTIONS_TABLE_SESSION, + WINDOW_CUSTOMIZE_GLOBAL_SESSION, global_s_options, + WINDOW_CUSTOMIZE_SESSION, fs.s->options, + WINDOW_CUSTOMIZE_NONE, NULL, + ft, filter, &fs); + window_customize_build_options(data, "Window & Pane Options", + (1ULL << 63)|OPTIONS_TABLE_WINDOW, + WINDOW_CUSTOMIZE_GLOBAL_WINDOW, global_w_options, + WINDOW_CUSTOMIZE_WINDOW, fs.w->options, + WINDOW_CUSTOMIZE_PANE, fs.wp->options, + ft, filter, &fs); + + format_free(ft); +} + +static void +window_customize_draw(void *modedata, void *itemdata, + struct screen_write_ctx *ctx, u_int sx, u_int sy) +{ + struct window_customize_modedata *data = modedata; + struct window_customize_itemdata *item = itemdata; + struct screen *s = ctx->s; + u_int cx = s->cx, cy = s->cy; + int idx; + struct options_entry *o, *parent; + struct options *go, *wo; + const struct options_table_entry *oe; + struct grid_cell gc; + const char **choice, *text, *name; + const char *space = "", *unit = ""; + char *value = NULL, *expanded; + char *default_value = NULL; + char choices[256] = ""; + struct cmd_find_state fs; + struct format_tree *ft; + + if (item == NULL || !window_customize_check_item(data, item, &fs)) + return; + name = item->name; + idx = item->idx; + + o = options_get(item->oo, name); + if (o == NULL) + return; + oe = options_table_entry(o); + + if (oe != NULL && oe->unit != NULL) { + space = " "; + unit = oe->unit; + } + ft = format_create_from_state(NULL, NULL, &fs); + + if (oe == NULL) + text = "This is a user option."; + else if (oe->text == NULL) + text = "This option doesn't have a description."; + else + text = oe->text; + screen_write_text(ctx, sx, sy, &grid_default_cell, "%s", text); + if (s->cy >= cy + sy - 1) + goto out; + + if (oe == NULL) + text = "user"; + else if ((oe->scope & (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) == + (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) + text = "window and pane"; + else if (oe->scope & OPTIONS_TABLE_WINDOW) + text = "window"; + else if (oe->scope & OPTIONS_TABLE_SESSION) + text = "session"; + else + text = "server"; + screen_write_text(ctx, sx, sy - (s->cy - cy), &grid_default_cell, + "This is a %s option.", text); + if (s->cy > cy + sy - 1) + goto out; + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { + if (idx != -1) { + screen_write_text(ctx, sx, sy - (s->cy - cy), + &grid_default_cell, + "This is an array option, index %u.", idx); + } else { + screen_write_text(ctx, sx, sy - (s->cy - cy), + &grid_default_cell, "This is an array option."); + } + if (idx == -1) + goto out; + if (s->cy > cy + sy - 1) + goto out; + } + + value = options_to_string(o, idx, 0); + if (oe != NULL && idx == -1) { + default_value = options_default_to_string(oe); + if (strcmp(default_value, value) == 0) { + free(default_value); + default_value = NULL; + } + } + screen_write_nputs(ctx, sx, &grid_default_cell, "Option value: %s%s%s", + value, space, unit); + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + if (s->cy > cy + sy - 1) + goto out; + + if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) { + expanded = format_expand(ft, value); + if (strcmp(expanded, value) != 0) { + screen_write_nputs(ctx, sx, &grid_default_cell, + "This expands to: %s", expanded); + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + if (s->cy > cy + sy - 1) + goto out; + } + free(expanded); + } + if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) { + for (choice = oe->choices; *choice != NULL; choice++) { + strlcat(choices, *choice, sizeof choices); + strlcat(choices, ", ", sizeof choices); + } + choices[strlen(choices) - 2] = '\0'; + screen_write_nputs(ctx, sx, &grid_default_cell, + "Available values are: %s", choices); + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + if (s->cy > cy + sy - 1) + goto out; + } + if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) { + screen_write_nputs(ctx, sx, &grid_default_cell, + "This is a colour option: "); + if (sx > 24) { + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.fg = options_get_number(item->oo, name); + screen_write_nputs(ctx, sx - 24, &gc, "EXAMPLE"); + } + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + if (s->cy > cy + sy - 1) + goto out; + } + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) { + screen_write_nputs(ctx, sx, &grid_default_cell, + "This is a style option: "); + if (sx > 24) { + style_apply(&gc, item->oo, name, ft); + screen_write_nputs(ctx, sx - 24, &gc, "EXAMPLE"); + } + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + if (s->cy > cy + sy - 1) + goto out; + } + if (default_value != NULL) { + screen_write_nputs(ctx, sx, &grid_default_cell, + "The default is: %s%s%s", default_value, space, unit); + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + if (s->cy > cy + sy - 1) + goto out; + } + + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + if (s->cy > cy + sy - 1) + goto out; + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { + wo = NULL; + go = NULL; + } else { + switch (item->scope) { + case WINDOW_CUSTOMIZE_PANE: + wo = options_get_parent(item->oo); + go = options_get_parent(wo); + break; + case WINDOW_CUSTOMIZE_WINDOW: + case WINDOW_CUSTOMIZE_SESSION: + wo = NULL; + go = options_get_parent(item->oo); + break; + default: + wo = NULL; + go = NULL; + break; + } + } + if (wo != NULL && options_owner(o) != wo) { + parent = options_get_only(wo, name); + if (parent != NULL) { + value = options_to_string(parent, -1 , 0); + screen_write_nputs(ctx, sx, &grid_default_cell, + "Window value (from window %u): %s%s%s", fs.wl->idx, + value, space, unit); + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + if (s->cy > cy + sy - 1) + goto out; + } + } + if (go != NULL && options_owner(o) != go) { + parent = options_get_only(go, name); + if (parent != NULL) { + value = options_to_string(parent, -1 , 0); + screen_write_nputs(ctx, sx, &grid_default_cell, + "Global value: %s%s%s", value, space, unit); + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + if (s->cy > cy + sy - 1) + goto out; + } + } + +out: + free(value); + free(default_value); + format_free(ft); +} + +static void +window_customize_menu(void *modedata, struct client *c, key_code key) +{ + struct window_customize_modedata *data = modedata; + struct window_pane *wp = data->wp; + struct window_mode_entry *wme; + + wme = TAILQ_FIRST(&wp->modes); + if (wme == NULL || wme->data != modedata) + return; + window_customize_key(wme, c, NULL, NULL, key, NULL); +} + +static u_int +window_customize_height(__unused void *modedata, __unused u_int height) +{ + return (12); +} + +static struct screen * +window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs, + struct args *args) +{ + struct window_pane *wp = wme->wp; + struct window_customize_modedata *data; + struct screen *s; + + wme->data = data = xcalloc(1, sizeof *data); + data->wp = wp; + data->references = 1; + + memcpy(&data->fs, fs, sizeof data->fs); + + if (args == NULL || !args_has(args, 'F')) + data->format = xstrdup(WINDOW_CUSTOMIZE_DEFAULT_FORMAT); + else + data->format = xstrdup(args_get(args, 'F')); + + data->data = mode_tree_start(wp, args, window_customize_build, + window_customize_draw, NULL, window_customize_menu, + window_customize_height, data, window_customize_menu_items, NULL, 0, + &s); + mode_tree_zoom(data->data, args); + + mode_tree_build(data->data); + mode_tree_draw(data->data); + + return (s); +} + +static void +window_customize_destroy(struct window_customize_modedata *data) +{ + u_int i; + + if (--data->references != 0) + return; + + for (i = 0; i < data->item_size; i++) + window_customize_free_item(data->item_list[i]); + free(data->item_list); + + free(data->format); + + free(data); +} + +static void +window_customize_free(struct window_mode_entry *wme) +{ + struct window_customize_modedata *data = wme->data; + + if (data == NULL) + return; + + data->dead = 1; + mode_tree_free(data->data); + window_customize_destroy(data); +} + +static void +window_customize_resize(struct window_mode_entry *wme, u_int sx, u_int sy) +{ + struct window_customize_modedata *data = wme->data; + + mode_tree_resize(data->data, sx, sy); +} + +static int +window_customize_set_callback(struct client *c, void *itemdata, const char *s, + __unused int done) +{ + struct window_customize_itemdata *item = itemdata; + struct window_customize_modedata *data = item->data; + struct options_entry *o; + const struct options_table_entry *oe; + struct options *oo = item->oo; + const char *name = item->name; + char *cause; + int idx = item->idx; + + if (s == NULL || *s == '\0' || data->dead) + return (0); + if (item == NULL || !window_customize_check_item(data, item, NULL)) + return (0); + o = options_get(oo, name); + if (o == NULL) + return (0); + oe = options_table_entry(o); + + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { + if (idx == -1) { + for (idx = 0; idx < INT_MAX; idx++) { + if (options_array_get(o, idx) == NULL) + break; + } + } + if (options_array_set(o, idx, s, 0, &cause) != 0) + goto fail; + } else { + if (options_from_string(oo, oe, name, s, 0, &cause) != 0) + goto fail; + } + + options_push_changes(item->name); + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (0); + +fail: + *cause = toupper((u_char)*cause); + status_message_set(c, "%s", cause); + free(cause); + return (0); +} + +static void +window_customize_set_free(void *itemdata) +{ + struct window_customize_itemdata *item = itemdata; + struct window_customize_modedata *data = item->data; + + window_customize_free_item(item); + window_customize_destroy(data); +} + +static void +window_customize_set_option(struct client *c, struct window_customize_modedata *data, + struct window_customize_itemdata *item, int global, int pane) +{ + struct options_entry *o; + const struct options_table_entry *oe; + struct options *oo; + struct window_customize_itemdata *new_item; + int flag, idx = item->idx; + enum window_customize_scope scope; + u_int choice; + const char *name = item->name, *space = ""; + char *prompt, *value, *text; + struct cmd_find_state fs; + + if (item == NULL || !window_customize_check_item(data, item, &fs)) + return; + o = options_get(item->oo, name); + if (o == NULL) + return; + value = options_to_string(o, idx, 0); + + oe = options_table_entry(o); + if (~oe->scope & OPTIONS_TABLE_PANE) + pane = 0; + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { + scope = item->scope; + oo = item->oo; + } else { + if (global) { + switch (item->scope) { + case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_SERVER: + case WINDOW_CUSTOMIZE_GLOBAL_SESSION: + case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: + scope = item->scope; + break; + case WINDOW_CUSTOMIZE_SESSION: + scope = WINDOW_CUSTOMIZE_GLOBAL_SESSION; + break; + case WINDOW_CUSTOMIZE_WINDOW: + case WINDOW_CUSTOMIZE_PANE: + scope = WINDOW_CUSTOMIZE_GLOBAL_WINDOW; + break; + } + } else { + switch (item->scope) { + case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_SERVER: + case WINDOW_CUSTOMIZE_SESSION: + scope = item->scope; + break; + case WINDOW_CUSTOMIZE_WINDOW: + case WINDOW_CUSTOMIZE_PANE: + if (pane) + scope = WINDOW_CUSTOMIZE_PANE; + else + scope = WINDOW_CUSTOMIZE_WINDOW; + break; + case WINDOW_CUSTOMIZE_GLOBAL_SESSION: + scope = WINDOW_CUSTOMIZE_SESSION; + break; + case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: + if (pane) + scope = WINDOW_CUSTOMIZE_PANE; + else + scope = WINDOW_CUSTOMIZE_WINDOW; + break; + } + } + if (scope == item->scope) + oo = item->oo; + else + oo = window_customize_get_tree(scope, &fs); + } + + if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) { + flag = options_get_number(oo, name); + options_set_number(oo, name, !flag); + } else if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) { + choice = options_get_number(oo, name); + if (oe->choices[choice + 1] == NULL) + choice = 0; + else + choice++; + options_set_number(oo, name, choice); + } else { + text = window_customize_scope_text(scope, &fs); + if (*text != '\0') + space = ", for "; + else if (scope != WINDOW_CUSTOMIZE_SERVER) + space = ", global"; + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { + if (idx == -1) { + xasprintf(&prompt, "(%s[+]%s%s) ", name, space, + text); + } else { + xasprintf(&prompt, "(%s[%d]%s%s) ", name, idx, + space, text); + } + } else + xasprintf(&prompt, "(%s%s%s) ", name, space, text); + free(text); + + new_item = xmalloc(sizeof *new_item); + new_item->data = data; + new_item->scope = scope; + new_item->oo = oo; + new_item->name = xstrdup(name); + new_item->idx = idx; + + data->references++; + status_prompt_set(c, prompt, value, window_customize_set_callback, + window_customize_set_free, new_item, PROMPT_NOFORMAT); + } + free(value); +} + +static void +window_customize_unset_option(struct window_customize_modedata *data, + struct window_customize_itemdata *item) +{ + struct options_entry *o; + const struct options_table_entry *oe; + + if (item == NULL || !window_customize_check_item(data, item, NULL)) + return; + + o = options_get(item->oo, item->name); + if (o == NULL) + return; + if (item->idx != -1) { + mode_tree_up(data->data, 0); + options_array_set(o, item->idx, NULL, 0, NULL); + return; + } + oe = options_table_entry(o); + if (oe != NULL && + options_owner(o) != global_options && + options_owner(o) != global_s_options && + options_owner(o) != global_w_options) + options_remove(o); + else + options_default(options_owner(o), oe); +} + +static void +window_customize_unset_each(void *modedata, void *itemdata, + __unused struct client *c, __unused key_code key) +{ + window_customize_unset_option(modedata, itemdata); +} + +static void +window_customize_key(struct window_mode_entry *wme, struct client *c, + __unused struct session *s, __unused struct winlink *wl, key_code key, + struct mouse_event *m) +{ + struct window_pane *wp = wme->wp; + struct window_customize_modedata *data = wme->data; + struct window_customize_itemdata *item, *new_item; + int finished; + + item = mode_tree_get_current(data->data); + finished = mode_tree_key(data->data, c, &key, m, NULL, NULL); + if (item != (new_item = mode_tree_get_current(data->data))) + item = new_item; + + switch (key) { + case '\r': + case 's': + if (item == NULL) + break; + window_customize_set_option(c, data, item, 0, 1); + options_push_changes(item->name); + mode_tree_build(data->data); + break; + case 'w': + if (item == NULL) + break; + window_customize_set_option(c, data, item, 0, 0); + options_push_changes(item->name); + mode_tree_build(data->data); + break; + case 'S': + case 'W': + if (item == NULL) + break; + window_customize_set_option(c, data, item, 1, 0); + options_push_changes(item->name); + mode_tree_build(data->data); + break; + case 'u': + if (item == NULL) + break; + window_customize_unset_option(data, item); + options_push_changes(item->name); + mode_tree_build(data->data); + break; + case 'U': + mode_tree_each_tagged(data->data, window_customize_unset_each, + c, KEYC_NONE, 1); + options_push_changes(item->name); + mode_tree_build(data->data); + break; + case 'H': + data->hide_global = !data->hide_global; + mode_tree_build(data->data); + break; + } + if (finished) + window_pane_reset_mode(wp); + else { + mode_tree_draw(data->data); + wp->flags |= PANE_REDRAW; + } +} diff --git a/window-tree.c b/window-tree.c index d245a715..b3ce8bb7 100644 --- a/window-tree.c +++ b/window-tree.c @@ -885,7 +885,7 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->squash_groups = !args_has(args, 'G'); data->data = mode_tree_start(wp, args, window_tree_build, - window_tree_draw, window_tree_search, window_tree_menu, data, + window_tree_draw, window_tree_search, window_tree_menu, NULL, data, window_tree_menu_items, window_tree_sort_list, nitems(window_tree_sort_list), &s); mode_tree_zoom(data->data, args); From 5e97d79eb1337db2dd5969fb2c339d14427f545b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 8 May 2020 22:09:31 +0100 Subject: [PATCH 0332/1006] Fix some customize mode drawing nits. --- key-bindings.c | 2 +- mode-tree.c | 3 ++- screen-write.c | 2 +- window-customize.c | 6 ++++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index 1847b6e6..d6a72297 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -278,7 +278,7 @@ key_bindings_init(void) "bind -N 'Toggle the marked pane' m select-pane -m", "bind -N 'Select the next window' n next-window", "bind -N 'Select the next pane' o select-pane -t:.+", - "bind -N 'Customizeoptions' C customize-mode", + "bind -N 'Customize options' C customize-mode", "bind -N 'Select the previous pane' p previous-window", "bind -N 'Display pane numbers' q display-panes", "bind -N 'Redraw the current client' r refresh-client", diff --git a/mode-tree.c b/mode-tree.c index 8f7ba85f..8a28be16 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -708,7 +708,8 @@ mode_tree_draw(struct mode_tree_data *mtd) else screen_write_puts(&ctx, &gc0, "active"); screen_write_puts(&ctx, &gc0, ") "); - } + } else + screen_write_puts(&ctx, &gc0, " "); } free(text); diff --git a/screen-write.c b/screen-write.c index adb53307..b168d0b6 100644 --- a/screen-write.c +++ b/screen-write.c @@ -428,7 +428,7 @@ screen_write_text(struct screen_write_ctx *ctx, u_int width, u_int lines, idx = next; } - screen_write_cursormove(ctx, cx, s->cy + 1, 0); + screen_write_cursormove(ctx, cx, s->cy, 0); free(text); } diff --git a/window-customize.c b/window-customize.c index 93b2e840..e07bc243 100644 --- a/window-customize.c +++ b/window-customize.c @@ -492,6 +492,9 @@ window_customize_draw(void *modedata, void *itemdata, else text = oe->text; screen_write_text(ctx, sx, sy, &grid_default_cell, "%s", text); + if (s->cy >= cy + sy - 1) + goto out; + screen_write_cursormove(ctx, s->cx, s->cy + 1, 0); if (s->cy >= cy + sy - 1) goto out; @@ -524,6 +527,9 @@ window_customize_draw(void *modedata, void *itemdata, if (s->cy > cy + sy - 1) goto out; } + screen_write_cursormove(ctx, s->cx, s->cy + 1, 0); + if (s->cy >= cy + sy - 1) + goto out; value = options_to_string(o, idx, 0); if (oe != NULL && idx == -1) { From ca18990826f29ced336a8164d923b5f08d6f3de8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 9 May 2020 14:33:25 +0100 Subject: [PATCH 0333/1006] Fix some warnings, from Jan Polensky. --- window-customize.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/window-customize.c b/window-customize.c index e07bc243..1030201d 100644 --- a/window-customize.c +++ b/window-customize.c @@ -268,7 +268,7 @@ window_customize_build_option(struct window_customize_modedata *data, if (oe != NULL && oe->unit != NULL) format_add(ft, "option_unit", "%s", oe->unit); else - format_add(ft, "option_unit", ""); + format_add(ft, "option_unit", "%s", ""); if (!array) { value = options_to_string(o, -1, 0); @@ -367,12 +367,12 @@ window_customize_build_options(struct window_customize_modedata *data, o = options_get(oo1, list[i]); else o = options_get(oo2, list[i]); - if (options_owner(o) == oo0) - scope = scope0; + if (options_owner(o) == oo2) + scope = scope2; else if (options_owner(o) == oo1) scope = scope1; - else if (options_owner(o) == oo2) - scope = scope2; + else + scope = scope0; window_customize_build_option(data, top, scope, o, ft, filter, fs); } @@ -391,12 +391,12 @@ window_customize_build_options(struct window_customize_modedata *data, o = options_get(oo1, name); else o = loop; - if (options_owner(o) == oo0) - scope = scope0; + if (options_owner(o) == oo2) + scope = scope2; else if (options_owner(o) == oo1) scope = scope1; - else if (options_owner(o) == oo2) - scope = scope2; + else + scope = scope0; window_customize_build_option(data, top, scope, o, ft, filter, fs); loop = options_next(loop); @@ -805,7 +805,7 @@ window_customize_set_option(struct client *c, struct window_customize_modedata * struct window_customize_itemdata *item, int global, int pane) { struct options_entry *o; - const struct options_table_entry *oe; + const struct options_table_entry *oe; struct options *oo; struct window_customize_itemdata *new_item; int flag, idx = item->idx; From 690d72adb378ee666b473da03a58aedc2d07b767 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 9 May 2020 14:38:00 +0100 Subject: [PATCH 0334/1006] Remove unused variables. --- cmd-set-option.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 6f4637d7..c6f83796 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -75,10 +75,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); struct cmd_find_state *target = cmdq_get_target(item); - struct client *loop; - struct session *s = target->s; - struct window *w; - struct window_pane *wp; struct options *oo; struct options_entry *parent, *o; char *name, *argument, *value = NULL, *cause; From 013d857ef8d0be9b862fd1496f35130f52306454 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 9 May 2020 15:29:14 +0100 Subject: [PATCH 0335/1006] Wrap all lines in customize mode, not just the description. --- screen-write.c | 40 +++++++++++++----- tmux.h | 4 +- window-customize.c | 101 ++++++++++++++++++++------------------------- 3 files changed, 75 insertions(+), 70 deletions(-) diff --git a/screen-write.c b/screen-write.c index b168d0b6..9571cbec 100644 --- a/screen-write.c +++ b/screen-write.c @@ -361,15 +361,14 @@ screen_write_strlen(const char *fmt, ...) } /* Write string wrapped over lines. */ -void -screen_write_text(struct screen_write_ctx *ctx, u_int width, u_int lines, - const struct grid_cell *gcp, const char *fmt, ...) +int +screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width, + u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...) { struct screen *s = ctx->s; - u_int cx = s->cx, cy = s->cy; va_list ap; char *tmp; - u_int i, end, next, idx = 0, at; + u_int cy = s->cy, i, end, next, idx = 0, at, left; struct utf8_data *text; struct grid_cell gc; @@ -382,13 +381,14 @@ screen_write_text(struct screen_write_ctx *ctx, u_int width, u_int lines, text = utf8_fromcstr(tmp); free(tmp); - while (text[idx].size != 0) { + left = (cx + width) - s->cx; + for (;;) { /* Find the end of what can fit on the line. */ at = 0; for (end = idx; text[end].size != 0; end++) { if (text[end].size == 1 && text[end].data[0] == '\n') break; - if (at + text[end].width > width) + if (at + text[end].width > left) break; at += text[end].width; } @@ -422,14 +422,32 @@ screen_write_text(struct screen_write_ctx *ctx, u_int width, u_int lines, } /* If at the bottom, stop. */ - if (s->cy == cy + lines - 1) - break; - screen_write_cursormove(ctx, cx, s->cy + 1, 0); idx = next; + if (s->cy == cy + lines - 1 || text[idx].size == 0) + break; + + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + left = width; } - screen_write_cursormove(ctx, cx, s->cy, 0); + /* + * Fail if on the last line and there is more to come or at the end, or + * if the text was not entirely consumed. + */ + if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) || + text[idx].size != 0) { + free(text); + return (0); + } free(text); + + /* + * If no more to come, move to the next line. Otherwise, leave on + * the same line (except if at the end). + */ + if (!more || s->cx == cx + width) + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + return (1); } /* Write simple string (no maximum length). */ diff --git a/tmux.h b/tmux.h index b3b7cb33..5853030c 100644 --- a/tmux.h +++ b/tmux.h @@ -2471,8 +2471,8 @@ void screen_write_start_callback(struct screen_write_ctx *, struct screen *, void screen_write_stop(struct screen_write_ctx *); void screen_write_reset(struct screen_write_ctx *); size_t printflike(1, 2) screen_write_strlen(const char *, ...); -void printflike(5, 6) screen_write_text(struct screen_write_ctx *, u_int, u_int, - const struct grid_cell *, const char *, ...); +int printflike(7, 8) screen_write_text(struct screen_write_ctx *, u_int, u_int, + u_int, int, const struct grid_cell *, const char *, ...); void printflike(3, 4) screen_write_puts(struct screen_write_ctx *, const struct grid_cell *, const char *, ...); void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *, diff --git a/window-customize.c b/window-customize.c index 1030201d..e180b001 100644 --- a/window-customize.c +++ b/window-customize.c @@ -491,10 +491,10 @@ window_customize_draw(void *modedata, void *itemdata, text = "This option doesn't have a description."; else text = oe->text; - screen_write_text(ctx, sx, sy, &grid_default_cell, "%s", text); - if (s->cy >= cy + sy - 1) + if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s", + text)) goto out; - screen_write_cursormove(ctx, s->cx, s->cy + 1, 0); + screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ if (s->cy >= cy + sy - 1) goto out; @@ -509,25 +509,24 @@ window_customize_draw(void *modedata, void *itemdata, text = "session"; else text = "server"; - screen_write_text(ctx, sx, sy - (s->cy - cy), &grid_default_cell, - "This is a %s option.", text); - if (s->cy > cy + sy - 1) + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "This is a %s option.", text)) goto out; if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { if (idx != -1) { - screen_write_text(ctx, sx, sy - (s->cy - cy), - &grid_default_cell, - "This is an array option, index %u.", idx); + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), + 0, &grid_default_cell, + "This is an array option, index %u.", idx)) + goto out; } else { - screen_write_text(ctx, sx, sy - (s->cy - cy), - &grid_default_cell, "This is an array option."); + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), + 0, &grid_default_cell, "This is an array option.")) + goto out; } if (idx == -1) goto out; - if (s->cy > cy + sy - 1) - goto out; } - screen_write_cursormove(ctx, s->cx, s->cy + 1, 0); + screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ if (s->cy >= cy + sy - 1) goto out; @@ -539,19 +538,15 @@ window_customize_draw(void *modedata, void *itemdata, default_value = NULL; } } - screen_write_nputs(ctx, sx, &grid_default_cell, "Option value: %s%s%s", - value, space, unit); - screen_write_cursormove(ctx, cx, s->cy + 1, 0); - if (s->cy > cy + sy - 1) + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "Option value: %s%s%s", value, space, unit)) goto out; - if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) { expanded = format_expand(ft, value); if (strcmp(expanded, value) != 0) { - screen_write_nputs(ctx, sx, &grid_default_cell, - "This expands to: %s", expanded); - screen_write_cursormove(ctx, cx, s->cy + 1, 0); - if (s->cy > cy + sy - 1) + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), + 0, &grid_default_cell, "This expands to: %s", + expanded)) goto out; } free(expanded); @@ -562,44 +557,38 @@ window_customize_draw(void *modedata, void *itemdata, strlcat(choices, ", ", sizeof choices); } choices[strlen(choices) - 2] = '\0'; - screen_write_nputs(ctx, sx, &grid_default_cell, - "Available values are: %s", choices); - screen_write_cursormove(ctx, cx, s->cy + 1, 0); - if (s->cy > cy + sy - 1) + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "Available values are: %s", + choices)) goto out; } if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) { - screen_write_nputs(ctx, sx, &grid_default_cell, - "This is a colour option: "); - if (sx > 24) { - memcpy(&gc, &grid_default_cell, sizeof gc); - gc.fg = options_get_number(item->oo, name); - screen_write_nputs(ctx, sx - 24, &gc, "EXAMPLE"); - } - screen_write_cursormove(ctx, cx, s->cy + 1, 0); - if (s->cy > cy + sy - 1) + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1, + &grid_default_cell, "This is a colour option: ")) + goto out; + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.fg = options_get_number(item->oo, name); + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc, + "EXAMPLE")) goto out; } if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) { - screen_write_nputs(ctx, sx, &grid_default_cell, - "This is a style option: "); - if (sx > 24) { - style_apply(&gc, item->oo, name, ft); - screen_write_nputs(ctx, sx - 24, &gc, "EXAMPLE"); - } - screen_write_cursormove(ctx, cx, s->cy + 1, 0); - if (s->cy > cy + sy - 1) + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1, + &grid_default_cell, "This is a style option: ")) + goto out; + style_apply(&gc, item->oo, name, ft); + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc, + "EXAMPLE")) goto out; } if (default_value != NULL) { - screen_write_nputs(ctx, sx, &grid_default_cell, - "The default is: %s%s%s", default_value, space, unit); - screen_write_cursormove(ctx, cx, s->cy + 1, 0); - if (s->cy > cy + sy - 1) + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "The default is: %s%s%s", default_value, + space, unit)) goto out; } - screen_write_cursormove(ctx, cx, s->cy + 1, 0); + screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ if (s->cy > cy + sy - 1) goto out; if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { @@ -626,11 +615,10 @@ window_customize_draw(void *modedata, void *itemdata, parent = options_get_only(wo, name); if (parent != NULL) { value = options_to_string(parent, -1 , 0); - screen_write_nputs(ctx, sx, &grid_default_cell, + if (!screen_write_text(ctx, s->cx, sx, + sy - (s->cy - cy), 0, &grid_default_cell, "Window value (from window %u): %s%s%s", fs.wl->idx, - value, space, unit); - screen_write_cursormove(ctx, cx, s->cy + 1, 0); - if (s->cy > cy + sy - 1) + value, space, unit)) goto out; } } @@ -638,10 +626,9 @@ window_customize_draw(void *modedata, void *itemdata, parent = options_get_only(go, name); if (parent != NULL) { value = options_to_string(parent, -1 , 0); - screen_write_nputs(ctx, sx, &grid_default_cell, - "Global value: %s%s%s", value, space, unit); - screen_write_cursormove(ctx, cx, s->cy + 1, 0); - if (s->cy > cy + sy - 1) + if (!screen_write_text(ctx, s->cx, sx, + sy - (s->cy - cy), 0, &grid_default_cell, + "Global value: %s%s%s", value, space, unit)) goto out; } } From 532d06c39985536e14ab09bb19c69d93341f4b64 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 9 May 2020 16:07:30 +0100 Subject: [PATCH 0336/1006] Initialize return code in case something mysterious happens. --- cmd-run-shell.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 82d4a1a2..3792aaae 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -190,7 +190,8 @@ cmd_run_shell_callback(struct job *job) retcode = WTERMSIG(status); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); retcode += 128; - } + } else + retcode = 0; if (msg != NULL) cmd_run_shell_print(job, msg); free(msg); From 79a9a7b931780b83add9d296a0dfc3745d4f0996 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 9 May 2020 16:14:45 +0100 Subject: [PATCH 0337/1006] Fix next-matching-bracket logic, from Chris Barber. --- window-copy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/window-copy.c b/window-copy.c index f500a65e..4104d2ad 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1365,9 +1365,9 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) px = data->cx; py = screen_hsize(s) + data->cy - data->oy; grid_get_cell(s->grid, px, py, &gc); - if (gc.data.size != 1 || - (gc.flags & GRID_FLAG_PADDING) || - strchr(close, *gc.data.data) == NULL) + if (gc.data.size == 1 && + (~gc.flags & GRID_FLAG_PADDING) && + strchr(close, *gc.data.data) != NULL) window_copy_scroll_to(wme, sx, sy); break; } From 5fa377d9273ee609784acc538a5defa09feb3095 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 10 May 2020 09:59:34 +0100 Subject: [PATCH 0338/1006] Do not loop forever when search finds an empty match, GitHub issue 2203. --- window-copy.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index 4104d2ad..95b4cc6e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2445,7 +2445,8 @@ window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, len += gd->sx; } - if (regexec(reg, buf, 1, ®match, eflags) == 0) { + if (regexec(reg, buf, 1, ®match, eflags) == 0 && + regmatch.rm_so != regmatch.rm_eo) { foundx = first; foundy = py; window_copy_cstrtocellpos(gd, len, &foundx, &foundy, @@ -2547,6 +2548,8 @@ window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, foundy = py; oldx = first; while (regexec(preg, buf + px, 1, ®match, eflags) == 0) { + if (regmatch.rm_so == regmatch.rm_eo) + break; window_copy_cstrtocellpos(gd, len, &foundx, &foundy, buf + px + regmatch.rm_so); if (foundy > py || foundx >= last) From d01e7aac89fefff31e668ac2d668c84c345b9e67 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 10 May 2020 16:52:46 +0100 Subject: [PATCH 0339/1006] Add a -D flag to ask tmux not to daemonize, useful both for running a debugger (lldb does not have follow-fork-mode) and for running with a managed supervisor init system. GitHub issue 2190. --- proc.c | 9 +++++++-- server-client.c | 15 +++++++++++++-- server.c | 44 +++++++++++++++++++++++++++----------------- tmux.1 | 13 ++++++++++++- tmux.c | 9 +++++++-- tmux.h | 1 + tty.c | 7 ++++++- 7 files changed, 73 insertions(+), 25 deletions(-) diff --git a/proc.c b/proc.c index 111c3730..f3d6063d 100644 --- a/proc.c +++ b/proc.c @@ -35,6 +35,7 @@ struct tmuxproc { void (*signalcb)(int); + struct event ev_sigint; struct event ev_sighup; struct event ev_sigchld; struct event ev_sigcont; @@ -219,10 +220,13 @@ proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int)) sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_IGN; - sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); + sigaction(SIGTTIN, &sa, NULL); + sigaction(SIGTTOU, &sa, NULL); + signal_set(&tp->ev_sigint, SIGINT, proc_signal_cb, tp); + signal_add(&tp->ev_sigint, NULL); signal_set(&tp->ev_sighup, SIGHUP, proc_signal_cb, tp); signal_add(&tp->ev_sighup, NULL); signal_set(&tp->ev_sigchld, SIGCHLD, proc_signal_cb, tp); @@ -249,10 +253,10 @@ proc_clear_signals(struct tmuxproc *tp, int defaults) sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_DFL; - sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); + signal_del(&tp->ev_sigint); signal_del(&tp->ev_sighup); signal_del(&tp->ev_sigchld); signal_del(&tp->ev_sigcont); @@ -262,6 +266,7 @@ proc_clear_signals(struct tmuxproc *tp, int defaults) signal_del(&tp->ev_sigwinch); if (defaults) { + sigaction(SIGINT, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); sigaction(SIGCONT, &sa, NULL); diff --git a/server-client.c b/server-client.c index 794cb41e..c9250491 100644 --- a/server-client.c +++ b/server-client.c @@ -236,11 +236,22 @@ server_client_create(int fd) int server_client_open(struct client *c, char **cause) { + const char *ttynam = _PATH_TTY; + if (c->flags & CLIENT_CONTROL) return (0); - if (strcmp(c->ttyname, "/dev/tty") == 0) { - *cause = xstrdup("can't use /dev/tty"); + if (strcmp(c->ttyname, ttynam) || + ((isatty(STDIN_FILENO) && + (ttynam = ttyname(STDIN_FILENO)) != NULL && + strcmp(c->ttyname, ttynam) == 0) || + (isatty(STDOUT_FILENO) && + (ttynam = ttyname(STDOUT_FILENO)) != NULL && + strcmp(c->ttyname, ttynam) == 0) || + (isatty(STDERR_FILENO) && + (ttynam = ttyname(STDERR_FILENO)) != NULL && + strcmp(c->ttyname, ttynam) == 0))) { + xasprintf(cause, "can't use %s", c->ttyname); return (-1); } diff --git a/server.c b/server.c index 2f262f3c..c451989c 100644 --- a/server.c +++ b/server.c @@ -160,29 +160,35 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, struct client *c; char *cause = NULL; - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) - fatal("socketpair failed"); - server_client_flags = flags; - sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); - switch (fork()) { - case -1: - fatal("fork failed"); - case 0: - break; - default: - sigprocmask(SIG_SETMASK, &oldset, NULL); - close(pair[1]); - return (pair[0]); + + if (~flags & CLIENT_NOFORK) { + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) + fatal("socketpair failed"); + + switch (fork()) { + case -1: + fatal("fork failed"); + case 0: + break; + default: + sigprocmask(SIG_SETMASK, &oldset, NULL); + close(pair[1]); + return (pair[0]); + } + close(pair[0]); + if (daemon(1, 0) != 0) + fatal("daemon failed"); } - close(pair[0]); - if (daemon(1, 0) != 0) - fatal("daemon failed"); + + server_client_flags = flags; proc_clear_signals(client, 0); + if (event_reinit(base) != 0) fatalx("event_reinit failed"); server_proc = proc_start("server"); + proc_set_signals(server_proc, server_signal); sigprocmask(SIG_SETMASK, &oldset, NULL); @@ -204,7 +210,10 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, server_fd = server_create_socket(flags, &cause); if (server_fd != -1) server_update_socket(); - c = server_client_create(pair[1]); + if (~flags & CLIENT_NOFORK) + c = server_client_create(pair[1]); + else + options_set_number(global_options, "exit-empty", 0); if (lockfd >= 0) { unlink(lockfile); @@ -395,6 +404,7 @@ server_signal(int sig) log_debug("%s: %s", __func__, strsignal(sig)); switch (sig) { + case SIGINT: case SIGTERM: server_exit = 1; server_send_exit(); diff --git a/tmux.1 b/tmux.1 index 56322d85..d93f34aa 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 2CluvV +.Op Fl 2CDluvV .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name @@ -122,6 +122,17 @@ This option is for compatibility with when .Nm is used as a login shell. +.It Fl D +Do not start the +.Nm +server as a daemon. +This also turns the +.Ic exit-empty +option off. +With +.Fl D , +.Ar command +may not be specified. .It Fl f Ar file Specify an alternative configuration file. By default, diff --git a/tmux.c b/tmux.c index 48b3b8b9..b600702c 100644 --- a/tmux.c +++ b/tmux.c @@ -54,7 +54,7 @@ static __dead void usage(void) { fprintf(stderr, - "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2CDluvV] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [-T features] [command [flags]]\n", getprogname()); exit(1); @@ -327,7 +327,7 @@ main(int argc, char **argv) if (**argv == '-') flags = CLIENT_LOGIN; - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:T:uUvV")) != -1) { + while ((opt = getopt(argc, argv, "2c:CDdf:lL:qS:T:uUvV")) != -1) { switch (opt) { case '2': tty_add_features(&feat, "256", ":,"); @@ -335,6 +335,9 @@ main(int argc, char **argv) case 'c': shell_command = optarg; break; + case 'D': + flags |= CLIENT_NOFORK; + break; case 'C': if (flags & CLIENT_CONTROL) flags |= CLIENT_CONTROLCONTROL; @@ -378,6 +381,8 @@ main(int argc, char **argv) if (shell_command != NULL && argc != 0) usage(); + if ((flags & CLIENT_NOFORK) && argc != 0) + usage(); if ((ptm_fd = getptmfd()) == -1) err(1, "getptmfd"); diff --git a/tmux.h b/tmux.h index 5853030c..6a05e93d 100644 --- a/tmux.h +++ b/tmux.h @@ -1586,6 +1586,7 @@ struct client { #define CLIENT_DEFAULTSOCKET 0x8000000 #define CLIENT_STARTSERVER 0x10000000 #define CLIENT_REDRAWPANES 0x20000000 +#define CLIENT_NOFORK 0x40000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ diff --git a/tty.c b/tty.c index c8efeac7..a1e2f2c6 100644 --- a/tty.c +++ b/tty.c @@ -154,16 +154,21 @@ tty_read_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; struct client *c = tty->client; + const char *name = c->name; size_t size = EVBUFFER_LENGTH(tty->in); int nread; nread = evbuffer_read(tty->in, tty->fd, -1); if (nread == 0 || nread == -1) { + if (nread == 0) + log_debug("%s: read closed", name); + else + log_debug("%s: read error: %s", name, strerror(errno)); event_del(&tty->event_in); server_client_lost(tty->client); return; } - log_debug("%s: read %d bytes (already %zu)", c->name, nread, size); + log_debug("%s: read %d bytes (already %zu)", name, nread, size); while (tty_keys_next(tty)) ; From 0070313e286bc26141209798ebb5c76f39f25df4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 10 May 2020 16:57:33 +0100 Subject: [PATCH 0340/1006] Fix comparison of tty name. --- server-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index c9250491..6e24fdf3 100644 --- a/server-client.c +++ b/server-client.c @@ -241,7 +241,7 @@ server_client_open(struct client *c, char **cause) if (c->flags & CLIENT_CONTROL) return (0); - if (strcmp(c->ttyname, ttynam) || + if (strcmp(c->ttyname, ttynam) == 0|| ((isatty(STDIN_FILENO) && (ttynam = ttyname(STDIN_FILENO)) != NULL && strcmp(c->ttyname, ttynam) == 0) || From 8502517d302363cf0d18c4aedf80a2761db24c1c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 10 May 2020 17:06:31 +0100 Subject: [PATCH 0341/1006] Add to CHANGES. --- CHANGES | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGES b/CHANGES index e5e966ed..16586ec6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,15 @@ CHANGES FROM 3.1b TO 3.2 +* Add a -D flag to make the tmux server run in the foreground and not as a + daemon. + +* Do not loop forever in copy mode when search finds an empty match. + +* Fix the next-matching-bracket logic when using vi(1) keys. + +* Add a customize mode where options may be browsed and changed, includes + adding a brief description of each option. Bound to "C-b C" by default. + * Change message log (C-b ~) so there is one for the server rather than one per client and it remains after detach, and make it useful by logging every command. From c489bf0a1e5d1ef7dfd886a95b638fa7bfa1fbe2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 11 May 2020 09:18:00 +0100 Subject: [PATCH 0342/1006] Support embedded styles in the display-message message, GitHub issue 2206. --- alerts.c | 8 +++++--- cmd-display-message.c | 2 +- cmd-list-keys.c | 2 +- cmd-queue.c | 2 +- mode-tree.c | 2 +- status.c | 8 ++++++-- tmux.h | 4 +++- window-customize.c | 2 +- 8 files changed, 19 insertions(+), 11 deletions(-) diff --git a/alerts.c b/alerts.c index 6fe88a09..9675934a 100644 --- a/alerts.c +++ b/alerts.c @@ -316,8 +316,10 @@ 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, "%s in current window", type); - else - status_message_set(c, "%s in window %d", type, wl->idx); + status_message_set(c, 1, "%s in current window", type); + else { + status_message_set(c, 1, "%s in window %d", type, + wl->idx); + } } } diff --git a/cmd-display-message.c b/cmd-display-message.c index 4e69f03a..634f0a93 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -117,7 +117,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, "%s", msg); + status_message_set(tc, 0, "%s", msg); free(msg); format_free(ft); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 1141bbb5..4e7ba39c 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -112,7 +112,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, "%s%s%s", prefix, tmp, note); + status_message_set(tc, 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 26e2f2f9..a40053a6 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -856,7 +856,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) c->retval = 1; } else { *msg = toupper((u_char) *msg); - status_message_set(c, "%s", msg); + status_message_set(c, 1, "%s", msg); } free(msg); diff --git a/mode-tree.c b/mode-tree.c index 8a28be16..9ed2e102 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1139,7 +1139,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, "%s", error); + status_message_set(c, 1, "%s", error); } free(error); } diff --git a/status.c b/status.c index 375cad4d..b5fa0824 100644 --- a/status.c +++ b/status.c @@ -423,7 +423,7 @@ status_redraw(struct client *c) /* Set a status line message. */ void -status_message_set(struct client *c, const char *fmt, ...) +status_message_set(struct client *c, int ignore_styles, const char *fmt, ...) { struct timeval tv; va_list ap; @@ -433,6 +433,7 @@ status_message_set(struct client *c, const char *fmt, ...) status_push_screen(c); va_start(ap, fmt); + c->message_ignore_styles = ignore_styles; xvasprintf(&c->message_string, fmt, ap); va_end(ap); @@ -515,7 +516,10 @@ status_message_redraw(struct client *c) for (offset = 0; offset < c->tty.sx; offset++) screen_write_putc(&ctx, &gc, ' '); screen_write_cursormove(&ctx, 0, lines - 1, 0); - screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); + if (c->message_ignore_styles) + screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); + else + format_draw(&ctx, &gc, c->tty.sx, c->message_string, NULL); screen_write_stop(&ctx); if (grid_compare(sl->active->grid, old_screen.grid) == 0) { diff --git a/tmux.h b/tmux.h index 6a05e93d..eab3e34a 100644 --- a/tmux.h +++ b/tmux.h @@ -1607,6 +1607,7 @@ struct client { uint64_t redraw_panes; + int message_ignore_styles; char *message_string; struct event message_timer; @@ -2354,7 +2355,8 @@ 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(2, 3) status_message_set(struct client *, const char *, ...); +void printflike(3, 4) status_message_set(struct client *, int, const char *, + ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, const char *, const char *, diff --git a/window-customize.c b/window-customize.c index e180b001..4efb3b1e 100644 --- a/window-customize.c +++ b/window-customize.c @@ -772,7 +772,7 @@ window_customize_set_callback(struct client *c, void *itemdata, const char *s, fail: *cause = toupper((u_char)*cause); - status_message_set(c, "%s", cause); + status_message_set(c, 1, "%s", cause); free(cause); return (0); } From 5a34f51d337aefa723d3e5366727f88c723b293a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 12 May 2020 08:57:55 +0100 Subject: [PATCH 0343/1006] Include key bindings in customize mode. --- cmd-list-keys.c | 8 +- mode-tree.c | 24 +++ tmux.1 | 10 +- tmux.h | 3 + window-customize.c | 510 ++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 496 insertions(+), 59 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 4e7ba39c..60ef73af 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -68,7 +68,8 @@ cmd_list_keys_get_width(const char *tablename, key_code only) while (bd != NULL) { if ((only != KEYC_UNKNOWN && bd->key != only) || KEYC_IS_MOUSE(bd->key) || - bd->note == NULL) { + bd->note == NULL || + *bd->note == '\0') { bd = key_bindings_next(table, bd); continue; } @@ -99,14 +100,15 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, while (bd != NULL) { if ((only != KEYC_UNKNOWN && bd->key != only) || KEYC_IS_MOUSE(bd->key) || - (bd->note == NULL && !args_has(args, 'a'))) { + ((bd->note == NULL || *bd->note == '\0') && + !args_has(args, 'a'))) { bd = key_bindings_next(table, bd); continue; } found = 1; key = key_string_lookup_key(bd->key); - if (bd->note == NULL) + if (bd->note == NULL || *bd->note == '\0') note = cmd_list_print(bd->cmdlist, 1); else note = xstrdup(bd->note); diff --git a/mode-tree.c b/mode-tree.c index 9ed2e102..0b454468 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -80,6 +80,7 @@ struct mode_tree_item { int expanded; int tagged; + int draw_as_parent; struct mode_tree_list children; TAILQ_ENTRY(mode_tree_item) entry; @@ -248,6 +249,12 @@ mode_tree_get_current(struct mode_tree_data *mtd) return (mtd->line_list[mtd->current].item->itemdata); } +const char * +mode_tree_get_current_name(struct mode_tree_data *mtd) +{ + return (mtd->line_list[mtd->current].item->name); +} + void mode_tree_expand_current(struct mode_tree_data *mtd) { @@ -257,6 +264,15 @@ mode_tree_expand_current(struct mode_tree_data *mtd) } } +void +mode_tree_collapse_current(struct mode_tree_data *mtd) +{ + if (mtd->line_list[mtd->current].item->expanded) { + mtd->line_list[mtd->current].item->expanded = 0; + mode_tree_build(mtd); + } +} + static int mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found) { @@ -543,6 +559,12 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, return (mti); } +void +mode_tree_draw_as_parent(struct mode_tree_item *mti) +{ + mti->draw_as_parent = 1; +} + void mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti) { @@ -683,6 +705,8 @@ mode_tree_draw(struct mode_tree_data *mtd) line = &mtd->line_list[mtd->current]; mti = line->item; + if (mti->draw_as_parent) + mti = mti->parent; screen_write_cursormove(&ctx, 0, h, 0); screen_write_box(&ctx, w, sy - h); diff --git a/tmux.1 b/tmux.1 index d93f34aa..b6ac248f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1970,8 +1970,8 @@ This command works only if at least one client is attached. .Op Fl t Ar target-pane .Op Ar template .Xc -Put a pane into customize mode, where options may be browsed and modified from a -list. +Put a pane into customize mode, where options and key bindings may be browsed +and modified from a list. Option values in the list are shown for the active pane in the current window. .Fl Z zooms the pane. @@ -1985,11 +1985,11 @@ The following keys may be used in customize mode: .It Li "-" Ta "Collapse selected item" .It Li "M-+" Ta "Expand all items" .It Li "M--" Ta "Collapse all items" -.It Li "s" Ta "Set pane, window, session or global option value" +.It Li "s" Ta "Set option value or key attribute" .It Li "S" Ta "Set global option value" .It Li "w" Ta "Set window option value, if option is for pane and window" -.It Li "u" Ta "Unset an option (set to default value if global)" -.It Li "U" Ta "Unset tagged options" +.It Li "u" Ta "Unset an option (set to default value if global) or unbind a key" +.It Li "U" Ta "Unset tagged options and unbind tagged keys" .It Li "C-s" Ta "Search by name" .It Li "n" Ta "Repeat last search" .It Li "t" Ta "Toggle if item is tagged" diff --git a/tmux.h b/tmux.h index eab3e34a..4b6bacf2 100644 --- a/tmux.h +++ b/tmux.h @@ -2698,7 +2698,9 @@ typedef u_int (*mode_tree_height_cb)(void *, u_int); typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code); u_int mode_tree_count_tagged(struct mode_tree_data *); void *mode_tree_get_current(struct mode_tree_data *); +const char *mode_tree_get_current_name(struct mode_tree_data *); void mode_tree_expand_current(struct mode_tree_data *); +void mode_tree_collapse_current(struct mode_tree_data *); void mode_tree_expand(struct mode_tree_data *, uint64_t); int mode_tree_set_current(struct mode_tree_data *, uint64_t); void mode_tree_each_tagged(struct mode_tree_data *, mode_tree_each_cb, @@ -2716,6 +2718,7 @@ void mode_tree_resize(struct mode_tree_data *, u_int, u_int); struct mode_tree_item *mode_tree_add(struct mode_tree_data *, struct mode_tree_item *, void *, uint64_t, const char *, const char *, int); +void mode_tree_draw_as_parent(struct mode_tree_item *); void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *); void mode_tree_draw(struct mode_tree_data *); int mode_tree_key(struct mode_tree_data *, struct client *, key_code *, diff --git a/window-customize.c b/window-customize.c index 4efb3b1e..ee4bf4ba 100644 --- a/window-customize.c +++ b/window-customize.c @@ -34,9 +34,13 @@ static void window_customize_key(struct window_mode_entry *, struct winlink *, key_code, struct mouse_event *); #define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \ - "#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \ - "#[ignore]" \ - "#{option_value}#{?option_unit, #{option_unit},}" + "#{?is_option," \ + "#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \ + "#[ignore]" \ + "#{option_value}#{?option_unit, #{option_unit},}" \ + "," \ + "#{key}" \ + "}" static const struct menu_item window_customize_menu_items[] = { { "Select", '\r', NULL }, @@ -63,6 +67,7 @@ const struct window_mode window_customize_mode = { enum window_customize_scope { WINDOW_CUSTOMIZE_NONE, + WINDOW_CUSTOMIZE_KEY, WINDOW_CUSTOMIZE_SERVER, WINDOW_CUSTOMIZE_GLOBAL_SESSION, WINDOW_CUSTOMIZE_SESSION, @@ -74,6 +79,10 @@ enum window_customize_scope { struct window_customize_itemdata { struct window_customize_modedata *data; enum window_customize_scope scope; + + char *table; + key_code key; + struct options *oo; char *name; int idx; @@ -82,7 +91,7 @@ struct window_customize_itemdata { struct window_customize_modedata { struct window_pane *wp; int dead; - int references; + int references; struct mode_tree_data *data; char *format; @@ -103,7 +112,7 @@ window_customize_get_tag(struct options_entry *o, int idx, if (oe == NULL) return ((uint64_t)o); offset = ((char *)oe - (char *)options_table) / sizeof *options_table; - return ((offset << 32) | ((idx + 1) << 1) | 1); + return ((2ULL << 62)|(offset << 32)|((idx + 1) << 1)|1); } static struct options * @@ -112,6 +121,7 @@ window_customize_get_tree(enum window_customize_scope scope, { switch (scope) { case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_KEY: return (NULL); case WINDOW_CUSTOMIZE_SERVER: return (global_options); @@ -145,6 +155,27 @@ window_customize_check_item(struct window_customize_modedata *data, return (item->oo == window_customize_get_tree(item->scope, fsp)); } +static int +window_customize_get_key(struct window_customize_itemdata *item, + struct key_table **ktp, struct key_binding **bdp) +{ + struct key_table *kt; + struct key_binding *bd; + + kt = key_bindings_get_table(item->table, 0); + if (kt == NULL) + return (0); + bd = key_bindings_get(kt, item->key); + if (bd == NULL) + return (0); + + if (ktp != NULL) + *ktp = kt; + if (bdp != NULL) + *bdp = bd; + return (1); +} + static char * window_customize_scope_text(enum window_customize_scope scope, struct cmd_find_state *fs) @@ -154,6 +185,7 @@ window_customize_scope_text(enum window_customize_scope scope, switch (scope) { case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_KEY: case WINDOW_CUSTOMIZE_SERVER: case WINDOW_CUSTOMIZE_GLOBAL_SESSION: case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: @@ -187,6 +219,7 @@ window_customize_add_item(struct window_customize_modedata *data) static void window_customize_free_item(struct window_customize_itemdata *item) { + free(item->table); free(item->name); free(item); } @@ -403,15 +436,93 @@ window_customize_build_options(struct window_customize_modedata *data, } } +static void +window_customize_build_keys(struct window_customize_modedata *data, + struct key_table *kt, struct format_tree *ft, const char *filter, + struct cmd_find_state *fs, u_int number) +{ + struct mode_tree_item *top, *child, *mti; + struct window_customize_itemdata *item; + struct key_binding *bd; + char *title, *text, *tmp, *expanded; + const char *flag; + uint64_t tag; + + tag = (1ULL << 62)|((uint64_t)number << 54)|1; + + xasprintf(&title, "Key Table - %s", kt->name); + top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0); + free(title); + + ft = format_create_from_state(NULL, NULL, fs); + format_add(ft, "is_option", "0"); + format_add(ft, "is_key", "1"); + + bd = key_bindings_first(kt); + while (bd != NULL) { + format_add(ft, "key", "%s", key_string_lookup_key(bd->key)); + if (bd->note != NULL) + format_add(ft, "key_note", "%s", bd->note); + if (filter != NULL) { + expanded = format_expand(ft, filter); + if (!format_true(expanded)) { + free(expanded); + continue; + } + free(expanded); + } + + item = window_customize_add_item(data); + item->scope = WINDOW_CUSTOMIZE_KEY; + item->table = xstrdup(kt->name); + item->key = bd->key; + + expanded = format_expand(ft, data->format); + child = mode_tree_add(data->data, top, item, (uint64_t)bd, + expanded, NULL, 0); + free(expanded); + + tmp = cmd_list_print(bd->cmdlist, 0); + xasprintf(&text, "#[ignore]%s", tmp); + free(tmp); + mti = mode_tree_add(data->data, child, item, + tag|(bd->key << 3)|(0 << 1)|1, "Command", text, -1); + mode_tree_draw_as_parent(mti); + free(text); + + if (bd->note != NULL) + xasprintf(&text, "#[ignore]%s", bd->note); + else + text = xstrdup(""); + mti = mode_tree_add(data->data, child, item, + tag|(bd->key << 3)|(1 << 1)|1, "Note", text, -1); + mode_tree_draw_as_parent(mti); + free(text); + + if (bd->flags & KEY_BINDING_REPEAT) + flag = "on"; + else + flag = "off"; + mti = mode_tree_add(data->data, child, item, + tag|(bd->key << 3)|(2 << 1)|1, "Repeat", flag, -1); + mode_tree_draw_as_parent(mti); + + bd = key_bindings_next(kt, bd); + } + + format_free(ft); +} + static void window_customize_build(void *modedata, __unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag, const char *filter) { struct window_customize_modedata *data = modedata; - struct cmd_find_state fs; - struct format_tree *ft; - u_int i; + struct cmd_find_state fs; + struct format_tree *ft; + u_int i; + struct key_table *kt; for (i = 0; i < data->item_size; i++) window_customize_free_item(data->item_list[i]); @@ -425,35 +536,94 @@ window_customize_build(void *modedata, cmd_find_from_pane(&fs, data->wp, 0); ft = format_create_from_state(NULL, NULL, &fs); + format_add(ft, "is_option", "1"); + format_add(ft, "is_key", "0"); window_customize_build_options(data, "Server Options", - (1ULL << 63)|OPTIONS_TABLE_SERVER, + (3ULL << 62)|(OPTIONS_TABLE_SERVER << 1)|1, WINDOW_CUSTOMIZE_SERVER, global_options, WINDOW_CUSTOMIZE_NONE, NULL, WINDOW_CUSTOMIZE_NONE, NULL, ft, filter, &fs); window_customize_build_options(data, "Session Options", - (1ULL << 63)|OPTIONS_TABLE_SESSION, + (3ULL << 62)|(OPTIONS_TABLE_SESSION << 1)|1, WINDOW_CUSTOMIZE_GLOBAL_SESSION, global_s_options, WINDOW_CUSTOMIZE_SESSION, fs.s->options, WINDOW_CUSTOMIZE_NONE, NULL, ft, filter, &fs); window_customize_build_options(data, "Window & Pane Options", - (1ULL << 63)|OPTIONS_TABLE_WINDOW, + (3ULL << 62)|(OPTIONS_TABLE_WINDOW << 1)|1, WINDOW_CUSTOMIZE_GLOBAL_WINDOW, global_w_options, WINDOW_CUSTOMIZE_WINDOW, fs.w->options, WINDOW_CUSTOMIZE_PANE, fs.wp->options, ft, filter, &fs); + format_free(ft); + ft = format_create_from_state(NULL, NULL, &fs); + + i = 0; + kt = key_bindings_first_table(); + while (kt != NULL) { + window_customize_build_keys(data, kt, ft, filter, &fs, i); + if (++i == 256) + break; + kt = key_bindings_next_table(kt); + } + format_free(ft); } static void -window_customize_draw(void *modedata, void *itemdata, - struct screen_write_ctx *ctx, u_int sx, u_int sy) +window_customize_draw_key(__unused struct window_customize_modedata *data, + struct window_customize_itemdata *item, struct screen_write_ctx *ctx, + u_int sx, u_int sy) +{ + struct screen *s = ctx->s; + u_int cx = s->cx, cy = s->cy; + struct key_table *kt; + struct key_binding *bd; + const char *note, *period = ""; + char *tmp; + + if (item == NULL || !window_customize_get_key(item, &kt, &bd)) + return; + + note = bd->note; + if (note == NULL) + note = "There is no note for this key."; + if (*note != '\0' && note[strlen (note) - 1] != '.') + period = "."; + if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s%s", + note, period)) + return; + screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ + if (s->cy >= cy + sy - 1) + return; + + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "This key is in the %s table.", kt->name)) + return; + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "This key %s repeat.", + (bd->flags & KEY_BINDING_REPEAT) ? "does" : "does not")) + return; + screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ + if (s->cy >= cy + sy - 1) + return; + + tmp = cmd_list_print(bd->cmdlist, 0); + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "Command: %s", tmp)) { + free(tmp); + return; + } +} + +static void +window_customize_draw_option(struct window_customize_modedata *data, + struct window_customize_itemdata *item, struct screen_write_ctx *ctx, + u_int sx, u_int sy) { - struct window_customize_modedata *data = modedata; - struct window_customize_itemdata *item = itemdata; struct screen *s = ctx->s; u_int cx = s->cx, cy = s->cy; int idx; @@ -469,7 +639,7 @@ window_customize_draw(void *modedata, void *itemdata, struct cmd_find_state fs; struct format_tree *ft; - if (item == NULL || !window_customize_check_item(data, item, &fs)) + if (!window_customize_check_item(data, item, &fs)) return; name = item->name; idx = item->idx; @@ -622,7 +792,7 @@ window_customize_draw(void *modedata, void *itemdata, goto out; } } - if (go != NULL && options_owner(o) != go) { + if (go != NULL && options_owner(o) != go) { parent = options_get_only(go, name); if (parent != NULL) { value = options_to_string(parent, -1 , 0); @@ -639,6 +809,22 @@ out: format_free(ft); } +static void +window_customize_draw(void *modedata, void *itemdata, + struct screen_write_ctx *ctx, u_int sx, u_int sy) +{ + struct window_customize_modedata *data = modedata; + struct window_customize_itemdata *item = itemdata; + + if (item == NULL) + return; + + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_draw_key(data, item, ctx, sx, sy); + else + window_customize_draw_option(data, item, ctx, sx, sy); +} + static void window_customize_menu(void *modedata, struct client *c, key_code key) { @@ -727,14 +913,30 @@ window_customize_resize(struct window_mode_entry *wme, u_int sx, u_int sy) mode_tree_resize(data->data, sx, sy); } +static void +window_customize_free_callback(void *modedata) +{ + window_customize_destroy(modedata); +} + +static void +window_customize_free_item_callback(void *itemdata) +{ + struct window_customize_itemdata *item = itemdata; + struct window_customize_modedata *data = item->data; + + window_customize_free_item(item); + window_customize_destroy(data); +} + static int -window_customize_set_callback(struct client *c, void *itemdata, const char *s, - __unused int done) +window_customize_set_option_callback(struct client *c, void *itemdata, + const char *s, __unused int done) { struct window_customize_itemdata *item = itemdata; struct window_customize_modedata *data = item->data; struct options_entry *o; - const struct options_table_entry *oe; + const struct options_table_entry *oe; struct options *oo = item->oo; const char *name = item->name; char *cause; @@ -778,17 +980,8 @@ fail: } static void -window_customize_set_free(void *itemdata) -{ - struct window_customize_itemdata *item = itemdata; - struct window_customize_modedata *data = item->data; - - window_customize_free_item(item); - window_customize_destroy(data); -} - -static void -window_customize_set_option(struct client *c, struct window_customize_modedata *data, +window_customize_set_option(struct client *c, + struct window_customize_modedata *data, struct window_customize_itemdata *item, int global, int pane) { struct options_entry *o; @@ -807,7 +1000,6 @@ window_customize_set_option(struct client *c, struct window_customize_modedata * o = options_get(item->oo, name); if (o == NULL) return; - value = options_to_string(o, idx, 0); oe = options_table_entry(o); if (~oe->scope & OPTIONS_TABLE_PANE) @@ -819,6 +1011,7 @@ window_customize_set_option(struct client *c, struct window_customize_modedata * if (global) { switch (item->scope) { case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_KEY: case WINDOW_CUSTOMIZE_SERVER: case WINDOW_CUSTOMIZE_GLOBAL_SESSION: case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: @@ -835,6 +1028,7 @@ window_customize_set_option(struct client *c, struct window_customize_modedata * } else { switch (item->scope) { case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_KEY: case WINDOW_CUSTOMIZE_SERVER: case WINDOW_CUSTOMIZE_SESSION: scope = item->scope; @@ -863,7 +1057,7 @@ window_customize_set_option(struct client *c, struct window_customize_modedata * oo = window_customize_get_tree(scope, &fs); } - if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) { + if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) { flag = options_get_number(oo, name); options_set_number(oo, name, !flag); } else if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) { @@ -891,7 +1085,9 @@ window_customize_set_option(struct client *c, struct window_customize_modedata * xasprintf(&prompt, "(%s%s%s) ", name, space, text); free(text); - new_item = xmalloc(sizeof *new_item); + value = options_to_string(o, idx, 0); + + new_item = xcalloc(1, sizeof *new_item); new_item->data = data; new_item->scope = scope; new_item->oo = oo; @@ -899,10 +1095,14 @@ window_customize_set_option(struct client *c, struct window_customize_modedata * new_item->idx = idx; data->references++; - status_prompt_set(c, prompt, value, window_customize_set_callback, - window_customize_set_free, new_item, PROMPT_NOFORMAT); + status_prompt_set(c, prompt, value, + window_customize_set_option_callback, + window_customize_free_item_callback, new_item, + PROMPT_NOFORMAT); + + free(prompt); + free(value); } - free(value); } static void @@ -915,11 +1115,12 @@ window_customize_unset_option(struct window_customize_modedata *data, if (item == NULL || !window_customize_check_item(data, item, NULL)) return; - o = options_get(item->oo, item->name); + o = options_get(item->oo, item->name); if (o == NULL) return; if (item->idx != -1) { - mode_tree_up(data->data, 0); + if (item == mode_tree_get_current(data->data)) + mode_tree_up(data->data, 0); options_array_set(o, item->idx, NULL, 0, NULL); return; } @@ -933,11 +1134,198 @@ window_customize_unset_option(struct window_customize_modedata *data, options_default(options_owner(o), oe); } +static int +window_customize_set_command_callback(struct client *c, void *itemdata, + const char *s, __unused int done) +{ + struct window_customize_itemdata *item = itemdata; + struct window_customize_modedata *data = item->data; + struct key_binding *bd; + struct cmd_parse_result *pr; + char *error; + + if (s == NULL || *s == '\0' || data->dead) + return (0); + if (item == NULL || !window_customize_get_key(item, NULL, &bd)) + return (0); + + pr = cmd_parse_from_string(s, NULL); + switch (pr->status) { + case CMD_PARSE_EMPTY: + error = xstrdup("empty command"); + goto fail; + case CMD_PARSE_ERROR: + error = pr->error; + goto fail; + case CMD_PARSE_SUCCESS: + break; + } + cmd_list_free(bd->cmdlist); + bd->cmdlist = pr->cmdlist; + + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (0); + +fail: + *error = toupper((u_char)*error); + status_message_set(c, 1, "%s", error); + free(error); + return (0); +} + +static int +window_customize_set_note_callback(__unused struct client *c, void *itemdata, + const char *s, __unused int done) +{ + struct window_customize_itemdata *item = itemdata; + struct window_customize_modedata *data = item->data; + struct key_binding *bd; + + if (s == NULL || *s == '\0' || data->dead) + return (0); + if (item == NULL || !window_customize_get_key(item, NULL, &bd)) + return (0); + + free((void *)bd->note); + bd->note = xstrdup(s); + + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (0); +} + +static void +window_customize_set_key(struct client *c, + struct window_customize_modedata *data, + struct window_customize_itemdata *item) +{ + key_code key = item->key; + struct key_binding *bd; + const char *s; + char *prompt, *value; + struct window_customize_itemdata *new_item; + + if (item == NULL || !window_customize_get_key(item, NULL, &bd)) + return; + + s = mode_tree_get_current_name(data->data); + if (strcmp(s, "Repeat") == 0) + bd->flags ^= KEY_BINDING_REPEAT; + else if (strcmp(s, "Command") == 0) { + xasprintf(&prompt, "(%s) ", key_string_lookup_key(key)); + value = cmd_list_print(bd->cmdlist, 0); + + new_item = xcalloc(1, sizeof *new_item); + new_item->data = data; + new_item->scope = item->scope; + new_item->table = xstrdup(item->table); + new_item->key = key; + + data->references++; + status_prompt_set(c, prompt, value, + window_customize_set_command_callback, + window_customize_free_item_callback, new_item, + PROMPT_NOFORMAT); + free(prompt); + free(value); + } else if (strcmp(s, "Note") == 0) { + xasprintf(&prompt, "(%s) ", key_string_lookup_key(key)); + + new_item = xcalloc(1, sizeof *new_item); + new_item->data = data; + new_item->scope = item->scope; + new_item->table = xstrdup(item->table); + new_item->key = key; + + data->references++; + status_prompt_set(c, prompt, (bd->note == NULL ? "" : bd->note), + window_customize_set_note_callback, + window_customize_free_item_callback, new_item, + PROMPT_NOFORMAT); + free(prompt); + } +} + +static void +window_customize_unset_key(struct window_customize_modedata *data, + struct window_customize_itemdata *item) +{ + struct key_table *kt; + struct key_binding *bd; + + if (item == NULL || !window_customize_get_key(item, &kt, &bd)) + return; + + if (item == mode_tree_get_current(data->data)) { + mode_tree_collapse_current(data->data); + mode_tree_up(data->data, 0); + } + key_bindings_remove(kt->name, bd->key); +} + static void window_customize_unset_each(void *modedata, void *itemdata, __unused struct client *c, __unused key_code key) { - window_customize_unset_option(modedata, itemdata); + struct window_customize_itemdata *item = itemdata; + + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_unset_key(modedata, item); + else { + window_customize_unset_option(modedata, item); + options_push_changes(item->name); + } +} + +static int +window_customize_unset_current_callback(__unused struct client *c, + void *modedata, const char *s, __unused int done) +{ + struct window_customize_modedata *data = modedata; + struct window_customize_itemdata *item; + + if (s == NULL || *s == '\0' || data->dead) + return (0); + if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') + return (0); + + item = mode_tree_get_current(data->data); + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_unset_key(data, item); + else { + window_customize_unset_option(data, item); + options_push_changes(item->name); + } + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (0); +} + +static int +window_customize_unset_tagged_callback(struct client *c, void *modedata, + const char *s, __unused int done) +{ + struct window_customize_modedata *data = modedata; + + if (s == NULL || *s == '\0' || data->dead) + return (0); + if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') + return (0); + + mode_tree_each_tagged(data->data, window_customize_unset_each, c, + KEYC_NONE, 0); + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (0); } static void @@ -949,6 +1337,8 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, struct window_customize_modedata *data = wme->data; struct window_customize_itemdata *item, *new_item; int finished; + char *prompt; + u_int tagged; item = mode_tree_get_current(data->data); finished = mode_tree_key(data->data, c, &key, m, NULL, NULL); @@ -960,12 +1350,16 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, case 's': if (item == NULL) break; - window_customize_set_option(c, data, item, 0, 1); - options_push_changes(item->name); + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_set_key(c, data, item); + else { + window_customize_set_option(c, data, item, 0, 1); + options_push_changes(item->name); + } mode_tree_build(data->data); break; case 'w': - if (item == NULL) + if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY) break; window_customize_set_option(c, data, item, 0, 0); options_push_changes(item->name); @@ -973,7 +1367,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, break; case 'S': case 'W': - if (item == NULL) + if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY) break; window_customize_set_option(c, data, item, 1, 0); options_push_changes(item->name); @@ -982,15 +1376,29 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, case 'u': if (item == NULL) break; - window_customize_unset_option(data, item); - options_push_changes(item->name); - mode_tree_build(data->data); + if (item->scope == WINDOW_CUSTOMIZE_KEY) { + xasprintf(&prompt, "Unbind key %s? ", + key_string_lookup_key(item->key)); + } else + xasprintf(&prompt, "Unset option %s? ", item->name); + data->references++; + status_prompt_set(c, prompt, "", + window_customize_unset_current_callback, + window_customize_free_callback, data, + PROMPT_SINGLE|PROMPT_NOFORMAT); + free(prompt); break; case 'U': - mode_tree_each_tagged(data->data, window_customize_unset_each, - c, KEYC_NONE, 1); - options_push_changes(item->name); - mode_tree_build(data->data); + tagged = mode_tree_count_tagged(data->data); + if (tagged == 0) + break; + xasprintf(&prompt, "Unset or unbind %u tagged? ", tagged); + data->references++; + status_prompt_set(c, prompt, "", + window_customize_unset_tagged_callback, + window_customize_free_callback, data, + PROMPT_SINGLE|PROMPT_NOFORMAT); + free(prompt); break; case 'H': data->hide_global = !data->hide_global; From 8d238491d01f21f5be5d6f8755126a0da6ad914c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 12 May 2020 10:20:58 +0100 Subject: [PATCH 0344/1006] Show default key bindings in customize mode. --- key-bindings.c | 59 +++++++++++++++++++++++++++++++++++++++------- tmux.h | 2 ++ window-customize.c | 32 ++++++++++++++++++------- 3 files changed, 77 insertions(+), 16 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index d6a72297..fc083db4 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -89,9 +89,8 @@ key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) } static void -key_bindings_free(struct key_table *table, struct key_binding *bd) +key_bindings_free(struct key_binding *bd) { - RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free((void *)bd->note); free(bd); @@ -110,6 +109,7 @@ key_bindings_get_table(const char *name, int create) table = xmalloc(sizeof *table); table->name = xstrdup(name); RB_INIT(&table->key_bindings); + RB_INIT(&table->default_key_bindings); table->references = 1; /* one reference in key_tables */ RB_INSERT(key_tables, &key_tables, table); @@ -138,8 +138,14 @@ key_bindings_unref_table(struct key_table *table) if (--table->references != 0) return; - RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) - key_bindings_free(table, bd); + RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) { + RB_REMOVE(key_bindings, &table->key_bindings, bd); + key_bindings_free(bd); + } + RB_FOREACH_SAFE(bd, key_bindings, &table->default_key_bindings, bd1) { + RB_REMOVE(key_bindings, &table->default_key_bindings, bd); + key_bindings_free(bd); + } free((void *)table->name); free(table); @@ -154,6 +160,15 @@ key_bindings_get(struct key_table *table, key_code key) return (RB_FIND(key_bindings, &table->key_bindings, &bd)); } +struct key_binding * +key_bindings_get_default(struct key_table *table, key_code key) +{ + struct key_binding bd; + + bd.key = key; + return (RB_FIND(key_bindings, &table->default_key_bindings, &bd)); +} + struct key_binding * key_bindings_first(struct key_table *table) { @@ -176,8 +191,10 @@ 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_XTERM); - if (bd != NULL) - key_bindings_free(table, bd); + if (bd != NULL) { + RB_REMOVE(key_bindings, &table->key_bindings, bd); + key_bindings_free(bd); + } bd = xcalloc(1, sizeof *bd); bd->key = key; @@ -203,9 +220,12 @@ key_bindings_remove(const char *name, key_code key) bd = key_bindings_get(table, key & ~KEYC_XTERM); if (bd == NULL) return; - key_bindings_free(table, bd); - if (RB_EMPTY(&table->key_bindings)) { + RB_REMOVE(key_bindings, &table->key_bindings, bd); + key_bindings_free(bd); + + if (RB_EMPTY(&table->key_bindings) && + RB_EMPTY(&table->default_key_bindings)) { RB_REMOVE(key_tables, &key_tables, table); key_bindings_unref_table(table); } @@ -228,6 +248,28 @@ key_bindings_remove_table(const char *name) } } +static enum cmd_retval +key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data) +{ + struct key_table *table; + struct key_binding *bd, *new_bd; + + RB_FOREACH(table, key_tables, &key_tables) { + RB_FOREACH(bd, key_bindings, &table->key_bindings) { + new_bd = xcalloc(1, sizeof *bd); + new_bd->key = bd->key; + if (bd->note != NULL) + new_bd->note = xstrdup(bd->note); + new_bd->flags = bd->flags; + new_bd->cmdlist = bd->cmdlist; + new_bd->cmdlist->references++; + RB_INSERT(key_bindings, &table->default_key_bindings, + new_bd); + } + } + return (CMD_RETURN_NORMAL); +} + void key_bindings_init(void) { @@ -525,6 +567,7 @@ key_bindings_init(void) cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL)); cmd_list_free(pr->cmdlist); } + cmdq_append(NULL, cmdq_get_callback(key_bindings_init_done, NULL)); } static enum cmd_retval diff --git a/tmux.h b/tmux.h index 4b6bacf2..f2675345 100644 --- a/tmux.h +++ b/tmux.h @@ -1669,6 +1669,7 @@ RB_HEAD(key_bindings, key_binding); struct key_table { const char *name; struct key_bindings key_bindings; + struct key_bindings default_key_bindings; u_int references; @@ -2245,6 +2246,7 @@ struct key_table *key_bindings_first_table(void); struct key_table *key_bindings_next_table(struct key_table *); void key_bindings_unref_table(struct key_table *); struct key_binding *key_bindings_get(struct key_table *, key_code); +struct key_binding *key_bindings_get_default(struct key_table *, key_code); struct key_binding *key_bindings_first(struct key_table *); struct key_binding *key_bindings_next(struct key_table *, struct key_binding *); void key_bindings_add(const char *, key_code, const char *, int, diff --git a/window-customize.c b/window-customize.c index ee4bf4ba..c74884d6 100644 --- a/window-customize.c +++ b/window-customize.c @@ -564,9 +564,12 @@ window_customize_build(void *modedata, i = 0; kt = key_bindings_first_table(); while (kt != NULL) { - window_customize_build_keys(data, kt, ft, filter, &fs, i); - if (++i == 256) - break; + if (!RB_EMPTY(&kt->key_bindings)) { + window_customize_build_keys(data, kt, ft, filter, &fs, + i); + if (++i == 256) + break; + } kt = key_bindings_next_table(kt); } @@ -581,9 +584,9 @@ window_customize_draw_key(__unused struct window_customize_modedata *data, struct screen *s = ctx->s; u_int cx = s->cx, cy = s->cy; struct key_table *kt; - struct key_binding *bd; + struct key_binding *bd, *default_bd; const char *note, *period = ""; - char *tmp; + char *cmd, *default_cmd; if (item == NULL || !window_customize_get_key(item, &kt, &bd)) return; @@ -611,12 +614,25 @@ window_customize_draw_key(__unused struct window_customize_modedata *data, if (s->cy >= cy + sy - 1) return; - tmp = cmd_list_print(bd->cmdlist, 0); + cmd = cmd_list_print(bd->cmdlist, 0); if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, - &grid_default_cell, "Command: %s", tmp)) { - free(tmp); + &grid_default_cell, "Command: %s", cmd)) { + free(cmd); return; } + default_bd = key_bindings_get_default(kt, bd->key); + if (default_bd != NULL) { + default_cmd = cmd_list_print(default_bd->cmdlist, 0); + if (strcmp(cmd, default_cmd) != 0 && + !screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "The default is: %s", default_cmd)) { + free(default_cmd); + free(cmd); + return; + } + free(default_cmd); + } + free(cmd); } static void From ba20e46bdc6d4624f54920c0efe753671c5184bb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 12 May 2020 10:36:31 +0100 Subject: [PATCH 0345/1006] Do not log NULL text. --- mode-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode-tree.c b/mode-tree.c index 0b454468..131830d6 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -528,7 +528,7 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, struct mode_tree_item *mti, *saved; log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag, - name, text); + name, (text == NULL ? "" : text)); mti = xcalloc(1, sizeof *mti); mti->parent = parent; From 6214cd07264b393fd751271791870f1e8f251371 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 13 May 2020 06:29:57 +0100 Subject: [PATCH 0346/1006] Add a mark in copy mode. Set with set-mark command (bound to 'X') by default and the mark and cursor position are swapped with 'jump-to-mark' (bound to M-x). The line containing the mark is shown in copy-mode-mark-style with the horizontal position in reverse. From Anindya Mukherjee in GitHub issue 2209. --- key-bindings.c | 4 ++ options-table.c | 9 ++++ tmux.1 | 10 +++++ window-copy.c | 107 ++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 121 insertions(+), 9 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index fc083db4..fe4c8fe3 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -426,6 +426,7 @@ key_bindings_init(void) "bind -Tcopy-mode N send -X search-reverse", "bind -Tcopy-mode R send -X rectangle-toggle", "bind -Tcopy-mode T command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"'", + "bind -Tcopy-mode X send -X set-mark", "bind -Tcopy-mode f command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"'", "bind -Tcopy-mode g command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode n send -X search-again", @@ -467,6 +468,7 @@ key_bindings_init(void) "bind -Tcopy-mode M-r send -X middle-line", "bind -Tcopy-mode M-v send -X page-up", "bind -Tcopy-mode M-w send -X copy-pipe-and-cancel", + "bind -Tcopy-mode M-x send -X jump-to-mark", "bind -Tcopy-mode 'M-{' send -X previous-paragraph", "bind -Tcopy-mode 'M-}' send -X next-paragraph", "bind -Tcopy-mode M-Up send -X halfpage-up", @@ -521,6 +523,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi T command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"'", "bind -Tcopy-mode-vi V send -X select-line", "bind -Tcopy-mode-vi W send -X next-space", + "bind -Tcopy-mode-vi X send -X set-mark", "bind -Tcopy-mode-vi ^ send -X back-to-indentation", "bind -Tcopy-mode-vi b send -X previous-word", "bind -Tcopy-mode-vi e send -X next-word-end", @@ -554,6 +557,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi Down send -X cursor-down", "bind -Tcopy-mode-vi Left send -X cursor-left", "bind -Tcopy-mode-vi Right send -X cursor-right", + "bind -Tcopy-mode-vi M-x send -X jump-to-mark", "bind -Tcopy-mode-vi C-Up send -X scroll-up", "bind -Tcopy-mode-vi C-Down send -X scroll-down", }; diff --git a/options-table.c b/options-table.c index c3a9f23b..39bb7d25 100644 --- a/options-table.c +++ b/options-table.c @@ -794,6 +794,15 @@ const struct options_table_entry options_table[] = { .text = "Style of the current search match in copy mode." }, + { .name = "copy-mode-mark-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "bg=red,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = ",", + .text = "Style of the marked line in copy mode." + }, + { .name = "main-pane-height", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, diff --git a/tmux.1 b/tmux.1 index b6ac248f..66922285 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1555,6 +1555,7 @@ The following commands are supported in copy mode: .It Li "jump-reverse" Ta "," Ta "," .It Li "jump-to-backward " Ta "T" Ta "" .It Li "jump-to-forward " Ta "t" Ta "" +.It Li "jump-to-mark" Ta "M-x" Ta "M-x" .It Li "middle-line" Ta "M" Ta "M-r" .It Li "next-matching-bracket" Ta "%" Ta "M-C-f" .It Li "next-paragraph" Ta "}" Ta "M-}" @@ -1585,6 +1586,7 @@ The following commands are supported in copy mode: .It Li "search-reverse" Ta "N" Ta "N" .It Li "select-line" Ta "V" Ta "" .It Li "select-word" Ta "" Ta "" +.It Li "set-mark" Ta "X" Ta "X" .It Li "start-of-line" Ta "0" Ta "C-a" .It Li "stop-selection" Ta "" Ta "" .It Li "top-line" Ta "H" Ta "M-R" @@ -3817,6 +3819,14 @@ see the .Sx STYLES section. .Pp +.It Ic copy-mode-mark-style Ar style +Set the style of the line containing the mark in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp .It Ic copy-mode-current-match-style Ar style Set the style of the current search match in copy mode. For how to specify diff --git a/window-copy.c b/window-copy.c index 95b4cc6e..e572eaa8 100644 --- a/window-copy.c +++ b/window-copy.c @@ -131,6 +131,7 @@ static void window_copy_rectangle_toggle(struct window_mode_entry *); static void window_copy_move_mouse(struct mouse_event *); static void window_copy_drag_update(struct client *, struct mouse_event *); static void window_copy_drag_release(struct client *, struct mouse_event *); +static void window_copy_jump_to_mark(struct window_mode_entry *); const struct window_mode window_copy_mode = { .name = "copy-mode", @@ -254,6 +255,10 @@ struct window_copy_mode_data { u_int lastcx; /* position in last line w/ content */ u_int lastsx; /* size of last line w/ content */ + u_int mx; /* mark position */ + u_int my; + int showmark; + int searchtype; int searchregex; char *searchstr; @@ -424,6 +429,9 @@ window_copy_init(struct window_mode_entry *wme, data->screen.cx = data->cx; data->screen.cy = data->cy; + data->mx = data->cx; + data->my = screen_hsize(data->backing) + data->cy - data->oy; + data->showmark = 0; screen_write_start(&ctx, &data->screen); for (i = 0; i < screen_size_y(&data->screen); i++) @@ -448,6 +456,9 @@ window_copy_view_init(struct window_mode_entry *wme, data->backing = s = xmalloc(sizeof *data->backing); screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX); + data->mx = data->cx; + data->my = screen_hsize(data->backing) + data->cy - data->oy; + data->showmark = 0; return (&data->screen); } @@ -1733,6 +1744,17 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_REDRAW); } +static enum window_copy_cmd_action +window_copy_cmd_set_mark(struct window_copy_cmd_state *cs) +{ + struct window_copy_mode_data *data = cs->wme->data; + + data->mx = data->cx; + data->my = screen_hsize(data->backing) + data->cy - data->oy; + data->showmark = 1; + return (WINDOW_COPY_CMD_REDRAW); +} + static enum window_copy_cmd_action window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs) { @@ -1877,6 +1899,15 @@ window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_jump_to_mark(wme); + return (WINDOW_COPY_CMD_NOTHING); +} + static enum window_copy_cmd_action window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) { @@ -2154,6 +2185,8 @@ static const struct { window_copy_cmd_jump_to_backward }, { "jump-to-forward", 1, 1, 1, window_copy_cmd_jump_to_forward }, + { "jump-to-mark", 0, 0, 0, + window_copy_cmd_jump_to_mark }, { "middle-line", 0, 0, 1, window_copy_cmd_middle_line }, { "next-matching-bracket", 0, 0, 0, @@ -2214,6 +2247,8 @@ static const struct { window_copy_cmd_select_line }, { "select-word", 0, 0, 0, window_copy_cmd_select_word }, + { "set-mark", 0, 0, 0, + window_copy_cmd_set_mark }, { "start-of-line", 0, 0, 1, window_copy_cmd_start_of_line }, { "stop-selection", 0, 0, 0, @@ -3129,11 +3164,26 @@ window_copy_match_at_cursor(struct window_copy_mode_data *data) static void window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, struct grid_cell *gc, const struct grid_cell *mgc, - const struct grid_cell *cgc) + const struct grid_cell *cgc, const struct grid_cell *mkgc) { struct window_copy_mode_data *data = wme->data; u_int mark, start, end, cy, cursor, current; u_int sx = screen_size_x(data->backing); + int inv = 0; + + if (data->showmark && fy == data->my) { + gc->attr = mkgc->attr; + if (fx == data->mx) + inv = 1; + if (inv) { + gc->fg = mkgc->bg; + gc->bg = mkgc->fg; + } + else { + gc->fg = mkgc->fg; + gc->bg = mkgc->bg; + } + } if (data->searchmark == NULL) return; @@ -3150,21 +3200,34 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, window_copy_match_start_end(data, cursor, &start, &end); if (current >= start && current <= end) { gc->attr = cgc->attr; - gc->fg = cgc->fg; - gc->bg = cgc->bg; + if (inv) { + gc->fg = cgc->bg; + gc->bg = cgc->fg; + } + else { + gc->fg = cgc->fg; + gc->bg = cgc->bg; + } return; } } gc->attr = mgc->attr; - gc->fg = mgc->fg; - gc->bg = mgc->bg; + if (inv) { + gc->fg = mgc->bg; + gc->bg = mgc->fg; + } + else { + gc->fg = mgc->fg; + gc->bg = mgc->bg; + } } static void window_copy_write_one(struct window_mode_entry *wme, struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx, - const struct grid_cell *mgc, const struct grid_cell *cgc) + const struct grid_cell *mgc, const struct grid_cell *cgc, + const struct grid_cell *mkgc) { struct window_copy_mode_data *data = wme->data; struct grid *gd = data->backing->grid; @@ -3175,7 +3238,8 @@ window_copy_write_one(struct window_mode_entry *wme, for (fx = 0; fx < nx; fx++) { grid_get_cell(gd, fx, fy, &gc); if (fx + gc.data.width <= nx) { - window_copy_update_style(wme, fx, fy, &gc, mgc, cgc); + window_copy_update_style(wme, fx, fy, &gc, mgc, cgc, + mkgc); screen_write_cell(ctx, &gc); } } @@ -3189,7 +3253,7 @@ window_copy_write_line(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct options *oo = wp->window->options; - struct grid_cell gc, mgc, cgc; + struct grid_cell gc, mgc, cgc, mkgc; char hdr[512]; size_t size = 0; u_int hsize = screen_hsize(data->backing); @@ -3200,6 +3264,8 @@ window_copy_write_line(struct window_mode_entry *wme, mgc.flags |= GRID_FLAG_NOPALETTE; style_apply(&cgc, oo, "copy-mode-current-match-style", NULL); cgc.flags |= GRID_FLAG_NOPALETTE; + style_apply(&mkgc, oo, "copy-mode-mark-style", NULL); + mkgc.flags |= GRID_FLAG_NOPALETTE; if (py == 0 && s->rupper < s->rlower && !data->hide_position) { if (data->searchmark == NULL) { @@ -3233,7 +3299,7 @@ window_copy_write_line(struct window_mode_entry *wme, if (size < screen_size_x(s)) { window_copy_write_one(wme, ctx, py, hsize - data->oy + py, - screen_size_x(s) - size, &mgc, &cgc); + screen_size_x(s) - size, &mgc, &cgc, &mkgc); } if (py == data->cy && data->cx == screen_size_x(s)) { @@ -4687,3 +4753,26 @@ window_copy_drag_release(struct client *c, struct mouse_event *m) data = wme->data; evtimer_del(&data->dragtimer); } + +static void +window_copy_jump_to_mark(struct window_mode_entry *wme) +{ + struct window_copy_mode_data *data = wme->data; + u_int tmx, tmy; + + tmx = data->cx; + tmy = screen_hsize(data->backing) + data->cy - data->oy; + data->cx = data->mx; + if (data->my < screen_hsize(data->backing)) { + data->cy = 0; + data->oy = screen_hsize(data->backing) - data->my; + } else { + data->cy = data->my - screen_hsize(data->backing); + data->oy = 0; + } + data->mx = tmx; + data->my = tmy; + data->showmark = 1; + window_copy_update_selection(wme, 0, 0); + window_copy_redraw_screen(wme); +} From 4cb1d3d7a92c08c64b58a600125d45256c760ace Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 13 May 2020 06:58:07 +0100 Subject: [PATCH 0347/1006] Move editor stuff to common code in popup.c. --- popup.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ tmux.h | 3 ++ window-buffer.c | 69 +++++------------------------------ 3 files changed, 110 insertions(+), 59 deletions(-) diff --git a/popup.c b/popup.c index 4b47df2b..2a50d4ad 100644 --- a/popup.c +++ b/popup.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "tmux.h" @@ -57,6 +58,12 @@ struct popup_data { u_int lb; }; +struct popup_editor { + char *path; + popup_finish_edit_cb cb; + void *arg; +}; + static void popup_redraw_cb(const struct tty_ctx *ttyctx) { @@ -519,3 +526,93 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, popup_draw_cb, popup_key_cb, popup_free_cb, pd); return (0); } + +static void +popup_editor_free(struct popup_editor *pe) +{ + unlink(pe->path); + free(pe->path); + free(pe); +} + +static void +popup_editor_close_cb(int status, void *arg) +{ + struct popup_editor *pe = arg; + FILE *f; + char *buf = NULL; + off_t len = 0; + + if (status != 0) { + pe->cb(NULL, 0, pe->arg); + popup_editor_free(pe); + return; + } + + f = fopen(pe->path, "r"); + if (f != NULL) { + fseeko(f, 0, SEEK_END); + len = ftello(f); + fseeko(f, 0, SEEK_SET); + + if (len == 0 || + (uintmax_t)len > (uintmax_t)SIZE_MAX || + (buf = malloc(len)) == NULL || + fread(buf, len, 1, f) != 1) { + free(buf); + buf = NULL; + len = 0; + } + fclose(f); + } + pe->cb(buf, len, pe->arg); /* callback now owns buffer */ + popup_editor_free(pe); +} + +int +popup_editor(struct client *c, const char *buf, size_t len, + popup_finish_edit_cb cb, void *arg) +{ + struct popup_editor *pe; + int fd; + FILE *f; + char *cmd; + char path[] = _PATH_TMP "tmux.XXXXXXXX"; + const char *editor; + u_int px, py, sx, sy; + + editor = options_get_string(global_options, "editor"); + if (*editor == '\0') + return (-1); + + fd = mkstemp(path); + if (fd == -1) + return (-1); + f = fdopen(fd, "w"); + if (fwrite(buf, len, 1, f) != 1) { + fclose(f); + return (-1); + } + fclose(f); + + pe = xcalloc(1, sizeof *pe); + pe->path = xstrdup(path); + pe->cb = cb; + pe->arg = arg; + + sx = c->tty.sx * 9 / 10; + sy = c->tty.sy * 9 / 10; + px = (c->tty.sx / 2) - (sx / 2); + py = (c->tty.sy / 2) - (sy / 2); + + xasprintf(&cmd, "%s %s", editor, path); + if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, + 0, NULL, cmd, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb, + pe) != 0) { + popup_editor_free(pe); + free(cmd); + return (-1); + } + free(cmd); + return (0); +} diff --git a/tmux.h b/tmux.h index f2675345..932d85ec 100644 --- a/tmux.h +++ b/tmux.h @@ -2872,6 +2872,7 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int, #define POPUP_CLOSEEXIT 0x2 #define POPUP_CLOSEEXITZERO 0x4 typedef void (*popup_close_cb)(int, void *); +typedef void (*popup_finish_edit_cb)(char *, size_t, void *); u_int popup_width(struct cmdq_item *, u_int, const char **, struct client *, struct cmd_find_state *); u_int popup_height(u_int, const char **); @@ -2879,6 +2880,8 @@ int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, u_int, u_int, const char **, const char *, const char *, const char *, struct client *, struct cmd_find_state *, popup_close_cb, void *); +int popup_editor(struct client *, const char *, size_t, + popup_finish_edit_cb, void *); /* style.c */ int style_parse(struct style *,const struct grid_cell *, diff --git a/window-buffer.c b/window-buffer.c index b3b8e27a..62412cd0 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -98,7 +98,6 @@ struct window_buffer_modedata { struct window_buffer_editdata { u_int wp_id; - char *path; char *name; struct paste_buffer *pb; }; @@ -364,19 +363,14 @@ window_buffer_do_paste(void *modedata, void *itemdata, struct client *c, static void window_buffer_finish_edit(struct window_buffer_editdata *ed) { - unlink(ed->path); - free(ed->path); free(ed->name); free(ed); } static void -window_buffer_edit_close_cb(int status, void *arg) +window_buffer_edit_close_cb(char *buf, size_t len, void *arg) { struct window_buffer_editdata *ed = arg; - FILE *f; - off_t len; - char *buf; size_t oldlen; const char *oldbuf; struct paste_buffer *pb; @@ -384,7 +378,7 @@ window_buffer_edit_close_cb(int status, void *arg) struct window_buffer_modedata *data; struct window_mode_entry *wme; - if (status != 0) { + if (buf == NULL || len == 0) { window_buffer_finish_edit(ed); return; } @@ -395,26 +389,13 @@ window_buffer_edit_close_cb(int status, void *arg) return; } - f = fopen(ed->path, "r"); - if (f != NULL) { - fseeko(f, 0, SEEK_END); - len = ftello(f); - fseeko(f, 0, SEEK_SET); - - if (len > 0 && - (uintmax_t)len <= (uintmax_t)SIZE_MAX && - (buf = malloc(len)) != NULL && - fread(buf, len, 1, f) == 1) { - oldbuf = paste_buffer_data(pb, &oldlen); - if (oldlen != '\0' && - oldbuf[oldlen - 1] != '\n' && - buf[len - 1] == '\n') - len--; - if (len != 0) - paste_replace(pb, buf, len); - } - fclose(f); - } + oldbuf = paste_buffer_data(pb, &oldlen); + if (oldlen != '\0' && + oldbuf[oldlen - 1] != '\n' && + buf[len - 1] == '\n') + len--; + if (len != 0) + paste_replace(pb, buf, len); wp = window_pane_find_by_id(ed->wp_id); if (wp != NULL) { @@ -434,51 +415,21 @@ window_buffer_start_edit(struct window_buffer_modedata *data, struct window_buffer_itemdata *item, struct client *c) { struct paste_buffer *pb; - int fd; - FILE *f; const char *buf; size_t len; struct window_buffer_editdata *ed; - char *cmd; - char path[] = _PATH_TMP "tmux.XXXXXXXX"; - const char *editor; - u_int px, py, sx, sy; if ((pb = paste_get_name(item->name)) == NULL) return; buf = paste_buffer_data(pb, &len); - editor = options_get_string(global_options, "editor"); - if (*editor == '\0') - return; - - fd = mkstemp(path); - if (fd == -1) - return; - f = fdopen(fd, "w"); - if (fwrite(buf, len, 1, f) != 1) { - fclose(f); - return; - } - fclose(f); - ed = xcalloc(1, sizeof *ed); ed->wp_id = data->wp->id; - ed->path = xstrdup(path); ed->name = xstrdup(paste_buffer_name(pb)); ed->pb = pb; - sx = c->tty.sx * 9 / 10; - sy = c->tty.sy * 9 / 10; - px = (c->tty.sx / 2) - (sx / 2); - py = (c->tty.sy / 2) - (sy / 2); - - xasprintf(&cmd, "%s %s", editor, path); - if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, - 0, NULL, cmd, NULL, _PATH_TMP, c, NULL, window_buffer_edit_close_cb, - ed) != 0) + if (popup_editor(c, buf, len, window_buffer_edit_close_cb, ed) != 0) window_buffer_finish_edit(ed); - free(cmd); } static void From 3f55d05386a1620d19420c1cdccf33c45ca6b641 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 13 May 2020 17:49:37 +0100 Subject: [PATCH 0348/1006] Tidy up border redrawing, fix some errors in how the window border connects with panes. --- screen-redraw.c | 219 +++++++++++++++++++++++++++--------------------- 1 file changed, 124 insertions(+), 95 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index dd5886b2..fc2817ce 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -49,9 +49,15 @@ const struct grid_cell screen_redraw_border_cell = { { { ' ' }, 0, 1, 1 }, GRID_ATTR_CHARSET, 0, 8, 8, 0 }; +enum screen_redraw_border_type { + SCREEN_REDRAW_OUTSIDE, + SCREEN_REDRAW_INSIDE, + SCREEN_REDRAW_BORDER +}; + /* Return if window has only two panes. */ static int -screen_redraw_two_panes(struct window *w) +screen_redraw_two_panes(struct window *w, int direction) { struct window_pane *wp; @@ -60,99 +66,176 @@ screen_redraw_two_panes(struct window *w) return (0); /* one pane */ if (TAILQ_NEXT(wp, entry) != NULL) return (0); /* more than two panes */ + if (direction == 0 && wp->xoff == 0) + return (0); + if (direction == 1 && wp->yoff == 0) + return (0); return (1); } -/* Check if cell is on the border of a particular pane. */ -static int -screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py, +/* Check if cell is on the border of a pane. */ +static enum screen_redraw_border_type +screen_redraw_pane_border(struct window_pane *wp, u_int px, u_int py, int pane_status) { u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy; /* Inside pane. */ if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey) - return (0); + return (SCREEN_REDRAW_INSIDE); /* Left/right borders. */ if (pane_status == PANE_STATUS_OFF) { - if (screen_redraw_two_panes(wp->window)) { + if (screen_redraw_two_panes(wp->window, 0)) { if (wp->xoff == 0 && px == wp->sx && py <= wp->sy / 2) - return (2); + return (SCREEN_REDRAW_BORDER); if (wp->xoff != 0 && px == wp->xoff - 1 && py > wp->sy / 2) - return (1); + return (SCREEN_REDRAW_BORDER); } else { if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { if (wp->xoff != 0 && px == wp->xoff - 1) - return (1); + return (SCREEN_REDRAW_BORDER); if (px == ex) - return (2); + return (SCREEN_REDRAW_BORDER); } } } else { if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { if (wp->xoff != 0 && px == wp->xoff - 1) - return (1); + return (SCREEN_REDRAW_BORDER); if (px == ex) - return (2); + return (SCREEN_REDRAW_BORDER); } } /* Top/bottom borders. */ if (pane_status == PANE_STATUS_OFF) { - if (screen_redraw_two_panes(wp->window)) { + if (screen_redraw_two_panes(wp->window, 1)) { if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2) - return (4); + return (SCREEN_REDRAW_BORDER); if (wp->yoff != 0 && py == wp->yoff - 1 && px > wp->sx / 2) - return (3); + return (SCREEN_REDRAW_BORDER); } else { if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { if (wp->yoff != 0 && py == wp->yoff - 1) - return (3); + return (SCREEN_REDRAW_BORDER); if (py == ey) - return (4); + return (SCREEN_REDRAW_BORDER); } } } else if (pane_status == PANE_STATUS_TOP) { if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { if (wp->yoff != 0 && py == wp->yoff - 1) - return (3); + return (SCREEN_REDRAW_BORDER); } } else { if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { if (py == ey) - return (4); + return (SCREEN_REDRAW_BORDER); } } /* Outside pane. */ - return (-1); + return (SCREEN_REDRAW_OUTSIDE); } -/* Check if a cell is on the pane border. */ +/* Check if a cell is on a border. */ static int screen_redraw_cell_border(struct client *c, u_int px, u_int py, int pane_status) { struct window *w = c->session->curw->window; struct window_pane *wp; - int retval; + + /* On the window border? */ + if (px == w->sx || py == w->sy) + return (1); + + /* Outside the window? */ + if (px > w->sx || py > w->sy) + return (0); /* Check all the panes. */ TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; - retval = screen_redraw_cell_border1(wp, px, py, pane_status); - if (retval != -1) - return (!!retval); + switch (screen_redraw_pane_border(wp, px, py, pane_status)) { + case SCREEN_REDRAW_INSIDE: + return (0); + case SCREEN_REDRAW_BORDER: + return (1); + case SCREEN_REDRAW_OUTSIDE: + break; + } } return (0); } +/* Work out type of border cell from surrounding cells. */ +static int +screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, + int pane_status) +{ + struct window *w = c->session->curw->window; + u_int sx = w->sx, sy = w->sy; + int borders = 0; + + /* + * Construct a bitmask of whether the cells to the left (bit 4), right, + * top, and bottom (bit 1) of this cell are borders. + */ + if (px == 0 || screen_redraw_cell_border(c, px - 1, py, pane_status)) + borders |= 8; + if (px <= sx && screen_redraw_cell_border(c, px + 1, py, pane_status)) + borders |= 4; + if (pane_status == PANE_STATUS_TOP) { + if (py != 0 && + screen_redraw_cell_border(c, px, py - 1, pane_status)) + borders |= 2; + } else { + if (py == 0 || + screen_redraw_cell_border(c, px, py - 1, pane_status)) + borders |= 2; + } + if (py <= sy && screen_redraw_cell_border(c, px, py + 1, pane_status)) + borders |= 1; + + /* + * Figure out what kind of border this cell is. Only one bit set + * doesn't make sense (can't have a border cell with no others + * connected). + */ + switch (borders) { + case 15: /* 1111, left right top bottom */ + return (CELL_JOIN); + case 14: /* 1110, left right top */ + return (CELL_BOTTOMJOIN); + case 13: /* 1101, left right bottom */ + return (CELL_TOPJOIN); + case 12: /* 1100, left right */ + return (CELL_TOPBOTTOM); + case 11: /* 1011, left top bottom */ + return (CELL_RIGHTJOIN); + case 10: /* 1010, left top */ + return (CELL_BOTTOMRIGHT); + case 9: /* 1001, left bottom */ + return (CELL_TOPRIGHT); + case 7: /* 0111, right top bottom */ + return (CELL_LEFTJOIN); + case 6: /* 0110, right top */ + return (CELL_BOTTOMLEFT); + case 5: /* 0101, right bottom */ + return (CELL_TOPLEFT); + case 3: /* 0011, top bottom */ + return (CELL_LEFTRIGHT); + } + return (CELL_OUTSIDE); +} + /* Check if cell inside a pane. */ static int screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, @@ -160,13 +243,15 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, { struct window *w = c->session->curw->window; struct window_pane *wp; - int borders, border; + int border; u_int right, line; *wpp = NULL; if (px > w->sx || py > w->sy) return (CELL_OUTSIDE); + if (px == w->sx || py == w->sy) /* window border */ + return (screen_redraw_type_of_cell(c, px, py, pane_status)); if (pane_status != PANE_STATUS_OFF) { wp = w->active; @@ -196,72 +281,16 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, goto next2; *wpp = wp; - /* If outside the pane and its border, skip it. */ - if ((wp->xoff != 0 && px < wp->xoff - 1) || - px > wp->xoff + wp->sx || - (wp->yoff != 0 && py < wp->yoff - 1) || - py > wp->yoff + wp->sy) - goto next2; - - /* If definitely inside, return. If not on border, skip. */ - border = screen_redraw_cell_border1(wp, px, py, pane_status); - if (border == 0) + /* + * If definitely inside, return. If not on border, skip. + * Otherwise work out the cell. + */ + border = screen_redraw_pane_border(wp, px, py, pane_status); + if (border == SCREEN_REDRAW_INSIDE) return (CELL_INSIDE); - if (border == -1) + if (border == SCREEN_REDRAW_OUTSIDE) goto next2; - - /* - * Construct a bitmask of whether the cells to the left (bit - * 4), right, top, and bottom (bit 1) of this cell are borders. - */ - borders = 0; - if (px == 0 || - screen_redraw_cell_border(c, px - 1, py, pane_status)) - borders |= 8; - if (px <= w->sx && - screen_redraw_cell_border(c, px + 1, py, pane_status)) - borders |= 4; - if (pane_status == PANE_STATUS_TOP && - py != 0 && - screen_redraw_cell_border(c, px, py - 1, pane_status)) - borders |= 2; - else if (pane_status != PANE_STATUS_TOP && - (py == 0 || - screen_redraw_cell_border(c, px, py - 1, pane_status))) - borders |= 2; - if (py <= w->sy && - screen_redraw_cell_border(c, px, py + 1, pane_status)) - borders |= 1; - - /* - * Figure out what kind of border this cell is. Only one bit - * set doesn't make sense (can't have a border cell with no - * others connected). - */ - switch (borders) { - case 15: /* 1111, left right top bottom */ - return (CELL_JOIN); - case 14: /* 1110, left right top */ - return (CELL_BOTTOMJOIN); - case 13: /* 1101, left right bottom */ - return (CELL_TOPJOIN); - case 12: /* 1100, left right */ - return (CELL_TOPBOTTOM); - case 11: /* 1011, left top bottom */ - return (CELL_RIGHTJOIN); - case 10: /* 1010, left top */ - return (CELL_BOTTOMRIGHT); - case 9: /* 1001, left bottom */ - return (CELL_TOPRIGHT); - case 7: /* 0111, right top bottom */ - return (CELL_LEFTJOIN); - case 6: /* 0110, right top */ - return (CELL_BOTTOMLEFT); - case 5: /* 0101, right bottom */ - return (CELL_TOPLEFT); - case 3: /* 0011, top bottom */ - return (CELL_LEFTRIGHT); - } + return (screen_redraw_type_of_cell(c, px, py, pane_status)); next2: wp = TAILQ_NEXT(wp, entry); @@ -277,12 +306,12 @@ static int screen_redraw_check_is(u_int px, u_int py, int pane_status, struct window_pane *wp) { - int border; + enum screen_redraw_border_type border; - border = screen_redraw_cell_border1(wp, px, py, pane_status); - if (border == 0 || border == -1) - return (0); - return (1); + border = screen_redraw_pane_border(wp, px, py, pane_status); + if (border == SCREEN_REDRAW_BORDER) + return (1); + return (0); } /* Update pane status. */ From e6d9f3f90cbc469ae9b93749a20c7d12536a6bad Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 13 May 2020 20:58:42 +0100 Subject: [PATCH 0349/1006] Add -Z to customize-mode binding. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index fe4c8fe3..05089bab 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -320,7 +320,7 @@ key_bindings_init(void) "bind -N 'Toggle the marked pane' m select-pane -m", "bind -N 'Select the next window' n next-window", "bind -N 'Select the next pane' o select-pane -t:.+", - "bind -N 'Customize options' C customize-mode", + "bind -N 'Customize options' C customize-mode -Z", "bind -N 'Select the previous pane' p previous-window", "bind -N 'Display pane numbers' q display-panes", "bind -N 'Redraw the current client' r refresh-client", From 09a2246b00fdb5b18db13b65d6496511dabba238 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 13 May 2020 21:11:46 +0100 Subject: [PATCH 0350/1006] Use safe loop for freeing client files. --- server-client.c | 7 +++---- tmux.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/server-client.c b/server-client.c index 6e24fdf3..2e9f15a6 100644 --- a/server-client.c +++ b/server-client.c @@ -211,7 +211,6 @@ server_client_create(int fd) c->queue = cmdq_new(); c->tty.fd = -1; - c->tty.sx = 80; c->tty.sy = 24; @@ -270,7 +269,7 @@ server_client_open(struct client *c, char **cause) void server_client_lost(struct client *c) { - struct client_file *cf; + struct client_file *cf, *cf1; c->flags |= CLIENT_DEAD; @@ -278,7 +277,7 @@ server_client_lost(struct client *c) status_prompt_clear(c); status_message_clear(c); - RB_FOREACH(cf, client_files, &c->files) { + RB_FOREACH_SAFE(cf, client_files, &c->files, cf1) { cf->error = EINTR; file_fire_done(cf); } @@ -2252,7 +2251,7 @@ server_client_set_flags(struct client *c, const char *flags) } -/*Get client flags. This is only flags useful to show to users. */ +/* Get client flags. This is only flags useful to show to users. */ const char * server_client_get_flags(struct client *c) { diff --git a/tmux.h b/tmux.h index 932d85ec..4c9d229f 100644 --- a/tmux.h +++ b/tmux.h @@ -1506,7 +1506,7 @@ struct client_file { client_file_cb cb; void *data; - RB_ENTRY (client_file) entry; + RB_ENTRY(client_file) entry; }; RB_HEAD(client_files, client_file); From 12eceaf2b3ef2b028a55d8ad11951be700be0446 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 14 May 2020 10:35:26 +0100 Subject: [PATCH 0351/1006] Expand target from client and use it to expand the prompt. --- cmd-command-prompt.c | 6 ++++-- cmd-confirm-before.c | 6 ++++-- cmd-queue.c | 3 +-- mode-tree.c | 4 ++-- status.c | 11 +++++++---- tmux.h | 5 +++-- window-customize.c | 11 ++++++----- window-tree.c | 9 +++++---- 8 files changed, 32 insertions(+), 23 deletions(-) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index b8e3bd5c..c82235c5 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -66,6 +66,7 @@ cmd_command_prompt_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_find_state *target = cmdq_get_target(item); const char *inputs, *prompts; struct cmd_command_prompt_cdata *cdata; char *prompt, *ptr, *input = NULL; @@ -125,8 +126,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_WINDOW; else if (args_has(args, 'T')) cdata->flags |= PROMPT_TARGET; - status_prompt_set(tc, prompt, input, cmd_command_prompt_callback, - cmd_command_prompt_free, cdata, cdata->flags); + status_prompt_set(tc, target, prompt, input, + cmd_command_prompt_callback, cmd_command_prompt_free, cdata, + cdata->flags); free(prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 0d881178..0c562836 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -56,6 +56,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_confirm_before_data *cdata; struct client *tc = cmdq_get_target_client(item); + struct cmd_find_state *target = cmdq_get_target(item); char *cmd, *copy, *new_prompt, *ptr; const char *prompt; @@ -71,8 +72,9 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) cdata = xmalloc(sizeof *cdata); cdata->cmd = xstrdup(args->argv[0]); - status_prompt_set(tc, new_prompt, NULL, cmd_confirm_before_callback, - cmd_confirm_before_free, cdata, PROMPT_SINGLE); + status_prompt_set(tc, target, new_prompt, NULL, + cmd_confirm_before_callback, cmd_confirm_before_free, cdata, + PROMPT_SINGLE); free(new_prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-queue.c b/cmd-queue.c index a40053a6..6bc6d0d2 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -522,7 +522,7 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs, const char *value; if (flag->flag == 0) { - cmd_find_clear_state(fs, 0); + cmd_find_from_client(fs, item->target_client, 0); return (CMD_RETURN_NORMAL); } @@ -610,7 +610,6 @@ cmdq_fire_command(struct cmdq_item *item) if (retval == CMD_RETURN_ERROR) goto out; - retval = entry->exec(cmd, item); if (retval == CMD_RETURN_ERROR) goto out; diff --git a/mode-tree.c b/mode-tree.c index 131830d6..a523478e 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1125,7 +1125,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, case '/': case '\023': /* C-s */ mtd->references++; - status_prompt_set(c, "(search) ", "", + status_prompt_set(c, NULL, "(search) ", "", mode_tree_search_callback, mode_tree_search_free, mtd, PROMPT_NOFORMAT); break; @@ -1134,7 +1134,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, break; case 'f': mtd->references++; - status_prompt_set(c, "(filter) ", mtd->filter, + status_prompt_set(c, NULL, "(filter) ", mtd->filter, mode_tree_filter_callback, mode_tree_filter_free, mtd, PROMPT_NOFORMAT); break; diff --git a/status.c b/status.c index b5fa0824..56af02f5 100644 --- a/status.c +++ b/status.c @@ -532,14 +532,17 @@ status_message_redraw(struct client *c) /* Enable status line prompt. */ void -status_prompt_set(struct client *c, const char *msg, const char *input, - prompt_input_cb inputcb, prompt_free_cb freecb, void *data, int flags) +status_prompt_set(struct client *c, struct cmd_find_state *fs, + const char *msg, const char *input, prompt_input_cb inputcb, + prompt_free_cb freecb, void *data, int flags) { struct format_tree *ft; char *tmp, *cp; - ft = format_create(c, NULL, FORMAT_NONE, 0); - format_defaults(ft, c, NULL, NULL, NULL); + if (fs != NULL) + ft = format_create_from_state(NULL, c, fs); + else + ft = format_create_defaults(NULL, c, NULL, NULL, NULL); if (input == NULL) input = ""; diff --git a/tmux.h b/tmux.h index 4c9d229f..2b88305a 100644 --- a/tmux.h +++ b/tmux.h @@ -2361,8 +2361,9 @@ void printflike(3, 4) status_message_set(struct client *, int, const char *, ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); -void status_prompt_set(struct client *, const char *, const char *, - prompt_input_cb, prompt_free_cb, void *, int); +void status_prompt_set(struct client *, struct cmd_find_state *, + const char *, const char *, prompt_input_cb, prompt_free_cb, + void *, int); void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); int status_prompt_key(struct client *, key_code); diff --git a/window-customize.c b/window-customize.c index c74884d6..12c09cf6 100644 --- a/window-customize.c +++ b/window-customize.c @@ -1111,7 +1111,7 @@ window_customize_set_option(struct client *c, new_item->idx = idx; data->references++; - status_prompt_set(c, prompt, value, + status_prompt_set(c, NULL, prompt, value, window_customize_set_option_callback, window_customize_free_item_callback, new_item, PROMPT_NOFORMAT); @@ -1243,7 +1243,7 @@ window_customize_set_key(struct client *c, new_item->key = key; data->references++; - status_prompt_set(c, prompt, value, + status_prompt_set(c, NULL, prompt, value, window_customize_set_command_callback, window_customize_free_item_callback, new_item, PROMPT_NOFORMAT); @@ -1259,7 +1259,8 @@ window_customize_set_key(struct client *c, new_item->key = key; data->references++; - status_prompt_set(c, prompt, (bd->note == NULL ? "" : bd->note), + status_prompt_set(c, NULL, prompt, + (bd->note == NULL ? "" : bd->note), window_customize_set_note_callback, window_customize_free_item_callback, new_item, PROMPT_NOFORMAT); @@ -1398,7 +1399,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, } else xasprintf(&prompt, "Unset option %s? ", item->name); data->references++; - status_prompt_set(c, prompt, "", + status_prompt_set(c, NULL, prompt, "", window_customize_unset_current_callback, window_customize_free_callback, data, PROMPT_SINGLE|PROMPT_NOFORMAT); @@ -1410,7 +1411,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, break; xasprintf(&prompt, "Unset or unbind %u tagged? ", tagged); data->references++; - status_prompt_set(c, prompt, "", + status_prompt_set(c, NULL, prompt, "", window_customize_unset_tagged_callback, window_customize_free_callback, data, PROMPT_SINGLE|PROMPT_NOFORMAT); diff --git a/window-tree.c b/window-tree.c index b3ce8bb7..393b88a4 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1234,7 +1234,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, if (prompt == NULL) break; data->references++; - status_prompt_set(c, prompt, "", + status_prompt_set(c, NULL, prompt, "", window_tree_kill_current_callback, window_tree_command_free, data, PROMPT_SINGLE|PROMPT_NOFORMAT); free(prompt); @@ -1245,7 +1245,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, break; xasprintf(&prompt, "Kill %u tagged? ", tagged); data->references++; - status_prompt_set(c, prompt, "", + status_prompt_set(c, NULL, prompt, "", window_tree_kill_tagged_callback, window_tree_command_free, data, PROMPT_SINGLE|PROMPT_NOFORMAT); free(prompt); @@ -1257,8 +1257,9 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, else xasprintf(&prompt, "(current) "); data->references++; - status_prompt_set(c, prompt, "", window_tree_command_callback, - window_tree_command_free, data, PROMPT_NOFORMAT); + status_prompt_set(c, NULL, prompt, "", + window_tree_command_callback, window_tree_command_free, + data, PROMPT_NOFORMAT); free(prompt); break; case '\r': From 0bdbf47ef946b3535cc32b45ea8d971de5baddb5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 14 May 2020 11:18:19 +0100 Subject: [PATCH 0352/1006] Add a client flag 'active-pane' which stores the active pane in the client and allows it to be changed independently from the real active pane stored in the window. This is can be used with session groups which allow an independent current window (although it would be nice to have a flag for this too and remove session groups). The client active pane is only really useful interactively, many things (hooks, window-style, zooming) still use the window active pane. --- cmd-break-pane.c | 1 + cmd-find.c | 23 +++++++++--- cmd-join-pane.c | 1 + cmd-kill-pane.c | 1 + cmd-queue.c | 2 +- cmd-select-pane.c | 18 ++++++--- cmd-split-window.c | 1 + cmd-swap-pane.c | 3 ++ screen-redraw.c | 15 ++++---- server-client.c | 91 +++++++++++++++++++++++++++++++++++++++++++++- server-fn.c | 2 + spawn.c | 1 + tmux.1 | 9 +++++ tmux.h | 17 ++++++++- tty.c | 5 +-- 15 files changed, 164 insertions(+), 26 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 87892d73..9483aa7e 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -89,6 +89,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) } TAILQ_REMOVE(&w->panes, wp, entry); + server_client_remove_pane(wp); window_lost_pane(w, wp); layout_close_pane(wp); diff --git a/cmd-find.c b/cmd-find.c index e70eb606..e98090d2 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -587,22 +587,22 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) return (-1); return (0); } else if (strcmp(pane, "{up-of}") == 0) { - fs->wp = window_pane_find_up(fs->w->active); + fs->wp = window_pane_find_up(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{down-of}") == 0) { - fs->wp = window_pane_find_down(fs->w->active); + fs->wp = window_pane_find_down(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{left-of}") == 0) { - fs->wp = window_pane_find_left(fs->w->active); + fs->wp = window_pane_find_left(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{right-of}") == 0) { - fs->wp = window_pane_find_right(fs->w->active); + fs->wp = window_pane_find_right(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); @@ -614,7 +614,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) n = strtonum(pane + 1, 1, INT_MAX, NULL); else n = 1; - wp = fs->w->active; + wp = fs->current->wp; if (pane[0] == '+') fs->wp = window_pane_next_by_number(fs->w, wp, n); else @@ -866,7 +866,18 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) /* If this is an attached client, all done. */ if (c->session != NULL) { - cmd_find_from_session(fs, c->session, flags); + cmd_find_clear_state(fs, flags); + + fs->wp = server_client_get_pane(c); + if (fs->wp == NULL) { + cmd_find_from_session(fs, c->session, flags); + return (0); + } + fs->s = c->session; + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + + cmd_find_log_state(__func__, fs); return (0); } cmd_find_clear_state(fs, flags); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 2e4bec50..c3d8f784 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -135,6 +135,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) layout_close_pane(src_wp); + server_client_remove_pane(src_wp); window_lost_pane(src_w, src_wp); TAILQ_REMOVE(&src_w->panes, src_wp, entry); diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 2302d7bb..3bf6e26e 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -54,6 +54,7 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { if (loopwp == wp) continue; + server_client_remove_pane(loopwp); layout_close_pane(loopwp); window_remove_pane(wl->window, loopwp); } diff --git a/cmd-queue.c b/cmd-queue.c index 6bc6d0d2..5620fdad 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -809,7 +809,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) } file_print(c, "%s\n", msg); } else { - wp = c->session->curw->window->active; + wp = server_client_get_pane(c); wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) { window_pane_set_mode(wp, NULL, &window_view_mode, NULL, diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 224370ab..3b639e06 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -87,10 +87,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) const struct cmd_entry *entry = cmd_get_entry(self); struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); + struct client *c = cmdq_get_client(item); struct winlink *wl = target->wl; struct window *w = wl->window; struct session *s = target->s; - struct window_pane *wp = target->wp, *lastwp, *markedwp; + struct window_pane *wp = target->wp, *activewp, *lastwp, *markedwp; struct options *oo = wp->options; char *title; const char *style; @@ -201,16 +202,21 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (wp == w->active) + if (c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) + activewp = server_client_get_pane(c); + else + activewp = w->active; + if (wp == activewp) return (CMD_RETURN_NORMAL); if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); - if (window_set_active_pane(w, wp, 1)) { + if (c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) + server_client_set_pane(c, wp); + else if (window_set_active_pane(w, wp, 1)) cmd_find_from_winlink_pane(current, wl, wp, 0); - cmdq_insert_hook(s, item, current, "after-select-pane"); - cmd_select_pane_redraw(w); - } + cmdq_insert_hook(s, item, current, "after-select-pane"); + cmd_select_pane_redraw(w); if (window_pop_zoom(w)) server_redraw_window(w); diff --git a/cmd-split-window.c b/cmd-split-window.c index 130aca2e..e5b3ac49 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -159,6 +159,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } if (input && window_pane_start_input(new_wp, item, &cause) != 0) { + server_client_remove_pane(new_wp); layout_close_pane(new_wp); window_remove_pane(wp->window, new_wp); cmdq_error(item, "%s", cause); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 021ac224..dd981b9a 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -79,6 +79,9 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) if (src_wp == dst_wp) goto out; + server_client_remove_pane(src_wp); + server_client_remove_pane(dst_wp); + tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry); TAILQ_REMOVE(&dst_w->panes, dst_wp, entry); TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry); diff --git a/screen-redraw.c b/screen-redraw.c index fc2817ce..564fd9c9 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -242,7 +242,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, struct window_pane **wpp) { struct window *w = c->session->curw->window; - struct window_pane *wp; + struct window_pane *wp, *active; int border; u_int right, line; @@ -254,7 +254,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, return (screen_redraw_type_of_cell(c, px, py, pane_status)); if (pane_status != PANE_STATUS_OFF) { - wp = w->active; + active = wp = server_client_get_pane(c); do { if (!window_pane_visible(wp)) goto next1; @@ -272,10 +272,10 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, wp = TAILQ_NEXT(wp, entry); if (wp == NULL) wp = TAILQ_FIRST(&w->panes); - } while (wp != w->active); + } while (wp != active); } - wp = w->active; + active = wp = server_client_get_pane(c); do { if (!window_pane_visible(wp)) goto next2; @@ -296,7 +296,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, wp = TAILQ_NEXT(wp, entry); if (wp == NULL) wp = TAILQ_FIRST(&w->panes); - } while (wp != w->active); + } while (wp != active); return (CELL_OUTSIDE); } @@ -330,7 +330,7 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); format_defaults(ft, c, c->session, c->session->curw, wp); - if (wp == w->active) + if (wp == server_client_get_pane(c)) style_apply(&gc, w->options, "pane-active-border-style", ft); else style_apply(&gc, w->options, "pane-border-style", ft); @@ -558,6 +558,7 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, struct client *c = ctx->c; struct session *s = c->session; struct window *w = s->curw->window; + struct window_pane *active = server_client_get_pane(c); struct options *oo = w->options; struct grid_cell *gc; struct format_tree *ft; @@ -569,7 +570,7 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, ft = format_create_defaults(NULL, c, s, s->curw, wp); gc = &wp->border_gc; - if (screen_redraw_check_is(x, y, ctx->pane_status, w->active)) { + if (screen_redraw_check_is(x, y, ctx->pane_status, active)) { style_apply(gc, oo, "pane-active-border-style", ft); gc->attr |= GRID_ATTR_CHARSET; } else { diff --git a/server-client.c b/server-client.c index 2e9f15a6..c7bec010 100644 --- a/server-client.c +++ b/server-client.c @@ -54,6 +54,19 @@ static void server_client_dispatch_read_data(struct client *, static void server_client_dispatch_read_done(struct client *, struct imsg *); +/* Compare client windows. */ +static int +server_client_window_cmp(struct client_window *cw1, + struct client_window *cw2) +{ + if (cw1->window < cw2->window) + return (-1); + if (cw1->window > cw2->window) + return (1); + return (0); +} +RB_GENERATE(client_windows, client_window, entry, server_client_window_cmp); + /* Number of attached clients. */ u_int server_client_how_many(void) @@ -209,6 +222,7 @@ server_client_create(int fd) c->cwd = NULL; c->queue = cmdq_new(); + RB_INIT(&c->windows); c->tty.fd = -1; c->tty.sx = 80; @@ -270,6 +284,7 @@ void server_client_lost(struct client *c) { struct client_file *cf, *cf1; + struct client_window *cw, *cw1; c->flags |= CLIENT_DEAD; @@ -281,6 +296,10 @@ server_client_lost(struct client *c) cf->error = EINTR; file_fire_done(cf); } + RB_FOREACH_SAFE(cw, client_windows, &c->windows, cw1) { + RB_REMOVE(client_windows, &c->windows, cw); + free(cw); + } TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); @@ -1124,7 +1143,7 @@ server_client_key_callback(struct cmdq_item *item, void *data) /* Find affected pane. */ if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0) - cmd_find_from_session(&fs, s, 0); + cmd_find_from_client(&fs, c, 0); wp = fs.wp; /* Forward mouse keys if disabled. */ @@ -1533,7 +1552,7 @@ server_client_reset_state(struct client *c) { struct tty *tty = &c->tty; struct window *w = c->session->curw->window; - struct window_pane *wp = w->active, *loop; + struct window_pane *wp = server_client_get_pane(c), *loop; struct screen *s = NULL; struct options *oo = c->session->options; int mode = 0, cursor, flags; @@ -2238,6 +2257,8 @@ server_client_set_flags(struct client *c, const char *flags) flag = CLIENT_READONLY; else if (strcmp(next, "ignore-size") == 0) flag = CLIENT_IGNORESIZE; + else if (strcmp(next, "active-pane") == 0) + flag = CLIENT_ACTIVEPANE; else continue; @@ -2268,6 +2289,8 @@ server_client_get_flags(struct client *c) strlcat(s, "no-output,", sizeof s); if (c->flags & CLIENT_READONLY) strlcat(s, "read-only,", sizeof s); + if (c->flags & CLIENT_ACTIVEPANE) + strlcat(s, "active-pane,", sizeof s); if (c->flags & CLIENT_SUSPENDED) strlcat(s, "suspended,", sizeof s); if (c->flags & CLIENT_UTF8) @@ -2276,3 +2299,67 @@ server_client_get_flags(struct client *c) s[strlen(s) - 1] = '\0'; return (s); } + +/* Get client window. */ +static struct client_window * +server_client_get_client_window(struct client *c, u_int id) +{ + struct client_window cw = { .window = id }; + + return (RB_FIND(client_windows, &c->windows, &cw)); +} + +/* Get client active pane. */ +struct window_pane * +server_client_get_pane(struct client *c) +{ + struct session *s = c->session; + struct client_window *cw; + + if (s == NULL) + return (NULL); + + if (~c->flags & CLIENT_ACTIVEPANE) + return (s->curw->window->active); + cw = server_client_get_client_window(c, s->curw->window->id); + if (cw == NULL) + return (s->curw->window->active); + return (cw->pane); +} + +/* Set client active pane. */ +void +server_client_set_pane(struct client *c, struct window_pane *wp) +{ + struct session *s = c->session; + struct client_window *cw; + + if (s == NULL) + return; + + cw = server_client_get_client_window(c, s->curw->window->id); + if (cw == NULL) { + cw = xcalloc(1, sizeof *cw); + cw->window = s->curw->window->id; + RB_INSERT(client_windows, &c->windows, cw); + } + cw->pane = wp; + log_debug("%s pane now %%%u", c->name, wp->id); +} + +/* Remove pane from client lists. */ +void +server_client_remove_pane(struct window_pane *wp) +{ + struct client *c; + struct window *w = wp->window; + struct client_window *cw; + + TAILQ_FOREACH(c, &clients, entry) { + cw = server_client_get_client_window(c, w->id); + if (cw != NULL && cw->pane == wp) { + RB_REMOVE(client_windows, &c->windows, cw); + free(cw); + } + } +} diff --git a/server-fn.c b/server-fn.c index 127afb6b..df222d7e 100644 --- a/server-fn.c +++ b/server-fn.c @@ -185,6 +185,7 @@ server_kill_pane(struct window_pane *wp) recalculate_sizes(); } else { server_unzoom_window(w); + server_client_remove_pane(wp); layout_close_pane(wp); window_remove_pane(w, wp); server_redraw_window(w); @@ -349,6 +350,7 @@ server_destroy_pane(struct window_pane *wp, int notify) notify_pane("pane-exited", wp); server_unzoom_window(w); + server_client_remove_pane(wp); layout_close_pane(wp); window_remove_pane(w, wp); diff --git a/spawn.c b/spawn.c index f1ea87d2..5865a881 100644 --- a/spawn.c +++ b/spawn.c @@ -360,6 +360,7 @@ spawn_pane(struct spawn_context *sc, char **cause) xasprintf(cause, "fork failed: %s", strerror(errno)); new_wp->fd = -1; if (~sc->flags & SPAWN_RESPAWN) { + server_client_remove_pane(new_wp); layout_close_pane(new_wp); window_remove_pane(w, new_wp); } diff --git a/tmux.1 b/tmux.1 index 66922285..5209a5b4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -986,6 +986,8 @@ the client is read-only the client does not affect the size of other clients .It no-output the client does not receive pane output in control mode +.It active-pane +the client has an independent active pane .El .Pp A leading @@ -1000,6 +1002,13 @@ When a client is read-only, only keys bound to the or .Ic switch-client commands have any effect. +A client with the +.Ar active-pane +flag allows the active pane to be selected independently of the window's active +pane used by clients without the flag. +This only affects the cursor position and commands issued from the client; +other features such as hooks and styles continue to use the window's active +pane. .Pp If no server is started, .Ic attach-session diff --git a/tmux.h b/tmux.h index 2b88305a..a708a609 100644 --- a/tmux.h +++ b/tmux.h @@ -1510,6 +1510,14 @@ struct client_file { }; RB_HEAD(client_files, client_file); +/* Client window. */ +struct client_window { + u_int window; + struct window_pane *pane; + RB_ENTRY(client_window) entry; +}; +RB_HEAD(client_windows, client_window); + /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); @@ -1523,6 +1531,8 @@ struct client { struct tmuxpeer *peer; struct cmdq_list *queue; + struct client_windows windows; + pid_t pid; int fd; struct event event; @@ -1587,6 +1597,7 @@ struct client { #define CLIENT_STARTSERVER 0x10000000 #define CLIENT_REDRAWPANES 0x20000000 #define CLIENT_NOFORK 0x40000000 +#define CLIENT_ACTIVEPANE 0x80000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -1602,7 +1613,7 @@ struct client { (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_DETACHING) - int flags; + uint64_t flags; struct key_table *keytable; uint64_t redraw_panes; @@ -2301,6 +2312,7 @@ void server_add_accept(int); void printflike(1, 2) server_add_message(const char *, ...); /* server-client.c */ +RB_PROTOTYPE(client_windows, client_window, entry, server_client_window_cmp); u_int server_client_how_many(void); void server_client_set_overlay(struct client *, u_int, overlay_check_cb, overlay_mode_cb, overlay_draw_cb, overlay_key_cb, @@ -2323,6 +2335,9 @@ void server_client_push_stderr(struct client *); const char *server_client_get_cwd(struct client *, struct session *); void server_client_set_flags(struct client *, const char *); const char *server_client_get_flags(struct client *); +struct window_pane *server_client_get_pane(struct client *); +void server_client_set_pane(struct client *, struct window_pane *); +void server_client_remove_pane(struct window_pane *); /* server-fn.c */ void server_redraw_client(struct client *); diff --git a/tty.c b/tty.c index a1e2f2c6..a4f2acee 100644 --- a/tty.c +++ b/tty.c @@ -693,8 +693,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } if (s != NULL && tty->cstyle != s->cstyle) { if (tty_term_has(tty->term, TTYC_SS)) { - if (s->cstyle == 0 && - tty_term_has(tty->term, TTYC_SE)) + if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) tty_putcode(tty, TTYC_SE); else tty_putcode1(tty, TTYC_SS, s->cstyle); @@ -792,7 +791,7 @@ tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) { struct client *c = tty->client; struct window *w = c->session->curw->window; - struct window_pane *wp = w->active; + struct window_pane *wp = server_client_get_pane(c); u_int cx, cy, lines; lines = status_line_size(c); From 09a66451ce3df94120bb7d8486c89158bc5b85c6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 14 May 2020 13:18:05 +0100 Subject: [PATCH 0353/1006] Add screen write flags instead of individual bits and fix line length calculation with padding. --- grid.c | 4 +++- screen-write.c | 4 ++-- tmux.h | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/grid.c b/grid.c index 2437e2ea..06a82522 100644 --- a/grid.c +++ b/grid.c @@ -1391,7 +1391,9 @@ grid_line_length(struct grid *gd, u_int py) px = gd->sx; while (px > 0) { grid_get_cell(gd, px - 1, py, &gc); - if (gc.data.size != 1 || *gc.data.data != ' ') + if ((gc.flags & GRID_FLAG_PADDING) || + gc.data.size != 1 || + *gc.data.data != ' ') break; px--; } diff --git a/screen-write.c b/screen-write.c index 9571cbec..062a2929 100644 --- a/screen-write.c +++ b/screen-write.c @@ -184,10 +184,10 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, } if (ctx->wp != NULL && - !ctx->sync && + (~ctx->flags & SCREEN_WRITE_SYNC) && (sync || ctx->wp != ctx->wp->window->active)) { tty_write(tty_cmd_syncstart, ttyctx); - ctx->sync = 1; + ctx->flags |= SCREEN_WRITE_SYNC; } } diff --git a/tmux.h b/tmux.h index a708a609..99600f37 100644 --- a/tmux.h +++ b/tmux.h @@ -798,7 +798,9 @@ typedef void (*screen_write_init_ctx_cb)(struct screen_write_ctx *, struct screen_write_ctx { struct window_pane *wp; struct screen *s; - int sync; + + int flags; +#define SCREEN_WRITE_SYNC 0x1 screen_write_init_ctx_cb init_ctx_cb; void *arg; From 4dc0f3ee6beaa3aaee0f5845f03131322e366ba7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 14 May 2020 14:01:32 +0100 Subject: [PATCH 0354/1006] Update CHANGES. --- CHANGES | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 16586ec6..b5ed1eb7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,15 @@ CHANGES FROM 3.1b TO 3.2 +* Add an active-pane client flag (set with attach-session -f, new-session -f + or refresh-client -f). This allows a client to have an independent active + pane for interactive use (the window client pane is still used for many + things however). + +* Add a mark to copy mode, this is set with the set-mark command (bound to X) + and appears with the entire line shown using copy-mode-mark-style and the + marked character in reverse. The jump-to-mark command (bound to M-x) swaps + the mark and the cursor positions. + * Add a -D flag to make the tmux server run in the foreground and not as a daemon. @@ -8,7 +18,7 @@ CHANGES FROM 3.1b TO 3.2 * Fix the next-matching-bracket logic when using vi(1) keys. * Add a customize mode where options may be browsed and changed, includes - adding a brief description of each option. Bound to "C-b C" by default. + adding a brief description of each option. Bound to C-b C by default. * Change message log (C-b ~) so there is one for the server rather than one per client and it remains after detach, and make it useful by logging every @@ -21,7 +31,7 @@ CHANGES FROM 3.1b TO 3.2 refresh-client -F has become -f (-F stays for backwards compatibility) and attach-session and switch-client now have -f flags also. A new format - "client_flags" lists the flags and is shown by list-clients by default. + client_flags lists the flags and is shown by list-clients by default. This separates the read-only flag from "ignore size" behaviour (new ignore-size) flag - both behaviours are useful in different circumstances. From 97c8374855c216a645d558e5d56212e6c002cd72 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 14 May 2020 14:07:26 +0100 Subject: [PATCH 0355/1006] Tweak CHANGES. --- CHANGES | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index b5ed1eb7..eb94477f 100644 --- a/CHANGES +++ b/CHANGES @@ -71,8 +71,10 @@ CHANGES FROM 3.1b TO 3.2 * Check if the clear terminfo(5) capability starts with CSI and if so then assume the terminal is VT100-like, rather than relying on the XT capability. -* Improve command prompt tab completion and add menus both for strings and for - -t and -s (when used without a trailing space). +* Improve command prompt tab completion and add menus both for strings and -t + and -s (when used without a trailing space). command-prompt has additional + flags for only completing a window (-W) and a target (-T), allowing C-b ' to + only show windows and C-b . only targets. * Change all the style options to string options so they can support formats. Change pane-border-active-style to use this to change the border colour when From 31621036ad38ef3a2d88da838eb021430ea3c944 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 14 May 2020 16:49:08 +0100 Subject: [PATCH 0356/1006] Add an option to set the pane border lines style from a choice of single lines (ACS or UTF-8), double or heavy (UTF-8), simple (plain ASCII) or number (the pane numbers). Lines that won't work on a non-UTF-8 terminal are translated back into ACS when they are output. --- format-draw.c | 3 +- options-table.c | 11 +++++ screen-redraw.c | 124 +++++++++++++++++++++++++++++++++++------------- tmux.1 | 22 +++++++++ tmux.h | 11 +++++ tty-acs.c | 85 ++++++++++++++++++++++++++++++--- tty.c | 34 +++++++------ 7 files changed, 234 insertions(+), 56 deletions(-) diff --git a/format-draw.c b/format-draw.c index bd32b2a8..ec98ba95 100644 --- a/format-draw.c +++ b/format-draw.c @@ -600,7 +600,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, /* If this style pushed or popped the default, update it. */ if (sy.default_type == STYLE_DEFAULT_PUSH) { - memcpy(¤t_default, &saved_sy.gc, sizeof current_default); + memcpy(¤t_default, &saved_sy.gc, + sizeof current_default); sy.default_type = STYLE_DEFAULT_BASE; } else if (sy.default_type == STYLE_DEFAULT_POP) { memcpy(¤t_default, base, sizeof current_default); diff --git a/options-table.c b/options-table.c index 39bb7d25..aaedbac9 100644 --- a/options-table.c +++ b/options-table.c @@ -59,6 +59,9 @@ static const char *options_table_visual_bell_list[] = { static const char *options_table_pane_status_list[] = { "off", "top", "bottom", NULL }; +static const char *options_table_pane_lines_list[] = { + "single", "double", "heavy", "simple", "number", NULL +}; static const char *options_table_set_clipboard_list[] = { "off", "external", "on", NULL }; @@ -903,6 +906,14 @@ const struct options_table_entry options_table[] = { .text = "Format of text in the pane status lines." }, + { .name = "pane-border-lines", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, + .choices = options_table_pane_lines_list, + .default_num = PANE_LINES_SINGLE, + .text = "Type of the pane type lines." + }, + { .name = "pane-border-status", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, diff --git a/screen-redraw.c b/screen-redraw.c index 564fd9c9..3fa537a4 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -45,8 +45,36 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *, #define CELL_BORDERS " xqlkmjwvtun~" -const struct grid_cell screen_redraw_border_cell = { - { { ' ' }, 0, 1, 1 }, GRID_ATTR_CHARSET, 0, 8, 8, 0 +static const struct utf8_data screen_redraw_double_borders[] = { + { "", 0, 0, 0 }, + { "\342\225\221", 0, 3, 1 }, /* U+2551 */ + { "\342\225\220", 0, 3, 1 }, /* U+2550 */ + { "\342\225\224", 0, 3, 1 }, /* U+2554 */ + { "\342\225\227", 0, 3, 1 }, /* U+2557 */ + { "\342\225\232", 0, 3, 1 }, /* U+255A */ + { "\342\225\235", 0, 3, 1 }, /* U+255D */ + { "\342\225\246", 0, 3, 1 }, /* U+2566 */ + { "\342\225\251", 0, 3, 1 }, /* U+2569 */ + { "\342\225\240", 0, 3, 1 }, /* U+2560 */ + { "\342\225\243", 0, 3, 1 }, /* U+2563 */ + { "\342\225\254", 0, 3, 1 }, /* U+256C */ + { "\302\267", 0, 2, 1 } /* U+00B7 */ +}; + +static const struct utf8_data screen_redraw_heavy_borders[] = { + { "", 0, 0, 0 }, + { "\342\224\203", 0, 3, 1 }, /* U+2503 */ + { "\342\224\201", 0, 3, 1 }, /* U+2501 */ + { "\342\224\223", 0, 3, 1 }, /* U+2513 */ + { "\342\224\217", 0, 3, 1 }, /* U+250F */ + { "\342\224\227", 0, 3, 1 }, /* U+2517 */ + { "\342\224\233", 0, 3, 1 }, /* U+251B */ + { "\342\224\263", 0, 3, 1 }, /* U+2533 */ + { "\342\224\273", 0, 3, 1 }, /* U+253B */ + { "\342\224\243", 0, 3, 1 }, /* U+2523 */ + { "\342\224\253", 0, 3, 1 }, /* U+252B */ + { "\342\225\213", 0, 3, 1 }, /* U+254B */ + { "\302\267", 0, 2, 1 } /* U+00B7 */ }; enum screen_redraw_border_type { @@ -55,6 +83,40 @@ enum screen_redraw_border_type { SCREEN_REDRAW_BORDER }; +/* Get cell border character. */ +static void +screen_redraw_border_set(struct window_pane *wp, int pane_lines, int cell_type, + struct grid_cell *gc) +{ + u_int idx; + + switch (pane_lines) { + case PANE_LINES_NUMBER: + gc->attr &= ~GRID_ATTR_CHARSET; + if (window_pane_index(wp, &idx) == 0) + utf8_set(&gc->data, '0' + (idx % 10)); + else + utf8_set(&gc->data, '*'); + break; + case PANE_LINES_DOUBLE: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_copy(&gc->data, &screen_redraw_double_borders[cell_type]); + break; + case PANE_LINES_HEAVY: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_copy(&gc->data, &screen_redraw_heavy_borders[cell_type]); + break; + case PANE_LINES_SIMPLE: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_set(&gc->data, " |-+++++++++."[cell_type]); + break; + default: + gc->attr |= GRID_ATTR_CHARSET; + utf8_set(&gc->data, CELL_BORDERS[cell_type]); + break; + } +} + /* Return if window has only two panes. */ static int screen_redraw_two_panes(struct window *w, int direction) @@ -317,7 +379,7 @@ screen_redraw_check_is(u_int px, u_int py, int pane_status, /* Update pane status. */ static int screen_redraw_make_pane_status(struct client *c, struct window *w, - struct window_pane *wp) + struct window_pane *wp, int pane_lines) { struct grid_cell gc; const char *fmt; @@ -348,9 +410,9 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, screen_write_start(&ctx, &wp->status_screen); - gc.attr |= GRID_ATTR_CHARSET; + screen_redraw_border_set(wp, pane_lines, CELL_TOPBOTTOM, &gc); for (i = 0; i < width; i++) - screen_write_putc(&ctx, &gc, 'q'); + screen_write_cell(&ctx, &gc); gc.attr &= ~GRID_ATTR_CHARSET; screen_write_cursormove(&ctx, 0, 0, 0); @@ -436,7 +498,7 @@ screen_redraw_update(struct client *c, int flags) struct window *w = c->session->curw->window; struct window_pane *wp; struct options *wo = w->options; - int redraw; + int redraw, lines; if (c->message_string != NULL) redraw = status_message_redraw(c); @@ -451,9 +513,10 @@ screen_redraw_update(struct client *c, int flags) flags |= CLIENT_REDRAWOVERLAY; if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) { + lines = options_get_number(wo, "pane-border-lines"); redraw = 0; TAILQ_FOREACH(wp, &w->panes, entry) { - if (screen_redraw_make_pane_status(c, w, wp)) + if (screen_redraw_make_pane_status(c, w, wp, lines)) redraw = 1; } if (redraw) @@ -483,6 +546,7 @@ screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx) ctx->statuslines = lines; ctx->pane_status = options_get_number(wo, "pane-border-status"); + ctx->pane_lines = options_get_number(wo, "pane-border-lines"); tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); @@ -560,7 +624,6 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, struct window *w = s->curw->window; struct window_pane *active = server_client_get_pane(c); struct options *oo = w->options; - struct grid_cell *gc; struct format_tree *ft; if (wp->border_gc_set) @@ -568,18 +631,13 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, wp->border_gc_set = 1; ft = format_create_defaults(NULL, c, s, s->curw, wp); - gc = &wp->border_gc; - - if (screen_redraw_check_is(x, y, ctx->pane_status, active)) { - style_apply(gc, oo, "pane-active-border-style", ft); - gc->attr |= GRID_ATTR_CHARSET; - } else { - style_apply(gc, oo, "pane-border-style", ft); - gc->attr |= GRID_ATTR_CHARSET; - } - + if (screen_redraw_check_is(x, y, ctx->pane_status, active)) + style_apply(&wp->border_gc, oo, "pane-active-border-style", ft); + else + style_apply(&wp->border_gc, oo, "pane-border-style", ft); format_free(ft); - return (gc); + + return (&wp->border_gc); } /* Draw a border cell. */ @@ -590,39 +648,37 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; - u_int type, x = ctx->ox + i, y = ctx->oy + j; + u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; int pane_status = ctx->pane_status; - const struct grid_cell *gc; - struct grid_cell copy; + struct grid_cell gc; + const struct grid_cell *tmp; if (c->overlay_check != NULL && !c->overlay_check(c, x, y)) return; - type = screen_redraw_check_cell(c, x, y, pane_status, &wp); - if (type == CELL_INSIDE) + cell_type = screen_redraw_check_cell(c, x, y, pane_status, &wp); + if (cell_type == CELL_INSIDE) return; if (wp == NULL) - gc = &screen_redraw_border_cell; + memcpy(&gc, &grid_default_cell, sizeof gc); else { - gc = screen_redraw_draw_borders_style(ctx, x, y, wp); - if (gc == NULL) + tmp = screen_redraw_draw_borders_style(ctx, x, y, wp); + if (tmp == NULL) return; + memcpy(&gc, tmp, sizeof gc); if (server_is_marked(s, s->curw, marked_pane.wp) && - screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) { - memcpy(©, gc, sizeof copy); - copy.attr ^= GRID_ATTR_REVERSE; - gc = © - } + screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) + gc.attr ^= GRID_ATTR_REVERSE; } + screen_redraw_border_set(wp, ctx->pane_lines, cell_type, &gc); - tty_attributes(tty, gc, &grid_default_cell, NULL); if (ctx->statustop) tty_cursor(tty, i, ctx->statuslines + j); else tty_cursor(tty, i, j); - tty_putc(tty, CELL_BORDERS[type]); + tty_cell(tty, &gc, &grid_default_cell, NULL); } /* Draw the borders. */ diff --git a/tmux.1 b/tmux.1 index 5209a5b4..2979c4b2 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3924,6 +3924,28 @@ but set the starting index for pane numbers. .It Ic pane-border-format Ar format Set the text shown in pane border status lines. .Pp +.It Ic pane-border-lines Ar type +Set the type of characters used for drawing pane borders. +.Ar type +may be one of: +.Bl -tag -width Ds +.It single +single lines using ACS or UTF-8 characters +.It double +double lines using UTF-8 characters +.It heavy +heavy lines using UTF-8 characters +.It simple +simple ASCII characters +.It number +the pane number +.El +.Pp +.Ql double +and +.Ql heavy +will fall back to standard ACS line drawing when UTF-8 is not supported. +.Pp .It Xo Ic pane-border-status .Op Ic off | top | bottom .Xc diff --git a/tmux.h b/tmux.h index 99600f37..4df79bd5 100644 --- a/tmux.h +++ b/tmux.h @@ -822,6 +822,7 @@ struct screen_redraw_ctx { int statustop; int pane_status; + int pane_lines; u_int sx; u_int sy; @@ -1052,6 +1053,13 @@ TAILQ_HEAD(winlink_stack, winlink); #define PANE_STATUS_TOP 1 #define PANE_STATUS_BOTTOM 2 +/* Pane border lines option. */ +#define PANE_LINES_SINGLE 0 +#define PANE_LINES_DOUBLE 1 +#define PANE_LINES_HEAVY 2 +#define PANE_LINES_SIMPLE 3 +#define PANE_LINES_NUMBER 4 + /* Layout direction. */ enum layout_type { LAYOUT_LEFTRIGHT, @@ -2032,6 +2040,8 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); +void tty_cell(struct tty *, const struct grid_cell *, + const struct grid_cell *, int *); int tty_init(struct tty *, struct client *, int); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); @@ -2105,6 +2115,7 @@ void tty_default_features(int *, const char *, u_int); /* tty-acs.c */ int tty_acs_needed(struct tty *); const char *tty_acs_get(struct tty *, u_char); +int tty_acs_reverse_get(struct tty *, const char *, size_t); /* tty-keys.c */ void tty_keys_build(struct tty *); diff --git a/tty-acs.c b/tty-acs.c index 3e811103..63eccb93 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -19,11 +19,10 @@ #include #include +#include #include "tmux.h" -static int tty_acs_cmp(const void *, const void *); - /* Table mapping ACS entries to UTF-8. */ struct tty_acs_entry { u_char key; @@ -68,14 +67,65 @@ static const struct tty_acs_entry tty_acs_table[] = { { '~', "\302\267" } /* bullet */ }; +/* Table mapping UTF-8 to ACS entries. */ +struct tty_acs_reverse_entry { + const char *string; + u_char key; +}; +static const struct tty_acs_reverse_entry tty_acs_reverse2[] = { + { "\302\267", '~' } +}; +static const struct tty_acs_reverse_entry tty_acs_reverse3[] = { + { "\342\224\200", 'q' }, + { "\342\224\201", 'q' }, + { "\342\224\202", 'x' }, + { "\342\224\203", 'x' }, + { "\342\224\214", 'l' }, + { "\342\224\217", 'k' }, + { "\342\224\220", 'k' }, + { "\342\224\223", 'l' }, + { "\342\224\224", 'm' }, + { "\342\224\227", 'm' }, + { "\342\224\230", 'j' }, + { "\342\224\233", 'j' }, + { "\342\224\234", 't' }, + { "\342\224\243", 't' }, + { "\342\224\244", 'u' }, + { "\342\224\253", 'u' }, + { "\342\224\263", 'w' }, + { "\342\224\264", 'v' }, + { "\342\224\273", 'v' }, + { "\342\224\274", 'n' }, + { "\342\225\213", 'n' }, + { "\342\225\220", 'q' }, + { "\342\225\221", 'x' }, + { "\342\225\224", 'l' }, + { "\342\225\227", 'k' }, + { "\342\225\232", 'm' }, + { "\342\225\235", 'j' }, + { "\342\225\240", 't' }, + { "\342\225\243", 'u' }, + { "\342\225\246", 'w' }, + { "\342\225\251", 'v' }, + { "\342\225\254", 'n' }, +}; + static int tty_acs_cmp(const void *key, const void *value) { const struct tty_acs_entry *entry = value; - u_char ch; + int test = *(u_char *)key; - ch = *(u_char *) key; - return (ch - entry->key); + return (test - entry->key); +} + +static int +tty_acs_reverse_cmp(const void *key, const void *value) +{ + const struct tty_acs_reverse_entry *entry = value; + const char *test = key; + + return (strcmp(test, entry->string)); } /* Should this terminal use ACS instead of UTF-8 line drawing? */ @@ -104,11 +154,11 @@ tty_acs_needed(struct tty *tty) return (1); } -/* Retrieve ACS to output as a string. */ +/* Retrieve ACS to output as UTF-8. */ const char * tty_acs_get(struct tty *tty, u_char ch) { - struct tty_acs_entry *entry; + const struct tty_acs_entry *entry; /* Use the ACS set instead of UTF-8 if needed. */ if (tty_acs_needed(tty)) { @@ -124,3 +174,24 @@ tty_acs_get(struct tty *tty, u_char ch) return (NULL); return (entry->string); } + +/* Reverse UTF-8 into ACS. */ +int +tty_acs_reverse_get(__unused struct tty *tty, const char *s, size_t slen) +{ + const struct tty_acs_reverse_entry *table, *entry; + u_int items; + + if (slen == 2) { + table = tty_acs_reverse2; + items = nitems(tty_acs_reverse2); + } else if (slen == 3) { + table = tty_acs_reverse3; + items = nitems(tty_acs_reverse3); + } else + return (-1); + entry = bsearch(s, table, items, sizeof table[0], tty_acs_reverse_cmp); + if (entry == NULL) + return (-1); + return (entry->key); +} diff --git a/tty.c b/tty.c index a4f2acee..99996dfa 100644 --- a/tty.c +++ b/tty.c @@ -65,8 +65,6 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, u_int); static void tty_repeat_space(struct tty *, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); -static void tty_cell(struct tty *, const struct grid_cell *, - const struct grid_cell *, int *); static void tty_default_attributes(struct tty *, const struct grid_cell *, int *, u_int); @@ -1243,7 +1241,7 @@ static const struct grid_cell * tty_check_codeset(struct tty *tty, const struct grid_cell *gc) { static struct grid_cell new; - u_int n; + int c; /* Characters less than 0x7f are always fine, no matter what. */ if (gc->data.size == 1 && *gc->data.data < 0x7f) @@ -1252,14 +1250,21 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) /* UTF-8 terminal and a UTF-8 character - fine. */ if (tty->client->flags & CLIENT_UTF8) return (gc); + memcpy(&new, gc, sizeof new); + + /* See if this can be mapped to an ACS character. */ + c = tty_acs_reverse_get(tty, gc->data.data, gc->data.size); + if (c != -1) { + utf8_set(&new.data, c); + new.attr |= GRID_ATTR_CHARSET; + return (&new); + } /* Replace by the right number of underscores. */ - n = gc->data.width; - if (n > UTF8_SIZE) - n = UTF8_SIZE; - memcpy(&new, gc, sizeof new); - new.data.size = n; - memset(new.data.data, '_', n); + new.data.size = gc->data.width; + if (new.data.size > UTF8_SIZE) + new.data.size = UTF8_SIZE; + memset(new.data.data, '_', new.data.size); return (&new); } @@ -1924,7 +1929,7 @@ tty_cmd_syncstart(struct tty *tty, __unused const struct tty_ctx *ctx) tty_sync_start(tty); } -static void +void tty_cell(struct tty *tty, const struct grid_cell *gc, const struct grid_cell *defaults, int *palette) { @@ -1940,12 +1945,13 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, if (gc->flags & GRID_FLAG_PADDING) return; - /* Set the attributes. */ - tty_attributes(tty, gc, defaults, palette); - - /* Get the cell and if ASCII write with putc to do ACS translation. */ + /* Check the output codeset and apply attributes. */ gcp = tty_check_codeset(tty, gc); + tty_attributes(tty, gcp, defaults, palette); + + /* If it is a single character, write with putc to handle ACS. */ if (gcp->data.size == 1) { + tty_attributes(tty, gcp, defaults, palette); if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f) return; tty_putc(tty, *gcp->data.data); From bef70132ac23e5d13fd6db3d3f5f8ebdf486c1f3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 14 May 2020 16:53:04 +0100 Subject: [PATCH 0357/1006] Check if outside the window before checking if on the border so that cells that are outside in one direction but not the other are not given the wrong type. --- screen-redraw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 3fa537a4..ae9d8a90 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -212,14 +212,14 @@ screen_redraw_cell_border(struct client *c, u_int px, u_int py, int pane_status) struct window *w = c->session->curw->window; struct window_pane *wp; - /* On the window border? */ - if (px == w->sx || py == w->sy) - return (1); - /* Outside the window? */ if (px > w->sx || py > w->sy) return (0); + /* On the window border? */ + if (px == w->sx || py == w->sy) + return (1); + /* Check all the panes. */ TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) From c4d8100b2fd220d358481db419221ee1454d3cad Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 14 May 2020 16:58:14 +0100 Subject: [PATCH 0358/1006] Draw outside correctly with pane numbers. --- screen-redraw.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/screen-redraw.c b/screen-redraw.c index ae9d8a90..002970e9 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -92,8 +92,13 @@ screen_redraw_border_set(struct window_pane *wp, int pane_lines, int cell_type, switch (pane_lines) { case PANE_LINES_NUMBER: + if (cell_type == CELL_OUTSIDE) { + gc->attr |= GRID_ATTR_CHARSET; + utf8_set(&gc->data, CELL_BORDERS[CELL_OUTSIDE]); + break; + } gc->attr &= ~GRID_ATTR_CHARSET; - if (window_pane_index(wp, &idx) == 0) + if (wp != NULL && window_pane_index(wp, &idx) == 0) utf8_set(&gc->data, '0' + (idx % 10)); else utf8_set(&gc->data, '*'); From 5ee4d991b6a325848083017665ac3d3ace2d1fa1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 10:31:54 +0100 Subject: [PATCH 0359/1006] xterm-keys has been on by default for 5 years and all other modern terminals use these key sequences by default. Merge the code into the main tty and input tree processing (convering the latter to use a tree rather than a table at the same time) and make the option a no-op. --- Makefile.am | 3 +- input-keys.c | 502 +++++++++++++++++++++++++++++++++++------------- options-table.c | 5 +- popup.c | 2 +- server.c | 1 + tmux.1 | 10 - tmux.h | 10 +- tty-keys.c | 135 +++++++++---- xterm-keys.c | 252 ------------------------ 9 files changed, 471 insertions(+), 449 deletions(-) delete mode 100644 xterm-keys.c diff --git a/Makefile.am b/Makefile.am index fdd39107..91d641fd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -184,8 +184,7 @@ dist_tmux_SOURCES = \ window-tree.c \ window.c \ xmalloc.c \ - xmalloc.h \ - xterm-keys.c + xmalloc.h nodist_tmux_SOURCES = osdep-@PLATFORM@.c # Add compat file for forkpty. diff --git a/input-keys.c b/input-keys.c index 04ecb264..30141243 100644 --- a/input-keys.c +++ b/input-keys.c @@ -32,112 +32,331 @@ static void input_key_mouse(struct window_pane *, struct mouse_event *); -struct input_key_ent { - key_code key; - const char *data; +/* Entry in the key tree. */ +struct input_key_entry { + key_code key; + const char *data; - int flags; -#define INPUTKEY_KEYPAD 0x1 /* keypad key */ -#define INPUTKEY_CURSOR 0x2 /* cursor key */ + RB_ENTRY(input_key_entry) entry; }; +RB_HEAD(input_key_tree, input_key_entry); -static const struct input_key_ent input_keys[] = { +/* Tree of input keys. */ +static int input_key_cmp(struct input_key_entry *, + struct input_key_entry *); +RB_GENERATE_STATIC(input_key_tree, input_key_entry, entry, input_key_cmp); +struct input_key_tree input_key_tree = RB_INITIALIZER(&input_key_tree); + +/* List of default keys, the tree is built from this. */ +static struct input_key_entry input_key_defaults[] = { /* Paste keys. */ - { KEYC_PASTE_START, "\033[200~", 0 }, - { KEYC_PASTE_END, "\033[201~", 0 }, + { .key = KEYC_PASTE_START, + .data = "\033[200~" + }, + { .key = KEYC_PASTE_END, + .data = "\033[201~" + }, /* Function keys. */ - { KEYC_F1, "\033OP", 0 }, - { KEYC_F2, "\033OQ", 0 }, - { KEYC_F3, "\033OR", 0 }, - { KEYC_F4, "\033OS", 0 }, - { KEYC_F5, "\033[15~", 0 }, - { KEYC_F6, "\033[17~", 0 }, - { KEYC_F7, "\033[18~", 0 }, - { KEYC_F8, "\033[19~", 0 }, - { KEYC_F9, "\033[20~", 0 }, - { KEYC_F10, "\033[21~", 0 }, - { KEYC_F11, "\033[23~", 0 }, - { KEYC_F12, "\033[24~", 0 }, - { KEYC_F1|KEYC_SHIFT, "\033[25~", 0 }, - { KEYC_F2|KEYC_SHIFT, "\033[26~", 0 }, - { KEYC_F3|KEYC_SHIFT, "\033[28~", 0 }, - { KEYC_F4|KEYC_SHIFT, "\033[29~", 0 }, - { KEYC_F5|KEYC_SHIFT, "\033[31~", 0 }, - { KEYC_F6|KEYC_SHIFT, "\033[32~", 0 }, - { KEYC_F7|KEYC_SHIFT, "\033[33~", 0 }, - { KEYC_F8|KEYC_SHIFT, "\033[34~", 0 }, - { KEYC_IC, "\033[2~", 0 }, - { KEYC_DC, "\033[3~", 0 }, - { KEYC_HOME, "\033[1~", 0 }, - { KEYC_END, "\033[4~", 0 }, - { KEYC_NPAGE, "\033[6~", 0 }, - { KEYC_PPAGE, "\033[5~", 0 }, - { KEYC_BTAB, "\033[Z", 0 }, + { .key = KEYC_F1, + .data = "\033OP" + }, + { .key = KEYC_F2, + .data = "\033OQ" + }, + { .key = KEYC_F3, + .data = "\033OR" + }, + { .key = KEYC_F4, + .data = "\033OS" + }, + { .key = KEYC_F5, + .data = "\033[15~" + }, + { .key = KEYC_F6, + .data = "\033[17~" + }, + { .key = KEYC_F7, + .data = "\033[18~" + }, + { .key = KEYC_F8, + .data = "\033[19~" + }, + { .key = KEYC_F9, + .data = "\033[20~" + }, + { .key = KEYC_F10, + .data = "\033[21~" + }, + { .key = KEYC_F11, + .data = "\033[23~" + }, + { .key = KEYC_F12, + .data = "\033[24~" + }, + { .key = KEYC_F1|KEYC_SHIFT, + .data = "\033[25~" + }, + { .key = KEYC_F2|KEYC_SHIFT, + .data = "\033[26~" + }, + { .key = KEYC_F3|KEYC_SHIFT, + .data = "\033[28~" + }, + { .key = KEYC_F4|KEYC_SHIFT, + .data = "\033[29~" + }, + { .key = KEYC_F5|KEYC_SHIFT, + .data = "\033[31~" + }, + { .key = KEYC_F6|KEYC_SHIFT, + .data = "\033[32~" + }, + { .key = KEYC_F7|KEYC_SHIFT, + .data = "\033[33~" + }, + { .key = KEYC_F8|KEYC_SHIFT, + .data = "\033[34~" + }, + { .key = KEYC_IC, + .data = "\033[2~" + }, + { .key = KEYC_DC, + .data = "\033[3~" + }, + { .key = KEYC_HOME, + .data = "\033[1~" + }, + { .key = KEYC_END, + .data = "\033[4~" + }, + { .key = KEYC_NPAGE, + .data = "\033[6~" + }, + { .key = KEYC_PPAGE, + .data = "\033[5~" + }, + { .key = KEYC_BTAB, + .data = "\033[Z" + }, - /* - * Arrow keys. Cursor versions must come first. The codes are toggled - * between CSI and SS3 versions when ctrl is pressed. - */ - { KEYC_UP|KEYC_CTRL, "\033[A", INPUTKEY_CURSOR }, - { KEYC_DOWN|KEYC_CTRL, "\033[B", INPUTKEY_CURSOR }, - { KEYC_RIGHT|KEYC_CTRL, "\033[C", INPUTKEY_CURSOR }, - { KEYC_LEFT|KEYC_CTRL, "\033[D", INPUTKEY_CURSOR }, + /* Arrow keys. */ + { .key = KEYC_UP|KEYC_CURSOR, + .data = "\033OA" + }, + { .key = KEYC_DOWN|KEYC_CURSOR, + .data = "\033OB" + }, + { .key = KEYC_RIGHT|KEYC_CURSOR, + .data = "\033OC" + }, + { .key = KEYC_LEFT|KEYC_CURSOR, + .data = "\033OD" + }, + { .key = KEYC_UP, + .data = "\033[A" + }, + { .key = KEYC_DOWN, + .data = "\033[B" + }, + { .key = KEYC_RIGHT, + .data = "\033[C" + }, + { .key = KEYC_LEFT, + .data = "\033[D" + }, - { KEYC_UP, "\033OA", INPUTKEY_CURSOR }, - { KEYC_DOWN, "\033OB", INPUTKEY_CURSOR }, - { KEYC_RIGHT, "\033OC", INPUTKEY_CURSOR }, - { KEYC_LEFT, "\033OD", INPUTKEY_CURSOR }, + /* Keypad keys. */ + { .key = KEYC_KP_SLASH|KEYC_KEYPAD, + .data = "\033Oo" + }, + { .key = KEYC_KP_STAR|KEYC_KEYPAD, + .data = "\033Oj" + }, + { .key = KEYC_KP_MINUS|KEYC_KEYPAD, + .data = "\033Om" + }, + { .key = KEYC_KP_SEVEN|KEYC_KEYPAD, + .data = "\033Ow" + }, + { .key = KEYC_KP_EIGHT|KEYC_KEYPAD, + .data = "\033Ox" + }, + { .key = KEYC_KP_NINE|KEYC_KEYPAD, + .data = "\033Oy" + }, + { .key = KEYC_KP_PLUS|KEYC_KEYPAD, + .data = "\033Ok" + }, + { .key = KEYC_KP_FOUR|KEYC_KEYPAD, + .data = "\033Ot" + }, + { .key = KEYC_KP_FIVE|KEYC_KEYPAD, + .data = "\033Ou" + }, + { .key = KEYC_KP_SIX|KEYC_KEYPAD, + .data = "\033Ov" + }, + { .key = KEYC_KP_ONE|KEYC_KEYPAD, + .data = "\033Oq" + }, + { .key = KEYC_KP_TWO|KEYC_KEYPAD, + .data = "\033Or" + }, + { .key = KEYC_KP_THREE|KEYC_KEYPAD, + .data = "\033Os" + }, + { .key = KEYC_KP_ENTER|KEYC_KEYPAD, + .data = "\033OM" + }, + { .key = KEYC_KP_ZERO|KEYC_KEYPAD, + .data = "\033Op" + }, + { .key = KEYC_KP_PERIOD|KEYC_KEYPAD, + .data = "\033On" + }, + { .key = KEYC_KP_SLASH, + .data = "/" + }, + { .key = KEYC_KP_STAR, + .data = "*" + }, + { .key = KEYC_KP_MINUS, + .data = "-" + }, + { .key = KEYC_KP_SEVEN, + .data = "7" + }, + { .key = KEYC_KP_EIGHT, + .data = "8" + }, + { .key = KEYC_KP_NINE, + .data = "9" + }, + { .key = KEYC_KP_PLUS, + .data = "+" + }, + { .key = KEYC_KP_FOUR, + .data = "4" + }, + { .key = KEYC_KP_FIVE, + .data = "5" + }, + { .key = KEYC_KP_SIX, + .data = "6" + }, + { .key = KEYC_KP_ONE, + .data = "1" + }, + { .key = KEYC_KP_TWO, + .data = "2" + }, + { .key = KEYC_KP_THREE, + .data = "3" + }, + { .key = KEYC_KP_ENTER, + .data = "\n" + }, + { .key = KEYC_KP_ZERO, + .data = "0" + }, + { .key = KEYC_KP_PERIOD, + .data = "." + }, - { KEYC_UP|KEYC_CTRL, "\033OA", 0 }, - { KEYC_DOWN|KEYC_CTRL, "\033OB", 0 }, - { KEYC_RIGHT|KEYC_CTRL, "\033OC", 0 }, - { KEYC_LEFT|KEYC_CTRL, "\033OD", 0 }, - - { KEYC_UP, "\033[A", 0 }, - { KEYC_DOWN, "\033[B", 0 }, - { KEYC_RIGHT, "\033[C", 0 }, - { KEYC_LEFT, "\033[D", 0 }, - - /* Keypad keys. Keypad versions must come first. */ - { KEYC_KP_SLASH, "\033Oo", INPUTKEY_KEYPAD }, - { KEYC_KP_STAR, "\033Oj", INPUTKEY_KEYPAD }, - { KEYC_KP_MINUS, "\033Om", INPUTKEY_KEYPAD }, - { KEYC_KP_SEVEN, "\033Ow", INPUTKEY_KEYPAD }, - { KEYC_KP_EIGHT, "\033Ox", INPUTKEY_KEYPAD }, - { KEYC_KP_NINE, "\033Oy", INPUTKEY_KEYPAD }, - { KEYC_KP_PLUS, "\033Ok", INPUTKEY_KEYPAD }, - { KEYC_KP_FOUR, "\033Ot", INPUTKEY_KEYPAD }, - { KEYC_KP_FIVE, "\033Ou", INPUTKEY_KEYPAD }, - { KEYC_KP_SIX, "\033Ov", INPUTKEY_KEYPAD }, - { KEYC_KP_ONE, "\033Oq", INPUTKEY_KEYPAD }, - { KEYC_KP_TWO, "\033Or", INPUTKEY_KEYPAD }, - { KEYC_KP_THREE, "\033Os", INPUTKEY_KEYPAD }, - { KEYC_KP_ENTER, "\033OM", INPUTKEY_KEYPAD }, - { KEYC_KP_ZERO, "\033Op", INPUTKEY_KEYPAD }, - { KEYC_KP_PERIOD, "\033On", INPUTKEY_KEYPAD }, - - { KEYC_KP_SLASH, "/", 0 }, - { KEYC_KP_STAR, "*", 0 }, - { KEYC_KP_MINUS, "-", 0 }, - { KEYC_KP_SEVEN, "7", 0 }, - { KEYC_KP_EIGHT, "8", 0 }, - { KEYC_KP_NINE, "9", 0 }, - { KEYC_KP_PLUS, "+", 0 }, - { KEYC_KP_FOUR, "4", 0 }, - { KEYC_KP_FIVE, "5", 0 }, - { KEYC_KP_SIX, "6", 0 }, - { KEYC_KP_ONE, "1", 0 }, - { KEYC_KP_TWO, "2", 0 }, - { KEYC_KP_THREE, "3", 0 }, - { KEYC_KP_ENTER, "\n", 0 }, - { KEYC_KP_ZERO, "0", 0 }, - { KEYC_KP_PERIOD, ".", 0 }, + /* Keys with an embedded modifier. */ + { .key = KEYC_F1|KEYC_XTERM, + .data = "\033[1;_P" + }, + { .key = KEYC_F2|KEYC_XTERM, + .data = "\033[1;_Q" + }, + { .key = KEYC_F3|KEYC_XTERM, + .data = "\033[1;_R" + }, + { .key = KEYC_F4|KEYC_XTERM, + .data = "\033[1;_S" + }, + { .key = KEYC_F5|KEYC_XTERM, + .data = "\033[15;_~" + }, + { .key = KEYC_F6|KEYC_XTERM, + .data = "\033[17;_~" + }, + { .key = KEYC_F7|KEYC_XTERM, + .data = "\033[18;_~" + }, + { .key = KEYC_F8|KEYC_XTERM, + .data = "\033[19;_~" + }, + { .key = KEYC_F9|KEYC_XTERM, + .data = "\033[20;_~" + }, + { .key = KEYC_F10|KEYC_XTERM, + .data = "\033[21;_~" + }, + { .key = KEYC_F11|KEYC_XTERM, + .data = "\033[23;_~" + }, + { .key = KEYC_F12|KEYC_XTERM, + .data = "\033[24;_~" + }, + { .key = KEYC_UP|KEYC_XTERM, + .data = "\033[1;_A" + }, + { .key = KEYC_DOWN|KEYC_XTERM, + .data = "\033[1;_B" + }, + { .key = KEYC_RIGHT|KEYC_XTERM, + .data = "\033[1;_C" + }, + { .key = KEYC_LEFT|KEYC_XTERM, + .data = "\033[1;_D" + }, + { .key = KEYC_HOME|KEYC_XTERM, + .data = "\033[1;_H" + }, + { .key = KEYC_END|KEYC_XTERM, + .data = "\033[1;_F" + }, + { .key = KEYC_PPAGE|KEYC_XTERM, + .data = "\033[5;_~" + }, + { .key = KEYC_NPAGE|KEYC_XTERM, + .data = "\033[6;_~" + }, + { .key = KEYC_IC|KEYC_XTERM, + .data = "\033[2;_~" + }, + { .key = KEYC_DC|KEYC_XTERM, + .data = "\033[3;_~" } }; +static const key_code input_key_modifiers[] = { + 0, + 0, + KEYC_SHIFT|KEYC_XTERM, + KEYC_ESCAPE|KEYC_XTERM, + KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM, + KEYC_CTRL|KEYC_XTERM, + KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM, + KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM, + KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM +}; + +/* Input key comparison function. */ +static int +input_key_cmp(struct input_key_entry *ike1, struct input_key_entry *ike2) +{ + if (ike1->key < ike2->key) + return (-1); + if (ike1->key > ike2->key) + return (1); + return (0); +} /* Split a character into two UTF-8 bytes. */ static size_t -input_split2(u_int c, u_char *dst) +input_key_split2(u_int c, u_char *dst) { if (c > 0x7f) { dst[0] = (c >> 6) | 0xc0; @@ -148,32 +367,63 @@ input_split2(u_int c, u_char *dst) return (1); } +/* Build input key tree. */ +void +input_key_build(void) +{ + struct input_key_entry *ike, *new; + u_int i, j; + char *data; + + for (i = 0; i < nitems(input_key_defaults); i++) { + ike = &input_key_defaults[i]; + if (~ike->key & KEYC_XTERM) { + RB_INSERT(input_key_tree, &input_key_tree, ike); + continue; + } + + for (j = 2; j < nitems(input_key_modifiers); j++) { + data = xstrdup(ike->data); + data[strcspn(data, "_")] = '0' + j; + + new = xcalloc(1, sizeof *new); + new->key = ike->key|input_key_modifiers[j]; + new->data = data; + RB_INSERT(input_key_tree, &input_key_tree, new); + } + } + + RB_FOREACH(ike, input_key_tree, &input_key_tree) { + log_debug("%s: 0x%llx (%s) is %s", __func__, ike->key, + key_string_lookup_key(ike->key), ike->data); + } +} + /* Translate a key code into an output key sequence for a pane. */ int input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) { - log_debug("writing key 0x%llx (%s) to %%%u", key, - key_string_lookup_key(key), wp->id); + if (log_get_level() != 0) { + log_debug("writing key 0x%llx (%s) to %%%u", key, + key_string_lookup_key(key), wp->id); + } if (KEYC_IS_MOUSE(key)) { if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) input_key_mouse(wp, m); return (0); } - return (input_key(wp, wp->screen, wp->event, key)); + return (input_key(wp->screen, wp->event, key)); } /* Translate a key code into an output key sequence. */ int -input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, - key_code key) +input_key(struct screen *s, struct bufferevent *bev, key_code key) { - const struct input_key_ent *ike; - u_int i; - size_t dlen; - char *out; - key_code justkey, newkey; - struct utf8_data ud; + struct input_key_entry *ike, entry; + size_t datalen; + key_code justkey, newkey; + struct utf8_data ud; /* Mouse keys need a pane. */ if (KEYC_IS_MOUSE(key)) @@ -216,43 +466,25 @@ input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, } /* - * Then try to look this up as an xterm key, if the flag to output them - * is set. + * Look up in the tree. If not in application keypad or cursor mode, + * remove the flags from the key. */ - if (wp == NULL || options_get_number(wp->window->options, "xterm-keys")) { - if ((out = xterm_keys_lookup(key)) != NULL) { - bufferevent_write(bev, out, strlen(out)); - free(out); - return (0); - } - } - key &= ~KEYC_XTERM; - - /* Otherwise look the key up in the table. */ - for (i = 0; i < nitems(input_keys); i++) { - ike = &input_keys[i]; - - if ((ike->flags & INPUTKEY_KEYPAD) && (~s->mode & MODE_KKEYPAD)) - continue; - if ((ike->flags & INPUTKEY_CURSOR) && (~s->mode & MODE_KCURSOR)) - continue; - - if ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key) - break; - if (ike->key == key) - break; - } - if (i == nitems(input_keys)) { + if (~s->mode & MODE_KKEYPAD) + key &= ~KEYC_KEYPAD; + if (~s->mode & MODE_KCURSOR) + key &= ~KEYC_CURSOR; + entry.key = key; + if ((ike = RB_FIND(input_key_tree, &input_key_tree, &entry)) == NULL) { log_debug("key 0x%llx missing", key); return (-1); } - dlen = strlen(ike->data); + datalen = strlen(ike->data); log_debug("found key 0x%llx: \"%s\"", key, ike->data); /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) bufferevent_write(bev, "\033", 1); - bufferevent_write(bev, ike->data, dlen); + bufferevent_write(bev, ike->data, datalen); return (0); } @@ -308,9 +540,9 @@ input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y, if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) return (0); len = xsnprintf(buf, sizeof buf, "\033[M"); - len += input_split2(m->b + 32, &buf[len]); - len += input_split2(x + 33, &buf[len]); - len += input_split2(y + 33, &buf[len]); + len += input_key_split2(m->b + 32, &buf[len]); + len += input_key_split2(x + 33, &buf[len]); + len += input_key_split2(y + 33, &buf[len]); } else { if (m->b > 223) return (0); diff --git a/options-table.c b/options-table.c index aaedbac9..7e379382 100644 --- a/options-table.c +++ b/options-table.c @@ -1052,11 +1052,12 @@ const struct options_table_entry options_table[] = { "bottom." }, - { .name = "xterm-keys", + { .name = "xterm-keys", /* no longer used */ .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1, - .text = "Whether xterm-style function key sequences should be sent." + .text = "Whether xterm-style function key sequences should be sent. " + "This option is no longer used." }, /* Hook options. */ diff --git a/popup.c b/popup.c index 2a50d4ad..8d88c99f 100644 --- a/popup.c +++ b/popup.c @@ -328,7 +328,7 @@ popup_key_cb(struct client *c, struct key_event *event) bufferevent_write(job_get_event(pd->job), buf, len); return (0); } - input_key(NULL, &pd->s, job_get_event(pd->job), event->key); + input_key(&pd->s, job_get_event(pd->job), event->key); return (0); } diff --git a/server.c b/server.c index c451989c..5613ddea 100644 --- a/server.c +++ b/server.c @@ -198,6 +198,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, "tty ps", NULL) != 0) fatal("pledge failed"); + input_key_build(); RB_INIT(&windows); RB_INIT(&all_window_panes); TAILQ_INIT(&clients); diff --git a/tmux.1 b/tmux.1 index 2979c4b2..17793911 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4054,16 +4054,6 @@ option. .Xc If this option is set, searches will wrap around the end of the pane contents. The default is on. -.Pp -.It Xo Ic xterm-keys -.Op Ic on | off -.Xc -If this option is set, -.Nm -will generate -.Xr xterm 1 -style -function key sequences; these have a number included to indicate modifiers such -as Shift, Alt or Ctrl. .El .Pp Available pane options are: diff --git a/tmux.h b/tmux.h index 4df79bd5..22baa560 100644 --- a/tmux.h +++ b/tmux.h @@ -125,6 +125,8 @@ struct winlink; #define KEYC_SHIFT 0x0400000000000ULL #define KEYC_XTERM 0x0800000000000ULL #define KEYC_LITERAL 0x1000000000000ULL +#define KEYC_KEYPAD 0x2000000000000ULL +#define KEYC_CURSOR 0x4000000000000ULL /* Available user keys. */ #define KEYC_NUSER 1000 @@ -2417,16 +2419,12 @@ void input_parse_screen(struct input_ctx *, struct screen *, screen_write_init_ctx_cb, void *, u_char *, size_t); /* input-key.c */ +void input_key_build(void); int input_key_pane(struct window_pane *, key_code, struct mouse_event *); -int input_key(struct window_pane *, struct screen *, struct bufferevent *, - key_code); +int input_key(struct screen *, struct bufferevent *, key_code); int input_key_get_mouse(struct screen *, struct mouse_event *, u_int, u_int, const char **, size_t *); -/* xterm-keys.c */ -char *xterm_keys_lookup(key_code); -int xterm_keys_find(const char *, size_t, size_t *, key_code *); - /* colour.c */ int colour_find_rgb(u_char, u_char, u_char); int colour_join_rgb(u_char, u_char, u_char); diff --git a/tty-keys.c b/tty-keys.c index dc064a17..8b3ee1ea 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -69,33 +69,33 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { * put the terminal into keypad_xmit mode. Translation of numbers * mode/applications mode is done in input-keys.c. */ - { "\033Oo", KEYC_KP_SLASH }, - { "\033Oj", KEYC_KP_STAR }, - { "\033Om", KEYC_KP_MINUS }, - { "\033Ow", KEYC_KP_SEVEN }, - { "\033Ox", KEYC_KP_EIGHT }, - { "\033Oy", KEYC_KP_NINE }, - { "\033Ok", KEYC_KP_PLUS }, - { "\033Ot", KEYC_KP_FOUR }, - { "\033Ou", KEYC_KP_FIVE }, - { "\033Ov", KEYC_KP_SIX }, - { "\033Oq", KEYC_KP_ONE }, - { "\033Or", KEYC_KP_TWO }, - { "\033Os", KEYC_KP_THREE }, - { "\033OM", KEYC_KP_ENTER }, - { "\033Op", KEYC_KP_ZERO }, - { "\033On", KEYC_KP_PERIOD }, + { "\033Oo", KEYC_KP_SLASH|KEYC_KEYPAD }, + { "\033Oj", KEYC_KP_STAR|KEYC_KEYPAD }, + { "\033Om", KEYC_KP_MINUS|KEYC_KEYPAD }, + { "\033Ow", KEYC_KP_SEVEN|KEYC_KEYPAD }, + { "\033Ox", KEYC_KP_EIGHT|KEYC_KEYPAD }, + { "\033Oy", KEYC_KP_NINE|KEYC_KEYPAD }, + { "\033Ok", KEYC_KP_PLUS|KEYC_KEYPAD }, + { "\033Ot", KEYC_KP_FOUR|KEYC_KEYPAD }, + { "\033Ou", KEYC_KP_FIVE|KEYC_KEYPAD }, + { "\033Ov", KEYC_KP_SIX|KEYC_KEYPAD }, + { "\033Oq", KEYC_KP_ONE|KEYC_KEYPAD }, + { "\033Or", KEYC_KP_TWO|KEYC_KEYPAD }, + { "\033Os", KEYC_KP_THREE|KEYC_KEYPAD }, + { "\033OM", KEYC_KP_ENTER|KEYC_KEYPAD }, + { "\033Op", KEYC_KP_ZERO|KEYC_KEYPAD }, + { "\033On", KEYC_KP_PERIOD|KEYC_KEYPAD }, /* Arrow keys. */ - { "\033OA", KEYC_UP }, - { "\033OB", KEYC_DOWN }, - { "\033OC", KEYC_RIGHT }, - { "\033OD", KEYC_LEFT }, + { "\033OA", KEYC_UP|KEYC_CURSOR }, + { "\033OB", KEYC_DOWN|KEYC_CURSOR }, + { "\033OC", KEYC_RIGHT|KEYC_CURSOR }, + { "\033OD", KEYC_LEFT|KEYC_CURSOR }, - { "\033[A", KEYC_UP }, - { "\033[B", KEYC_DOWN }, - { "\033[C", KEYC_RIGHT }, - { "\033[D", KEYC_LEFT }, + { "\033[A", KEYC_UP|KEYC_CURSOR }, + { "\033[B", KEYC_DOWN|KEYC_CURSOR }, + { "\033[C", KEYC_RIGHT|KEYC_CURSOR }, + { "\033[D", KEYC_LEFT|KEYC_CURSOR }, /* Other (xterm) "cursor" keys. */ { "\033OH", KEYC_HOME }, @@ -182,11 +182,59 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[201~", KEYC_PASTE_END }, }; +/* Default xterm keys. */ +struct tty_default_key_xterm { + const char *template; + key_code key; +}; +static const struct tty_default_key_xterm tty_default_xterm_keys[] = { + { "\033[1;_P", KEYC_F1 }, + { "\033O1;_P", KEYC_F1 }, + { "\033O_P", KEYC_F1 }, + { "\033[1;_Q", KEYC_F2 }, + { "\033O1;_Q", KEYC_F2 }, + { "\033O_Q", KEYC_F2 }, + { "\033[1;_R", KEYC_F3 }, + { "\033O1;_R", KEYC_F3 }, + { "\033O_R", KEYC_F3 }, + { "\033[1;_S", KEYC_F4 }, + { "\033O1;_S", KEYC_F4 }, + { "\033O_S", KEYC_F4 }, + { "\033[15;_~", KEYC_F5 }, + { "\033[17;_~", KEYC_F6 }, + { "\033[18;_~", KEYC_F7 }, + { "\033[19;_~", KEYC_F8 }, + { "\033[20;_~", KEYC_F9 }, + { "\033[21;_~", KEYC_F10 }, + { "\033[23;_~", KEYC_F11 }, + { "\033[24;_~", KEYC_F12 }, + { "\033[1;_A", KEYC_UP }, + { "\033[1;_B", KEYC_DOWN }, + { "\033[1;_C", KEYC_RIGHT }, + { "\033[1;_D", KEYC_LEFT }, + { "\033[1;_H", KEYC_HOME }, + { "\033[1;_F", KEYC_END }, + { "\033[5;_~", KEYC_PPAGE }, + { "\033[6;_~", KEYC_NPAGE }, + { "\033[2;_~", KEYC_IC }, + { "\033[3;_~", KEYC_DC }, +}; +static const key_code tty_default_xterm_modifiers[] = { + 0, + 0, + KEYC_SHIFT|KEYC_XTERM, + KEYC_ESCAPE|KEYC_XTERM, + KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM, + KEYC_CTRL|KEYC_XTERM, + KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM, + KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM, + KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM +}; + /* - * Default terminfo(5) keys. Any keys that have builtin modifiers - * (that is, where the key itself contains the modifiers) has the - * KEYC_XTERM flag set so a leading escape is not treated as meta (and - * probably removed). + * Default terminfo(5) keys. Any keys that have builtin modifiers (that is, + * where the key itself contains the modifiers) has the KEYC_XTERM flag set so + * a leading escape is not treated as meta (and probably removed). */ struct tty_default_key_code { enum tty_code_code code; @@ -272,10 +320,10 @@ static const struct tty_default_key_code tty_default_code_keys[] = { { TTYC_KCBT, KEYC_BTAB }, /* Arrow keys from terminfo. */ - { TTYC_KCUU1, KEYC_UP }, - { TTYC_KCUD1, KEYC_DOWN }, - { TTYC_KCUB1, KEYC_LEFT }, - { TTYC_KCUF1, KEYC_RIGHT }, + { TTYC_KCUU1, KEYC_UP|KEYC_CURSOR }, + { TTYC_KCUD1, KEYC_DOWN|KEYC_CURSOR }, + { TTYC_KCUB1, KEYC_LEFT|KEYC_CURSOR }, + { TTYC_KCUF1, KEYC_RIGHT|KEYC_CURSOR }, /* Key and modifier capabilities. */ { TTYC_KDC2, KEYC_DC|KEYC_SHIFT|KEYC_XTERM }, @@ -403,17 +451,30 @@ void tty_keys_build(struct tty *tty) { const struct tty_default_key_raw *tdkr; + const struct tty_default_key_xterm *tdkx; const struct tty_default_key_code *tdkc; - u_int i; + u_int i, j; const char *s; struct options_entry *o; struct options_array_item *a; union options_value *ov; + char copy[16]; + key_code key; if (tty->key_tree != NULL) tty_keys_free(tty); tty->key_tree = NULL; + for (i = 0; i < nitems(tty_default_xterm_keys); i++) { + tdkx = &tty_default_xterm_keys[i]; + for (j = 2; j < nitems(tty_default_xterm_modifiers); j++) { + strlcpy(copy, tdkx->template, sizeof copy); + copy[strcspn(copy, "_")] = '0' + j; + + key = tdkx->key|tty_default_xterm_modifiers[j]; + tty_keys_add(tty, copy, key); + } + } for (i = 0; i < nitems(tty_default_raw_keys); i++) { tdkr = &tty_default_raw_keys[i]; @@ -516,7 +577,6 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, enum utf8_state more; u_int i; wchar_t wc; - int n; log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len, (int)len, buf, expired); @@ -534,13 +594,6 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, return (0); } - /* Is this an an xterm(1) key? */ - n = xterm_keys_find(buf, len, size, key); - if (n == 0) - return (0); - if (n == 1 && !expired) - return (1); - /* Is this valid UTF-8? */ more = utf8_open(&ud, (u_char)*buf); if (more == UTF8_MORE) { diff --git a/xterm-keys.c b/xterm-keys.c deleted file mode 100644 index b10c10db..00000000 --- a/xterm-keys.c +++ /dev/null @@ -1,252 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2009 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 "tmux.h" - -/* - * xterm-style function keys append one of the following values before the last - * character: - * - * 2 Shift - * 3 Alt - * 4 Shift + Alt - * 5 Ctrl - * 6 Shift + Ctrl - * 7 Alt + Ctrl - * 8 Shift + Alt + Ctrl - * - * Rather than parsing them, just match against a table. - * - * There are three forms for F1-F4 (\\033O_P and \\033O1;_P and \\033[1;_P). - * We accept any but always output the latter (it comes first in the table). - */ - -static int xterm_keys_match(const char *, const char *, size_t, size_t *, - key_code *); -static int xterm_keys_modifiers(const char *, size_t, size_t *, - key_code *); - -struct xterm_keys_entry { - key_code key; - const char *template; -}; - -static const struct xterm_keys_entry xterm_keys_table[] = { - { KEYC_F1, "\033[1;_P" }, - { KEYC_F1, "\033O1;_P" }, - { KEYC_F1, "\033O_P" }, - { KEYC_F2, "\033[1;_Q" }, - { KEYC_F2, "\033O1;_Q" }, - { KEYC_F2, "\033O_Q" }, - { KEYC_F3, "\033[1;_R" }, - { KEYC_F3, "\033O1;_R" }, - { KEYC_F3, "\033O_R" }, - { KEYC_F4, "\033[1;_S" }, - { KEYC_F4, "\033O1;_S" }, - { KEYC_F4, "\033O_S" }, - { KEYC_F5, "\033[15;_~" }, - { KEYC_F6, "\033[17;_~" }, - { KEYC_F7, "\033[18;_~" }, - { KEYC_F8, "\033[19;_~" }, - { KEYC_F9, "\033[20;_~" }, - { KEYC_F10, "\033[21;_~" }, - { KEYC_F11, "\033[23;_~" }, - { KEYC_F12, "\033[24;_~" }, - { KEYC_UP, "\033[1;_A" }, - { KEYC_DOWN, "\033[1;_B" }, - { KEYC_RIGHT, "\033[1;_C" }, - { KEYC_LEFT, "\033[1;_D" }, - { KEYC_HOME, "\033[1;_H" }, - { KEYC_END, "\033[1;_F" }, - { KEYC_PPAGE, "\033[5;_~" }, - { KEYC_NPAGE, "\033[6;_~" }, - { KEYC_IC, "\033[2;_~" }, - { KEYC_DC, "\033[3;_~" }, - - { '!', "\033[27;_;33~" }, - { '#', "\033[27;_;35~" }, - { '(', "\033[27;_;40~" }, - { ')', "\033[27;_;41~" }, - { '+', "\033[27;_;43~" }, - { ',', "\033[27;_;44~" }, - { '-', "\033[27;_;45~" }, - { '.', "\033[27;_;46~" }, - { '0', "\033[27;_;48~" }, - { '1', "\033[27;_;49~" }, - { '2', "\033[27;_;50~" }, - { '3', "\033[27;_;51~" }, - { '4', "\033[27;_;52~" }, - { '5', "\033[27;_;53~" }, - { '6', "\033[27;_;54~" }, - { '7', "\033[27;_;55~" }, - { '8', "\033[27;_;56~" }, - { '9', "\033[27;_;57~" }, - { ':', "\033[27;_;58~" }, - { ';', "\033[27;_;59~" }, - { '<', "\033[27;_;60~" }, - { '=', "\033[27;_;61~" }, - { '>', "\033[27;_;62~" }, - { '?', "\033[27;_;63~" }, - { '\'', "\033[27;_;39~" }, - { '\r', "\033[27;_;13~" }, - { '\t', "\033[27;_;9~" }, -}; - -/* - * Match key against buffer, treating _ as a wildcard. Return -1 for no match, - * 0 for match, 1 if the end of the buffer is reached (need more data). - */ -static int -xterm_keys_match(const char *template, const char *buf, size_t len, - size_t *size, key_code *modifiers) -{ - size_t pos; - int retval; - - *modifiers = 0; - - if (len == 0) - return (0); - - pos = 0; - do { - if (*template == '_') { - retval = xterm_keys_modifiers(buf, len, &pos, - modifiers); - if (retval != 0) - return (retval); - continue; - } - if (buf[pos] != *template) - return (-1); - pos++; - } while (*++template != '\0' && pos != len); - - if (*template != '\0') /* partial */ - return (1); - - *size = pos; - return (0); -} - -/* Find modifiers from buffer. */ -static int -xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, - key_code *modifiers) -{ - u_int flags; - - if (len - *pos < 2) - return (1); - - if (buf[*pos] < '0' || buf[*pos] > '9') - return (-1); - flags = buf[(*pos)++] - '0'; - if (buf[*pos] >= '0' && buf[*pos] <= '9') - flags = (flags * 10) + (buf[(*pos)++] - '0'); - flags -= 1; - - *modifiers = 0; - if (flags & 1) - *modifiers |= KEYC_SHIFT; - if (flags & 2) - *modifiers |= KEYC_ESCAPE; - if (flags & 4) - *modifiers |= KEYC_CTRL; - if (flags & 8) - *modifiers |= KEYC_ESCAPE; - return (0); -} - -/* - * Lookup key from a buffer against the table. Returns 0 for found (and the - * key), -1 for not found, 1 for partial match. - */ -int -xterm_keys_find(const char *buf, size_t len, size_t *size, key_code *key) -{ - const struct xterm_keys_entry *entry; - u_int i; - int matched; - key_code modifiers; - - for (i = 0; i < nitems(xterm_keys_table); i++) { - entry = &xterm_keys_table[i]; - - matched = xterm_keys_match(entry->template, buf, len, size, - &modifiers); - if (matched == -1) - continue; - if (matched == 0) - *key = (entry->key|modifiers|KEYC_XTERM); - return (matched); - } - return (-1); -} - -/* Lookup a key number from the table. */ -char * -xterm_keys_lookup(key_code key) -{ - const struct xterm_keys_entry *entry; - u_int i; - key_code modifiers; - char *out; - - modifiers = 1; - if (key & KEYC_SHIFT) - modifiers += 1; - if (key & KEYC_ESCAPE) - modifiers += 2; - if (key & KEYC_CTRL) - modifiers += 4; - - /* - * If the key has no modifiers, return NULL and let it fall through to - * the normal lookup. - */ - if (modifiers == 1) - return (NULL); - - /* - * If this has the escape modifier, but was not originally an xterm - * key, it may be a genuine escape + key. So don't pass it through as - * an xterm key or programs like vi may be confused. - */ - if ((key & (KEYC_ESCAPE|KEYC_XTERM)) == KEYC_ESCAPE) - return (NULL); - - /* Otherwise, find the key in the table. */ - key &= KEYC_MASK_KEY; - for (i = 0; i < nitems(xterm_keys_table); i++) { - entry = &xterm_keys_table[i]; - if (key == entry->key) - break; - } - if (i == nitems(xterm_keys_table)) - return (NULL); - - /* Copy the template and replace the modifier. */ - out = xstrdup(entry->template); - out[strcspn(out, "_")] = '0' + modifiers; - return (out); -} From e95b6441396c96c7a9fbf115c2497198cd063ab0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 11:09:32 +0100 Subject: [PATCH 0360/1006] Recognise extended key sequences on input (both the forms xterm offers). --- tty-keys.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/tty-keys.c b/tty-keys.c index 8b3ee1ea..19ac71ed 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -46,6 +47,8 @@ static struct tty_key *tty_keys_find(struct tty *, const char *, size_t, static int tty_keys_next1(struct tty *, const char *, size_t, key_code *, size_t *, int); static void tty_keys_callback(int, short, void *); +static int tty_keys_extended_key(struct tty *, const char *, size_t, + size_t *, key_code *); static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *, struct mouse_event *); static int tty_keys_clipboard(struct tty *, const char *, size_t, @@ -690,6 +693,16 @@ tty_keys_next(struct tty *tty) goto partial_key; } + /* Is this an extended key press? */ + switch (tty_keys_extended_key(tty, buf, len, &size, &key)) { + case 0: /* yes */ + goto complete_key; + case -1: /* no, or not valid */ + break; + case 1: /* partial */ + goto partial_key; + } + first_key: /* Try to lookup complete key. */ n = tty_keys_next1(tty, buf, len, &key, &size, expired); @@ -827,6 +840,96 @@ tty_keys_callback(__unused int fd, __unused short events, void *data) } } +/* + * Handle extended key input. This has two forms: \033[27;m;k~ and \033[k;mu, + * where k is key as a number and m is a modifier. Returns 0 for success, -1 + * for failure, 1 for partial; + */ +static int +tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, + size_t *size, key_code *key) +{ + struct client *c = tty->client; + size_t end; + u_int number, modifiers; + char tmp[64]; + + *size = 0; + + /* First two bytes are always \033[. */ + if (buf[0] != '\033') + return (-1); + if (len == 1) + return (1); + if (buf[1] != '[') + return (-1); + if (len == 2) + return (1); + + /* + * Look for a terminator. Stop at either '~' or anything that isn't a + * number or ';'. + */ + for (end = 2; end < len && end != sizeof tmp; end++) { + if (buf[end] == '~') + break; + if (!isdigit((u_char)buf[end]) && buf[end] != ';') + break; + } + if (end == len) + return (1); + if (buf[end] != '~' && buf[end] != 'u') + return (-1); + + /* Copy to the buffer. */ + memcpy(tmp, buf + 2, end); + tmp[end] = '\0'; + + /* Try to parse either form of key. */ + if (buf[end] == '~') { + if (sscanf(tmp, "27;%u;%u", &modifiers, &number) != 2) + return (-1); + } else { + if (sscanf(tmp ,"%u;%u", &number, &modifiers) != 2) + return (-1); + } + *size = end + 1; + + /* Store the key and modifiers. */ + *key = number|KEYC_XTERM; + switch (modifiers) { + case 2: + (*key) |= KEYC_SHIFT; + break; + case 3: + (*key) |= KEYC_ESCAPE; + break; + case 4: + (*key) |= (KEYC_SHIFT|KEYC_ESCAPE); + break; + case 5: + (*key) |= KEYC_CTRL; + break; + case 6: + (*key) |= (KEYC_SHIFT|KEYC_CTRL); + break; + case 7: + (*key) |= (KEYC_ESCAPE|KEYC_CTRL); + break; + case 8: + (*key) |= (KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL); + break; + default: + *key = KEYC_NONE; + break; + } + if (log_get_level() != 0) { + log_debug("%s: extended key %.*s is %llx (%s)", c->name, + (int)*size, buf, *key, key_string_lookup_key(*key)); + } + return (0); +} + /* * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial * (probably a mouse sequence but need more data). From 2cf967ee6786776675634459eefdff3dd7d7b778 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 11:20:12 +0100 Subject: [PATCH 0361/1006] Always set xterm flag. --- cmd-send-keys.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index afaf0a81..27ca8a63 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -71,9 +71,7 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode->key_table == NULL) { - if (options_get_number(wp->window->options, "xterm-keys")) - key |= KEYC_XTERM; - if (window_pane_key(wp, tc, s, wl, key, NULL) != 0) + if (window_pane_key(wp, tc, s, wl, key|KEYC_XTERM, NULL) != 0) return (NULL); return (item); } From 031d4864a94cd7c042fe96a2d236bccde83655e7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 11:24:30 +0100 Subject: [PATCH 0362/1006] Rename KEYC_ESCAPE to KEYC_META. --- input-keys.c | 16 +++---- key-string.c | 4 +- mode-tree.c | 6 +-- server-client.c | 2 +- status.c | 8 ++-- tmux.h | 2 +- tty-keys.c | 110 ++++++++++++++++++++++++------------------------ 7 files changed, 74 insertions(+), 74 deletions(-) diff --git a/input-keys.c b/input-keys.c index 30141243..7f2bc7d8 100644 --- a/input-keys.c +++ b/input-keys.c @@ -335,12 +335,12 @@ static const key_code input_key_modifiers[] = { 0, 0, KEYC_SHIFT|KEYC_XTERM, - KEYC_ESCAPE|KEYC_XTERM, - KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM, + KEYC_META|KEYC_XTERM, + KEYC_SHIFT|KEYC_META|KEYC_XTERM, KEYC_CTRL|KEYC_XTERM, KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM, - KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM, - KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM + KEYC_META|KEYC_CTRL|KEYC_XTERM, + KEYC_SHIFT|KEYC_META|KEYC_CTRL|KEYC_XTERM }; /* Input key comparison function. */ @@ -448,9 +448,9 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) * If this is a normal 7-bit key, just send it, with a leading escape * if necessary. If it is a UTF-8 key, split it and send it. */ - justkey = (key & ~(KEYC_XTERM|KEYC_ESCAPE)); + justkey = (key & ~(KEYC_XTERM|KEYC_META)); if (justkey <= 0x7f) { - if (key & KEYC_ESCAPE) + if (key & KEYC_META) bufferevent_write(bev, "\033", 1); ud.data[0] = justkey; bufferevent_write(bev, &ud.data[0], 1); @@ -459,7 +459,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) return (-1); - if (key & KEYC_ESCAPE) + if (key & KEYC_META) bufferevent_write(bev, "\033", 1); bufferevent_write(bev, ud.data, ud.size); return (0); @@ -482,7 +482,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) log_debug("found key 0x%llx: \"%s\"", key, ike->data); /* Prefix a \033 for escape. */ - if (key & KEYC_ESCAPE) + if (key & KEYC_META) bufferevent_write(bev, "\033", 1); bufferevent_write(bev, ike->data, datalen); return (0); diff --git a/key-string.c b/key-string.c index 2a0602b2..bd98ca69 100644 --- a/key-string.c +++ b/key-string.c @@ -143,7 +143,7 @@ key_string_get_modifiers(const char **string) break; case 'M': case 'm': - modifiers |= KEYC_ESCAPE; + modifiers |= KEYC_META; break; case 'S': case 's': @@ -265,7 +265,7 @@ key_string_lookup_key(key_code key) /* Fill in the modifiers. */ if (key & KEYC_CTRL) strlcat(out, "C-", sizeof out); - if (key & KEYC_ESCAPE) + if (key & KEYC_META) strlcat(out, "M-", sizeof out); if (key & KEYC_SHIFT) strlcat(out, "S-", sizeof out); diff --git a/mode-tree.c b/mode-tree.c index a523478e..03738d56 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -987,7 +987,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, choice = -1; if (*key >= '0' && *key <= '9') choice = (*key) - '0'; - else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) { + else if (((*key) & KEYC_MASK_MOD) == KEYC_META) { tmp = (*key) & KEYC_MASK_KEY; if (tmp >= 'a' && tmp <= 'z') choice = 10 + (tmp - 'a'); @@ -1111,12 +1111,12 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_build(mtd); } break; - case '-'|KEYC_ESCAPE: + case '-'|KEYC_META: TAILQ_FOREACH(mti, &mtd->children, entry) mti->expanded = 0; mode_tree_build(mtd); break; - case '+'|KEYC_ESCAPE: + case '+'|KEYC_META: TAILQ_FOREACH(mti, &mtd->children, entry) mti->expanded = 1; mode_tree_build(mtd); diff --git a/server-client.c b/server-client.c index c7bec010..9631a7de 100644 --- a/server-client.c +++ b/server-client.c @@ -1033,7 +1033,7 @@ have_event: out: /* Apply modifiers if any. */ if (b & MOUSE_MASK_META) - key |= KEYC_ESCAPE; + key |= KEYC_META; if (b & MOUSE_MASK_CTRL) key |= KEYC_CTRL; if (b & MOUSE_MASK_SHIFT) diff --git a/status.c b/status.c index 56af02f5..9bee2e7b 100644 --- a/status.c +++ b/status.c @@ -827,7 +827,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) return (1); case 'b': case 'B': - *new_key = 'b'|KEYC_ESCAPE; + *new_key = 'b'|KEYC_META; return (1); case 'd': *new_key = '\025'; @@ -836,7 +836,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) case 'E': case 'w': case 'W': - *new_key = 'f'|KEYC_ESCAPE; + *new_key = 'f'|KEYC_META; return (1); case 'p': *new_key = '\031'; /* C-y */ @@ -1158,7 +1158,7 @@ process_key: c->prompt_index = idx; goto changed; - case 'f'|KEYC_ESCAPE: + case 'f'|KEYC_META: case KEYC_RIGHT|KEYC_CTRL: ws = options_get_string(oo, "word-separators"); @@ -1182,7 +1182,7 @@ process_key: c->prompt_index--; goto changed; - case 'b'|KEYC_ESCAPE: + case 'b'|KEYC_META: case KEYC_LEFT|KEYC_CTRL: ws = options_get_string(oo, "word-separators"); diff --git a/tmux.h b/tmux.h index 22baa560..9c27944b 100644 --- a/tmux.h +++ b/tmux.h @@ -120,7 +120,7 @@ struct winlink; #define KEYC_USER 0x0002000000000ULL /* Key modifier bits. */ -#define KEYC_ESCAPE 0x0100000000000ULL +#define KEYC_META 0x0100000000000ULL #define KEYC_CTRL 0x0200000000000ULL #define KEYC_SHIFT 0x0400000000000ULL #define KEYC_XTERM 0x0800000000000ULL diff --git a/tty-keys.c b/tty-keys.c index 19ac71ed..1c424c5d 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -226,12 +226,12 @@ static const key_code tty_default_xterm_modifiers[] = { 0, 0, KEYC_SHIFT|KEYC_XTERM, - KEYC_ESCAPE|KEYC_XTERM, - KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM, + KEYC_META|KEYC_XTERM, + KEYC_SHIFT|KEYC_META|KEYC_XTERM, KEYC_CTRL|KEYC_XTERM, KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM, - KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM, - KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM + KEYC_META|KEYC_CTRL|KEYC_XTERM, + KEYC_SHIFT|KEYC_META|KEYC_CTRL|KEYC_XTERM }; /* @@ -297,22 +297,22 @@ static const struct tty_default_key_code tty_default_code_keys[] = { { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF49, KEYC_F1|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF50, KEYC_F2|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF51, KEYC_F3|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF52, KEYC_F4|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF53, KEYC_F5|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF54, KEYC_F6|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF55, KEYC_F7|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF56, KEYC_F8|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF57, KEYC_F9|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF58, KEYC_F10|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF59, KEYC_F11|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF60, KEYC_F12|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KF49, KEYC_F1|KEYC_META|KEYC_XTERM }, + { TTYC_KF50, KEYC_F2|KEYC_META|KEYC_XTERM }, + { TTYC_KF51, KEYC_F3|KEYC_META|KEYC_XTERM }, + { TTYC_KF52, KEYC_F4|KEYC_META|KEYC_XTERM }, + { TTYC_KF53, KEYC_F5|KEYC_META|KEYC_XTERM }, + { TTYC_KF54, KEYC_F6|KEYC_META|KEYC_XTERM }, + { TTYC_KF55, KEYC_F7|KEYC_META|KEYC_XTERM }, + { TTYC_KF56, KEYC_F8|KEYC_META|KEYC_XTERM }, + { TTYC_KF57, KEYC_F9|KEYC_META|KEYC_XTERM }, + { TTYC_KF58, KEYC_F10|KEYC_META|KEYC_XTERM }, + { TTYC_KF59, KEYC_F11|KEYC_META|KEYC_XTERM }, + { TTYC_KF60, KEYC_F12|KEYC_META|KEYC_XTERM }, - { TTYC_KF61, KEYC_F1|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF62, KEYC_F2|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF63, KEYC_F3|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, + { TTYC_KF61, KEYC_F1|KEYC_META|KEYC_SHIFT|KEYC_XTERM }, + { TTYC_KF62, KEYC_F2|KEYC_META|KEYC_SHIFT|KEYC_XTERM }, + { TTYC_KF63, KEYC_F3|KEYC_META|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KICH1, KEYC_IC }, { TTYC_KDCH1, KEYC_DC }, @@ -330,67 +330,67 @@ static const struct tty_default_key_code tty_default_code_keys[] = { /* Key and modifier capabilities. */ { TTYC_KDC2, KEYC_DC|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDC3, KEYC_DC|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KDC3, KEYC_DC|KEYC_META|KEYC_XTERM }, + { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, { TTYC_KDC5, KEYC_DC|KEYC_CTRL|KEYC_XTERM }, { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDC7, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KDC7, KEYC_DC|KEYC_META|KEYC_CTRL|KEYC_XTERM }, { TTYC_KIND, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDN3, KEYC_DOWN|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KDN3, KEYC_DOWN|KEYC_META|KEYC_XTERM }, + { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL|KEYC_XTERM }, { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDN7, KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KDN7, KEYC_DOWN|KEYC_META|KEYC_CTRL|KEYC_XTERM }, { TTYC_KEND2, KEYC_END|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KEND3, KEYC_END|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KEND3, KEYC_END|KEYC_META|KEYC_XTERM }, + { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, { TTYC_KEND5, KEYC_END|KEYC_CTRL|KEYC_XTERM }, { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KEND7, KEYC_END|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KEND7, KEYC_END|KEYC_META|KEYC_CTRL|KEYC_XTERM }, { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KHOM3, KEYC_HOME|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KHOM3, KEYC_HOME|KEYC_META|KEYC_XTERM }, + { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL|KEYC_XTERM }, { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KHOM7, KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KHOM7, KEYC_HOME|KEYC_META|KEYC_CTRL|KEYC_XTERM }, { TTYC_KIC2, KEYC_IC|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KIC3, KEYC_IC|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KIC3, KEYC_IC|KEYC_META|KEYC_XTERM }, + { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, { TTYC_KIC5, KEYC_IC|KEYC_CTRL|KEYC_XTERM }, { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIC7, KEYC_IC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KIC7, KEYC_IC|KEYC_META|KEYC_CTRL|KEYC_XTERM }, { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KLFT3, KEYC_LEFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KLFT3, KEYC_LEFT|KEYC_META|KEYC_XTERM }, + { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KLFT7, KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KLFT7, KEYC_LEFT|KEYC_META|KEYC_CTRL|KEYC_XTERM }, { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KNXT3, KEYC_NPAGE|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KNXT3, KEYC_NPAGE|KEYC_META|KEYC_XTERM }, + { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KNXT7, KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KNXT7, KEYC_NPAGE|KEYC_META|KEYC_CTRL|KEYC_XTERM }, { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KPRV3, KEYC_PPAGE|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KPRV3, KEYC_PPAGE|KEYC_META|KEYC_XTERM }, + { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KPRV7, KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KPRV7, KEYC_PPAGE|KEYC_META|KEYC_CTRL|KEYC_XTERM }, { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KRIT3, KEYC_RIGHT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KRIT3, KEYC_RIGHT|KEYC_META|KEYC_XTERM }, + { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRIT7, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KRIT7, KEYC_RIGHT|KEYC_META|KEYC_CTRL|KEYC_XTERM }, { TTYC_KRI, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KUP2, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KUP3, KEYC_UP|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KUP3, KEYC_UP|KEYC_META|KEYC_XTERM }, + { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, { TTYC_KUP5, KEYC_UP|KEYC_CTRL|KEYC_XTERM }, { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KUP7, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KUP7, KEYC_UP|KEYC_META|KEYC_CTRL|KEYC_XTERM }, }; /* Add key to tree. */ @@ -731,7 +731,7 @@ first_key: size = 1; goto complete_key; } - key |= KEYC_ESCAPE; + key |= KEYC_META; size++; goto complete_key; } @@ -744,7 +744,7 @@ first_key: * escape). So pass it through even if the timer has not expired. */ if (*buf == '\033' && len >= 2) { - key = (u_char)buf[1] | KEYC_ESCAPE; + key = (u_char)buf[1] | KEYC_META; size = 2; } else { key = (u_char)buf[0]; @@ -902,10 +902,10 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, (*key) |= KEYC_SHIFT; break; case 3: - (*key) |= KEYC_ESCAPE; + (*key) |= KEYC_META; break; case 4: - (*key) |= (KEYC_SHIFT|KEYC_ESCAPE); + (*key) |= (KEYC_SHIFT|KEYC_META); break; case 5: (*key) |= KEYC_CTRL; @@ -914,10 +914,10 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, (*key) |= (KEYC_SHIFT|KEYC_CTRL); break; case 7: - (*key) |= (KEYC_ESCAPE|KEYC_CTRL); + (*key) |= (KEYC_META|KEYC_CTRL); break; case 8: - (*key) |= (KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL); + (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_CTRL); break; default: *key = KEYC_NONE; From 340fd691cbd6f575fc1169269dfa675f30ce71d3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 12:16:41 +0100 Subject: [PATCH 0363/1006] Separate key flags and modifiers, log key flags, make the "xterm" flag more explicit and fix M- keys with a leading escape. --- cmd-list-keys.c | 10 +- cmd-queue.c | 2 +- cmd-send-keys.c | 4 +- input-keys.c | 91 +++++++++------- key-bindings.c | 4 +- key-string.c | 47 ++++++--- menu.c | 2 +- mode-tree.c | 2 +- options.c | 4 +- popup.c | 2 +- server-client.c | 4 +- status.c | 4 +- tmux.h | 36 ++++--- tty-keys.c | 256 ++++++++++++++++++++++----------------------- window-customize.c | 8 +- window.c | 6 +- 16 files changed, 260 insertions(+), 222 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 60ef73af..51c90dfe 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -73,7 +73,7 @@ cmd_list_keys_get_width(const char *tablename, key_code only) bd = key_bindings_next(table, bd); continue; } - width = utf8_cstrwidth(key_string_lookup_key(bd->key)); + width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0)); if (width > keywidth) keywidth = width; @@ -106,7 +106,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, continue; } found = 1; - key = key_string_lookup_key(bd->key); + key = key_string_lookup_key(bd->key, 0); if (bd->note == NULL || *bd->note == '\0') note = cmd_list_print(bd->cmdlist, 1); @@ -135,7 +135,7 @@ cmd_list_keys_get_prefix(struct args *args, key_code *prefix) *prefix = options_get_number(global_s_options, "prefix"); if (!args_has(args, 'P')) { if (*prefix != KEYC_NONE) - xasprintf(&s, "%s ", key_string_lookup_key(*prefix)); + xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0)); else s = xstrdup(""); } else @@ -221,7 +221,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) bd = key_bindings_next(table, bd); continue; } - key = args_escape(key_string_lookup_key(bd->key)); + key = args_escape(key_string_lookup_key(bd->key, 0)); if (bd->flags & KEY_BINDING_REPEAT) repeat = 1; @@ -255,7 +255,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) continue; } found = 1; - key = args_escape(key_string_lookup_key(bd->key)); + key = args_escape(key_string_lookup_key(bd->key, 0)); if (!repeat) r = ""; diff --git a/cmd-queue.c b/cmd-queue.c index 5620fdad..b0c70428 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -547,7 +547,7 @@ cmdq_add_message(struct cmdq_item *item) if (c != NULL) { name = c->name; if (c->session != NULL && state->event.key != KEYC_NONE) { - key = key_string_lookup_key(state->event.key); + key = key_string_lookup_key(state->event.key, 0); server_add_message("%s key %s: %s", name, key, tmp); } else server_add_message("%s command: %s", name, tmp); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 27ca8a63..a9ecc807 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -71,13 +71,13 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode->key_table == NULL) { - if (window_pane_key(wp, tc, s, wl, key|KEYC_XTERM, NULL) != 0) + if (window_pane_key(wp, tc, s, wl, key, NULL) != 0) return (NULL); return (item); } table = key_bindings_get_table(wme->mode->key_table(wme), 1); - bd = key_bindings_get(table, key & ~KEYC_XTERM); + bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); if (bd != NULL) { table->references++; after = key_bindings_dispatch(bd, after, tc, NULL, target); diff --git a/input-keys.c b/input-keys.c index 7f2bc7d8..ef6caa31 100644 --- a/input-keys.c +++ b/input-keys.c @@ -265,82 +265,82 @@ static struct input_key_entry input_key_defaults[] = { }, /* Keys with an embedded modifier. */ - { .key = KEYC_F1|KEYC_XTERM, + { .key = KEYC_F1|KEYC_BUILD_MODIFIERS, .data = "\033[1;_P" }, - { .key = KEYC_F2|KEYC_XTERM, + { .key = KEYC_F2|KEYC_BUILD_MODIFIERS, .data = "\033[1;_Q" }, - { .key = KEYC_F3|KEYC_XTERM, + { .key = KEYC_F3|KEYC_BUILD_MODIFIERS, .data = "\033[1;_R" }, - { .key = KEYC_F4|KEYC_XTERM, + { .key = KEYC_F4|KEYC_BUILD_MODIFIERS, .data = "\033[1;_S" }, - { .key = KEYC_F5|KEYC_XTERM, + { .key = KEYC_F5|KEYC_BUILD_MODIFIERS, .data = "\033[15;_~" }, - { .key = KEYC_F6|KEYC_XTERM, + { .key = KEYC_F6|KEYC_BUILD_MODIFIERS, .data = "\033[17;_~" }, - { .key = KEYC_F7|KEYC_XTERM, + { .key = KEYC_F7|KEYC_BUILD_MODIFIERS, .data = "\033[18;_~" }, - { .key = KEYC_F8|KEYC_XTERM, + { .key = KEYC_F8|KEYC_BUILD_MODIFIERS, .data = "\033[19;_~" }, - { .key = KEYC_F9|KEYC_XTERM, + { .key = KEYC_F9|KEYC_BUILD_MODIFIERS, .data = "\033[20;_~" }, - { .key = KEYC_F10|KEYC_XTERM, + { .key = KEYC_F10|KEYC_BUILD_MODIFIERS, .data = "\033[21;_~" }, - { .key = KEYC_F11|KEYC_XTERM, + { .key = KEYC_F11|KEYC_BUILD_MODIFIERS, .data = "\033[23;_~" }, - { .key = KEYC_F12|KEYC_XTERM, + { .key = KEYC_F12|KEYC_BUILD_MODIFIERS, .data = "\033[24;_~" }, - { .key = KEYC_UP|KEYC_XTERM, + { .key = KEYC_UP|KEYC_BUILD_MODIFIERS, .data = "\033[1;_A" }, - { .key = KEYC_DOWN|KEYC_XTERM, + { .key = KEYC_DOWN|KEYC_BUILD_MODIFIERS, .data = "\033[1;_B" }, - { .key = KEYC_RIGHT|KEYC_XTERM, + { .key = KEYC_RIGHT|KEYC_BUILD_MODIFIERS, .data = "\033[1;_C" }, - { .key = KEYC_LEFT|KEYC_XTERM, + { .key = KEYC_LEFT|KEYC_BUILD_MODIFIERS, .data = "\033[1;_D" }, - { .key = KEYC_HOME|KEYC_XTERM, + { .key = KEYC_HOME|KEYC_BUILD_MODIFIERS, .data = "\033[1;_H" }, - { .key = KEYC_END|KEYC_XTERM, + { .key = KEYC_END|KEYC_BUILD_MODIFIERS, .data = "\033[1;_F" }, - { .key = KEYC_PPAGE|KEYC_XTERM, + { .key = KEYC_PPAGE|KEYC_BUILD_MODIFIERS, .data = "\033[5;_~" }, - { .key = KEYC_NPAGE|KEYC_XTERM, + { .key = KEYC_NPAGE|KEYC_BUILD_MODIFIERS, .data = "\033[6;_~" }, - { .key = KEYC_IC|KEYC_XTERM, + { .key = KEYC_IC|KEYC_BUILD_MODIFIERS, .data = "\033[2;_~" }, - { .key = KEYC_DC|KEYC_XTERM, + { .key = KEYC_DC|KEYC_BUILD_MODIFIERS, .data = "\033[3;_~" } }; static const key_code input_key_modifiers[] = { 0, 0, - KEYC_SHIFT|KEYC_XTERM, - KEYC_META|KEYC_XTERM, - KEYC_SHIFT|KEYC_META|KEYC_XTERM, - KEYC_CTRL|KEYC_XTERM, - KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM, - KEYC_META|KEYC_CTRL|KEYC_XTERM, - KEYC_SHIFT|KEYC_META|KEYC_CTRL|KEYC_XTERM + KEYC_SHIFT, + KEYC_META|KEYC_IMPLIED_META, + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META, + KEYC_CTRL, + KEYC_SHIFT|KEYC_CTRL, + KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, + KEYC_SHIFT|KEYC_META|KEYC_CTRL }; /* Input key comparison function. */ @@ -354,6 +354,15 @@ input_key_cmp(struct input_key_entry *ike1, struct input_key_entry *ike2) return (0); } +/* Look for key in tree. */ +static struct input_key_entry * +input_key_get (key_code key) +{ + struct input_key_entry entry = { .key = key }; + + return (RB_FIND(input_key_tree, &input_key_tree, &entry)); +} + /* Split a character into two UTF-8 bytes. */ static size_t input_key_split2(u_int c, u_char *dst) @@ -374,20 +383,22 @@ input_key_build(void) struct input_key_entry *ike, *new; u_int i, j; char *data; + key_code key; for (i = 0; i < nitems(input_key_defaults); i++) { ike = &input_key_defaults[i]; - if (~ike->key & KEYC_XTERM) { + if (~ike->key & KEYC_BUILD_MODIFIERS) { RB_INSERT(input_key_tree, &input_key_tree, ike); continue; } for (j = 2; j < nitems(input_key_modifiers); j++) { + key = (ike->key & ~KEYC_BUILD_MODIFIERS); data = xstrdup(ike->data); data[strcspn(data, "_")] = '0' + j; new = xcalloc(1, sizeof *new); - new->key = ike->key|input_key_modifiers[j]; + new->key = key|input_key_modifiers[j]; new->data = data; RB_INSERT(input_key_tree, &input_key_tree, new); } @@ -395,7 +406,7 @@ input_key_build(void) RB_FOREACH(ike, input_key_tree, &input_key_tree) { log_debug("%s: 0x%llx (%s) is %s", __func__, ike->key, - key_string_lookup_key(ike->key), ike->data); + key_string_lookup_key(ike->key, 1), ike->data); } } @@ -405,7 +416,7 @@ input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) { if (log_get_level() != 0) { log_debug("writing key 0x%llx (%s) to %%%u", key, - key_string_lookup_key(key), wp->id); + key_string_lookup_key(key, 1), wp->id); } if (KEYC_IS_MOUSE(key)) { @@ -420,7 +431,7 @@ input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) int input_key(struct screen *s, struct bufferevent *bev, key_code key) { - struct input_key_entry *ike, entry; + struct input_key_entry *ike; size_t datalen; key_code justkey, newkey; struct utf8_data ud; @@ -441,14 +452,14 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) newkey = options_get_number(global_options, "backspace"); if (newkey >= 0x7f) newkey = '\177'; - key = newkey|(key & KEYC_MASK_MOD); + key = newkey|(key & (KEYC_MASK_MODIFIERS|KEYC_MASK_FLAGS)); } /* * If this is a normal 7-bit key, just send it, with a leading escape * if necessary. If it is a UTF-8 key, split it and send it. */ - justkey = (key & ~(KEYC_XTERM|KEYC_META)); + justkey = (key & ~KEYC_META); if (justkey <= 0x7f) { if (key & KEYC_META) bufferevent_write(bev, "\033", 1); @@ -473,8 +484,10 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) key &= ~KEYC_KEYPAD; if (~s->mode & MODE_KCURSOR) key &= ~KEYC_CURSOR; - entry.key = key; - if ((ike = RB_FIND(input_key_tree, &input_key_tree, &entry)) == NULL) { + ike = input_key_get(key); + if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META)) + ike = input_key_get(key & ~KEYC_META); + if (ike == NULL) { log_debug("key 0x%llx missing", key); return (-1); } @@ -482,7 +495,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) log_debug("found key 0x%llx: \"%s\"", key, ike->data); /* Prefix a \033 for escape. */ - if (key & KEYC_META) + if (key & KEYC_META && (~key & KEYC_IMPLIED_META)) bufferevent_write(bev, "\033", 1); bufferevent_write(bev, ike->data, datalen); return (0); diff --git a/key-bindings.c b/key-bindings.c index 05089bab..59cfbb0d 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -190,7 +190,7 @@ 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_XTERM); + bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); if (bd != NULL) { RB_REMOVE(key_bindings, &table->key_bindings, bd); key_bindings_free(bd); @@ -217,7 +217,7 @@ key_bindings_remove(const char *name, key_code key) if (table == NULL) return; - bd = key_bindings_get(table, key & ~KEYC_XTERM); + bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); if (bd == NULL) return; diff --git a/key-string.c b/key-string.c index bd98ca69..65f1afe5 100644 --- a/key-string.c +++ b/key-string.c @@ -212,7 +212,7 @@ key_string_lookup_string(const char *string) return (KEYC_UNKNOWN); if (utf8_combine(&ud, &wc) != UTF8_DONE) return (KEYC_UNKNOWN); - return (wc | modifiers); + return (wc|modifiers); } /* Otherwise look the key up in the table. */ @@ -236,14 +236,15 @@ key_string_lookup_string(const char *string) modifiers &= ~KEYC_CTRL; } - return (key | modifiers); + return (key|modifiers); } /* Convert a key code into string format, with prefix if necessary. */ const char * -key_string_lookup_key(key_code key) +key_string_lookup_key(key_code key, int with_flags) { - static char out[32]; + key_code saved = key; + static char out[64]; char tmp[8]; const char *s; u_int i; @@ -255,12 +256,12 @@ key_string_lookup_key(key_code key) /* Literal keys are themselves. */ if (key & KEYC_LITERAL) { snprintf(out, sizeof out, "%c", (int)(key & 0xff)); - return (out); + goto out; } /* Display C-@ as C-Space. */ - if ((key & KEYC_MASK_KEY) == 0) - key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); + if ((key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)) == 0) + key = ' '|KEYC_CTRL; /* Fill in the modifiers. */ if (key & KEYC_CTRL) @@ -272,8 +273,10 @@ key_string_lookup_key(key_code key) key &= KEYC_MASK_KEY; /* Handle no key. */ - if (key == KEYC_NONE) - return ("None"); + if (key == KEYC_NONE) { + s = "None"; + goto append; + } /* Handle special keys. */ if (key == KEYC_UNKNOWN) { @@ -331,7 +334,7 @@ key_string_lookup_key(key_code key) if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) { snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER)); strlcat(out, tmp, sizeof out); - return (out); + goto out; } /* Try the key against the string table. */ @@ -341,7 +344,7 @@ key_string_lookup_key(key_code key) } if (i != nitems(key_string_table)) { strlcat(out, key_string_table[i].string, sizeof out); - return (out); + goto out; } /* Is this a UTF-8 key? */ @@ -350,14 +353,14 @@ key_string_lookup_key(key_code key) off = strlen(out); memcpy(out + off, ud.data, ud.size); out[off + ud.size] = '\0'; - return (out); + goto out; } } /* Invalid keys are errors. */ if (key > 255) { snprintf(out, sizeof out, "Invalid#%llx", key); - return (out); + goto out; } /* Check for standard or control key. */ @@ -375,9 +378,25 @@ key_string_lookup_key(key_code key) xsnprintf(tmp, sizeof tmp, "\\%llo", key); strlcat(out, tmp, sizeof out); - return (out); + goto out; append: strlcat(out, s, sizeof out); + +out: + if (with_flags && (saved & KEYC_MASK_FLAGS) != 0) { + strlcat(out, "[", sizeof out); + if (saved & KEYC_LITERAL) + strlcat(out, "L", sizeof out); + if (saved & KEYC_KEYPAD) + strlcat(out, "K", sizeof out); + if (saved & KEYC_CURSOR) + strlcat(out, "C", sizeof out); + if (saved & KEYC_IMPLIED_META) + strlcat(out, "I", sizeof out); + if (saved & KEYC_BUILD_MODIFIERS) + strlcat(out, "B", sizeof out); + strlcat(out, "]", sizeof out); + } return (out); } diff --git a/menu.c b/menu.c index 62010a58..1969cef4 100644 --- a/menu.c +++ b/menu.c @@ -81,7 +81,7 @@ menu_add_item(struct menu *menu, const struct menu_item *item, return; } if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) { - key = key_string_lookup_key(item->key); + key = key_string_lookup_key(item->key, 0); xasprintf(&name, "%s#[default] #[align=right](%s)", s, key); } else xasprintf(&name, "%s", s); diff --git a/mode-tree.c b/mode-tree.c index 03738d56..993070ec 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -987,7 +987,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, choice = -1; if (*key >= '0' && *key <= '9') choice = (*key) - '0'; - else if (((*key) & KEYC_MASK_MOD) == KEYC_META) { + else if (((*key) & KEYC_MASK_MODIFIERS) == KEYC_META) { tmp = (*key) & KEYC_MASK_KEY; if (tmp >= 'a' && tmp <= 'z') choice = 10 + (tmp - 'a'); diff --git a/options.c b/options.c index 22e1be7e..5685cb51 100644 --- a/options.c +++ b/options.c @@ -129,7 +129,7 @@ options_value_to_string(struct options_entry *o, union options_value *ov, xasprintf(&s, "%lld", ov->number); break; case OPTIONS_TABLE_KEY: - s = xstrdup(key_string_lookup_key(ov->number)); + s = xstrdup(key_string_lookup_key(ov->number, 0)); break; case OPTIONS_TABLE_COLOUR: s = xstrdup(colour_tostring(ov->number)); @@ -282,7 +282,7 @@ options_default_to_string(const struct options_table_entry *oe) xasprintf(&s, "%lld", oe->default_num); break; case OPTIONS_TABLE_KEY: - s = xstrdup(key_string_lookup_key(oe->default_num)); + s = xstrdup(key_string_lookup_key(oe->default_num, 0)); break; case OPTIONS_TABLE_COLOUR: s = xstrdup(colour_tostring(oe->default_num)); diff --git a/popup.c b/popup.c index 8d88c99f..6f2ab101 100644 --- a/popup.c +++ b/popup.c @@ -340,7 +340,7 @@ popup_key_cb(struct client *c, struct key_event *event) format_defaults(ft, c, fs->s, fs->wl, fs->wp); else format_defaults(ft, c, NULL, NULL, NULL); - format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key)); + format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key, 0)); if (KEYC_IS_MOUSE(event->key)) { format_add(ft, "popup_mouse", "1"); format_add(ft, "popup_mouse_x", "%u", m->x - pd->px); diff --git a/server-client.c b/server-client.c index 9631a7de..87a4f533 100644 --- a/server-client.c +++ b/server-client.c @@ -1040,7 +1040,7 @@ out: key |= KEYC_SHIFT; if (log_get_level() != 0) - log_debug("mouse key is %s", key_string_lookup_key (key)); + log_debug("mouse key is %s", key_string_lookup_key (key, 1)); return (key); } @@ -1172,7 +1172,7 @@ table_changed: * The prefix always takes precedence and forces a switch to the prefix * table, unless we are already there. */ - key0 = (key & ~KEYC_XTERM); + key0 = (key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)); if ((key0 == (key_code)options_get_number(s->options, "prefix") || key0 == (key_code)options_get_number(s->options, "prefix2")) && strcmp(table->name, "prefix") != 0) { diff --git a/status.c b/status.c index 9bee2e7b..93ac70df 100644 --- a/status.c +++ b/status.c @@ -1023,7 +1023,7 @@ status_prompt_key(struct client *c, key_code key) int keys; if (c->prompt_flags & PROMPT_KEY) { - keystring = key_string_lookup_key(key); + keystring = key_string_lookup_key(key, 0); c->prompt_inputcb(c, c->prompt_data, keystring, 1); status_prompt_clear(c); return (0); @@ -1039,7 +1039,7 @@ status_prompt_key(struct client *c, key_code key) free(s); return (1); } - key &= ~KEYC_XTERM; + key &= ~KEYC_MASK_FLAGS; keys = options_get_number(c->session->options, "status-keys"); if (keys == MODEKEY_VI) { diff --git a/tmux.h b/tmux.h index 9c27944b..07d10bcc 100644 --- a/tmux.h +++ b/tmux.h @@ -114,27 +114,31 @@ struct winlink; #define VISUAL_BOTH 2 /* Special key codes. */ -#define KEYC_NONE 0x00ff000000000ULL -#define KEYC_UNKNOWN 0x00fe000000000ULL -#define KEYC_BASE 0x0001000000000ULL -#define KEYC_USER 0x0002000000000ULL +#define KEYC_NONE 0x00ff000000000ULL +#define KEYC_UNKNOWN 0x00fe000000000ULL +#define KEYC_BASE 0x0001000000000ULL +#define KEYC_USER 0x0002000000000ULL /* Key modifier bits. */ -#define KEYC_META 0x0100000000000ULL -#define KEYC_CTRL 0x0200000000000ULL -#define KEYC_SHIFT 0x0400000000000ULL -#define KEYC_XTERM 0x0800000000000ULL -#define KEYC_LITERAL 0x1000000000000ULL -#define KEYC_KEYPAD 0x2000000000000ULL -#define KEYC_CURSOR 0x4000000000000ULL +#define KEYC_META 0x00100000000000ULL +#define KEYC_CTRL 0x00200000000000ULL +#define KEYC_SHIFT 0x00400000000000ULL + +/* Key flag bits. */ +#define KEYC_LITERAL 0x01000000000000ULL +#define KEYC_KEYPAD 0x02000000000000ULL +#define KEYC_CURSOR 0x04000000000000ULL +#define KEYC_IMPLIED_META 0x08000000000000ULL +#define KEYC_BUILD_MODIFIERS 0x10000000000000ULL + +/* Masks for key bits. */ +#define KEYC_MASK_MODIFIERS 0x00f00000000000ULL +#define KEYC_MASK_FLAGS 0xff000000000000ULL +#define KEYC_MASK_KEY 0x000fffffffffffULL /* Available user keys. */ #define KEYC_NUSER 1000 -/* Mask to obtain key w/o modifiers. */ -#define KEYC_MASK_MOD 0xff00000000000ULL -#define KEYC_MASK_KEY 0x00fffffffffffULL - /* Is this a mouse key? */ #define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) @@ -2286,7 +2290,7 @@ struct cmdq_item *key_bindings_dispatch(struct key_binding *, /* key-string.c */ key_code key_string_lookup_string(const char *); -const char *key_string_lookup_key(key_code); +const char *key_string_lookup_key(key_code, int); /* alerts.c */ void alerts_reset_all(void); diff --git a/tty-keys.c b/tty-keys.c index 1c424c5d..99200689 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -225,13 +225,13 @@ static const struct tty_default_key_xterm tty_default_xterm_keys[] = { static const key_code tty_default_xterm_modifiers[] = { 0, 0, - KEYC_SHIFT|KEYC_XTERM, - KEYC_META|KEYC_XTERM, - KEYC_SHIFT|KEYC_META|KEYC_XTERM, - KEYC_CTRL|KEYC_XTERM, - KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM, - KEYC_META|KEYC_CTRL|KEYC_XTERM, - KEYC_SHIFT|KEYC_META|KEYC_CTRL|KEYC_XTERM + KEYC_SHIFT, + KEYC_META|KEYC_IMPLIED_META, + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META, + KEYC_CTRL, + KEYC_SHIFT|KEYC_CTRL, + KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, + KEYC_SHIFT|KEYC_META|KEYC_CTRL }; /* @@ -258,61 +258,61 @@ static const struct tty_default_key_code tty_default_code_keys[] = { { TTYC_KF11, KEYC_F11 }, { TTYC_KF12, KEYC_F12 }, - { TTYC_KF13, KEYC_F1|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF14, KEYC_F2|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF15, KEYC_F3|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF16, KEYC_F4|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF17, KEYC_F5|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF18, KEYC_F6|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF19, KEYC_F7|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF20, KEYC_F8|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF21, KEYC_F9|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF22, KEYC_F10|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF23, KEYC_F11|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF24, KEYC_F12|KEYC_SHIFT|KEYC_XTERM }, + { TTYC_KF13, KEYC_F1|KEYC_SHIFT }, + { TTYC_KF14, KEYC_F2|KEYC_SHIFT }, + { TTYC_KF15, KEYC_F3|KEYC_SHIFT }, + { TTYC_KF16, KEYC_F4|KEYC_SHIFT }, + { TTYC_KF17, KEYC_F5|KEYC_SHIFT }, + { TTYC_KF18, KEYC_F6|KEYC_SHIFT }, + { TTYC_KF19, KEYC_F7|KEYC_SHIFT }, + { TTYC_KF20, KEYC_F8|KEYC_SHIFT }, + { TTYC_KF21, KEYC_F9|KEYC_SHIFT }, + { TTYC_KF22, KEYC_F10|KEYC_SHIFT }, + { TTYC_KF23, KEYC_F11|KEYC_SHIFT }, + { TTYC_KF24, KEYC_F12|KEYC_SHIFT }, - { TTYC_KF25, KEYC_F1|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF26, KEYC_F2|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF27, KEYC_F3|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF28, KEYC_F4|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF29, KEYC_F5|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF30, KEYC_F6|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF31, KEYC_F7|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF32, KEYC_F8|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF33, KEYC_F9|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF34, KEYC_F10|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF35, KEYC_F11|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF36, KEYC_F12|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KF25, KEYC_F1|KEYC_CTRL }, + { TTYC_KF26, KEYC_F2|KEYC_CTRL }, + { TTYC_KF27, KEYC_F3|KEYC_CTRL }, + { TTYC_KF28, KEYC_F4|KEYC_CTRL }, + { TTYC_KF29, KEYC_F5|KEYC_CTRL }, + { TTYC_KF30, KEYC_F6|KEYC_CTRL }, + { TTYC_KF31, KEYC_F7|KEYC_CTRL }, + { TTYC_KF32, KEYC_F8|KEYC_CTRL }, + { TTYC_KF33, KEYC_F9|KEYC_CTRL }, + { TTYC_KF34, KEYC_F10|KEYC_CTRL }, + { TTYC_KF35, KEYC_F11|KEYC_CTRL }, + { TTYC_KF36, KEYC_F12|KEYC_CTRL }, - { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL }, - { TTYC_KF49, KEYC_F1|KEYC_META|KEYC_XTERM }, - { TTYC_KF50, KEYC_F2|KEYC_META|KEYC_XTERM }, - { TTYC_KF51, KEYC_F3|KEYC_META|KEYC_XTERM }, - { TTYC_KF52, KEYC_F4|KEYC_META|KEYC_XTERM }, - { TTYC_KF53, KEYC_F5|KEYC_META|KEYC_XTERM }, - { TTYC_KF54, KEYC_F6|KEYC_META|KEYC_XTERM }, - { TTYC_KF55, KEYC_F7|KEYC_META|KEYC_XTERM }, - { TTYC_KF56, KEYC_F8|KEYC_META|KEYC_XTERM }, - { TTYC_KF57, KEYC_F9|KEYC_META|KEYC_XTERM }, - { TTYC_KF58, KEYC_F10|KEYC_META|KEYC_XTERM }, - { TTYC_KF59, KEYC_F11|KEYC_META|KEYC_XTERM }, - { TTYC_KF60, KEYC_F12|KEYC_META|KEYC_XTERM }, + { TTYC_KF49, KEYC_F1|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF50, KEYC_F2|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF51, KEYC_F3|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF52, KEYC_F4|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF53, KEYC_F5|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF54, KEYC_F6|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF55, KEYC_F7|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF56, KEYC_F8|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF57, KEYC_F9|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF58, KEYC_F10|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF59, KEYC_F11|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF60, KEYC_F12|KEYC_META|KEYC_IMPLIED_META }, - { TTYC_KF61, KEYC_F1|KEYC_META|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF62, KEYC_F2|KEYC_META|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF63, KEYC_F3|KEYC_META|KEYC_SHIFT|KEYC_XTERM }, + { TTYC_KF61, KEYC_F1|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, + { TTYC_KF62, KEYC_F2|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, + { TTYC_KF63, KEYC_F3|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, { TTYC_KICH1, KEYC_IC }, { TTYC_KDCH1, KEYC_DC }, @@ -329,68 +329,68 @@ static const struct tty_default_key_code tty_default_code_keys[] = { { TTYC_KCUF1, KEYC_RIGHT|KEYC_CURSOR }, /* Key and modifier capabilities. */ - { TTYC_KDC2, KEYC_DC|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDC3, KEYC_DC|KEYC_META|KEYC_XTERM }, - { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, - { TTYC_KDC5, KEYC_DC|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDC7, KEYC_DC|KEYC_META|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIND, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDN3, KEYC_DOWN|KEYC_META|KEYC_XTERM }, - { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, - { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDN7, KEYC_DOWN|KEYC_META|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KEND2, KEYC_END|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KEND3, KEYC_END|KEYC_META|KEYC_XTERM }, - { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, - { TTYC_KEND5, KEYC_END|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KEND7, KEYC_END|KEYC_META|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KHOM3, KEYC_HOME|KEYC_META|KEYC_XTERM }, - { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, - { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KHOM7, KEYC_HOME|KEYC_META|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIC2, KEYC_IC|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KIC3, KEYC_IC|KEYC_META|KEYC_XTERM }, - { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, - { TTYC_KIC5, KEYC_IC|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIC7, KEYC_IC|KEYC_META|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KLFT3, KEYC_LEFT|KEYC_META|KEYC_XTERM }, - { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, - { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KLFT7, KEYC_LEFT|KEYC_META|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KNXT3, KEYC_NPAGE|KEYC_META|KEYC_XTERM }, - { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, - { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KNXT7, KEYC_NPAGE|KEYC_META|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KPRV3, KEYC_PPAGE|KEYC_META|KEYC_XTERM }, - { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, - { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KPRV7, KEYC_PPAGE|KEYC_META|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KRIT3, KEYC_RIGHT|KEYC_META|KEYC_XTERM }, - { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, - { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRIT7, KEYC_RIGHT|KEYC_META|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRI, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KUP2, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KUP3, KEYC_UP|KEYC_META|KEYC_XTERM }, - { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_META|KEYC_XTERM }, - { TTYC_KUP5, KEYC_UP|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KUP7, KEYC_UP|KEYC_META|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KDC2, KEYC_DC|KEYC_SHIFT }, + { TTYC_KDC3, KEYC_DC|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDC5, KEYC_DC|KEYC_CTRL }, + { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KDC7, KEYC_DC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KIND, KEYC_DOWN|KEYC_SHIFT }, + { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT }, + { TTYC_KDN3, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL }, + { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KDN7, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KEND2, KEYC_END|KEYC_SHIFT }, + { TTYC_KEND3, KEYC_END|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KEND5, KEYC_END|KEYC_CTRL }, + { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KEND7, KEYC_END|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT }, + { TTYC_KHOM3, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL }, + { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KHOM7, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KIC2, KEYC_IC|KEYC_SHIFT }, + { TTYC_KIC3, KEYC_IC|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KIC5, KEYC_IC|KEYC_CTRL }, + { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KIC7, KEYC_IC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT }, + { TTYC_KLFT3, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL }, + { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KLFT7, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT }, + { TTYC_KNXT3, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL }, + { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KNXT7, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT }, + { TTYC_KPRV3, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL }, + { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KPRV7, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT }, + { TTYC_KRIT3, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL }, + { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KRIT7, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KRI, KEYC_UP|KEYC_SHIFT }, + { TTYC_KUP2, KEYC_UP|KEYC_SHIFT }, + { TTYC_KUP3, KEYC_UP|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KUP5, KEYC_UP|KEYC_CTRL }, + { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KUP7, KEYC_UP|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, }; /* Add key to tree. */ @@ -401,7 +401,7 @@ tty_keys_add(struct tty *tty, const char *s, key_code key) size_t size; const char *keystr; - keystr = key_string_lookup_key(key); + keystr = key_string_lookup_key(key, 1); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { log_debug("new key %s: 0x%llx (%s)", s, key, keystr); tty_keys_add1(&tty->key_tree, s, key); @@ -719,7 +719,7 @@ first_key: /* Look for a key without the escape. */ n = tty_keys_next1(tty, buf + 1, len - 1, &key, &size, expired); if (n == 0) { /* found */ - if (key & KEYC_XTERM) { + if (key & KEYC_IMPLIED_META) { /* * We want the escape key as well as the xterm * key, because the xterm sequence implicitly @@ -789,7 +789,7 @@ complete_key: */ bspace = tty->tio.c_cc[VERASE]; if (bspace != _POSIX_VDISABLE && (key & KEYC_MASK_KEY) == bspace) - key = (key & KEYC_MASK_MOD) | KEYC_BSPACE; + key = (key & KEYC_MASK_MODIFIERS)|KEYC_BSPACE; /* Remove data from buffer. */ evbuffer_drain(tty->in, size); @@ -896,16 +896,16 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, *size = end + 1; /* Store the key and modifiers. */ - *key = number|KEYC_XTERM; + *key = number; switch (modifiers) { case 2: (*key) |= KEYC_SHIFT; break; case 3: - (*key) |= KEYC_META; + (*key) |= (KEYC_META|KEYC_IMPLIED_META); break; case 4: - (*key) |= (KEYC_SHIFT|KEYC_META); + (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META); break; case 5: (*key) |= KEYC_CTRL; @@ -917,7 +917,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, (*key) |= (KEYC_META|KEYC_CTRL); break; case 8: - (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_CTRL); + (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); break; default: *key = KEYC_NONE; @@ -925,7 +925,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, } if (log_get_level() != 0) { log_debug("%s: extended key %.*s is %llx (%s)", c->name, - (int)*size, buf, *key, key_string_lookup_key(*key)); + (int)*size, buf, *key, key_string_lookup_key(*key, 1)); } return (0); } diff --git a/window-customize.c b/window-customize.c index 12c09cf6..5130357f 100644 --- a/window-customize.c +++ b/window-customize.c @@ -460,7 +460,7 @@ window_customize_build_keys(struct window_customize_modedata *data, bd = key_bindings_first(kt); while (bd != NULL) { - format_add(ft, "key", "%s", key_string_lookup_key(bd->key)); + format_add(ft, "key", "%s", key_string_lookup_key(bd->key, 0)); if (bd->note != NULL) format_add(ft, "key_note", "%s", bd->note); if (filter != NULL) { @@ -1233,7 +1233,7 @@ window_customize_set_key(struct client *c, if (strcmp(s, "Repeat") == 0) bd->flags ^= KEY_BINDING_REPEAT; else if (strcmp(s, "Command") == 0) { - xasprintf(&prompt, "(%s) ", key_string_lookup_key(key)); + xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0)); value = cmd_list_print(bd->cmdlist, 0); new_item = xcalloc(1, sizeof *new_item); @@ -1250,7 +1250,7 @@ window_customize_set_key(struct client *c, free(prompt); free(value); } else if (strcmp(s, "Note") == 0) { - xasprintf(&prompt, "(%s) ", key_string_lookup_key(key)); + xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0)); new_item = xcalloc(1, sizeof *new_item); new_item->data = data; @@ -1395,7 +1395,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, break; if (item->scope == WINDOW_CUSTOMIZE_KEY) { xasprintf(&prompt, "Unbind key %s? ", - key_string_lookup_key(item->key)); + key_string_lookup_key(item->key, 0)); } else xasprintf(&prompt, "Unset option %s? ", item->name); data->references++; diff --git a/window.c b/window.c index 8121ba2d..7cb098dd 100644 --- a/window.c +++ b/window.c @@ -1145,8 +1145,10 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { - if (wme->mode->key != NULL && c != NULL) - wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); + if (wme->mode->key != NULL && c != NULL) { + key &= ~KEYC_MASK_FLAGS; + wme->mode->key(wme, c, s, wl, key, m); + } return (0); } From 7501e297dd5c68e7f2014109f3f7f7424bb455d6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 12:39:37 +0100 Subject: [PATCH 0364/1006] Send CSI u sequences for any keys that do not have a defined sequence already - this should only be similar sequences sent by the terminal outside tmux if enabled. --- input-keys.c | 110 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 99 insertions(+), 11 deletions(-) diff --git a/input-keys.c b/input-keys.c index ef6caa31..e66b2f7e 100644 --- a/input-keys.c +++ b/input-keys.c @@ -432,9 +432,9 @@ int input_key(struct screen *s, struct bufferevent *bev, key_code key) { struct input_key_entry *ike; - size_t datalen; - key_code justkey, newkey; + key_code justkey, newkey, outkey; struct utf8_data ud; + char tmp[64], modifier; /* Mouse keys need a pane. */ if (KEYC_IS_MOUSE(key)) @@ -487,18 +487,106 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) ike = input_key_get(key); if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META)) ike = input_key_get(key & ~KEYC_META); - if (ike == NULL) { - log_debug("key 0x%llx missing", key); - return (-1); + if (ike != NULL) { + log_debug("found key 0x%llx: \"%s\"", key, ike->data); + if (key & KEYC_META && (~key & KEYC_IMPLIED_META)) + bufferevent_write(bev, "\033", 1); + bufferevent_write(bev, ike->data, strlen(ike->data)); + return (0); } - datalen = strlen(ike->data); - log_debug("found key 0x%llx: \"%s\"", key, ike->data); - /* Prefix a \033 for escape. */ - if (key & KEYC_META && (~key & KEYC_IMPLIED_META)) - bufferevent_write(bev, "\033", 1); - bufferevent_write(bev, ike->data, datalen); + /* No builtin key sequence; construct an extended key sequence. */ + outkey = (key & KEYC_MASK_KEY); + if (outkey >= KEYC_BASE) { + switch (outkey) { + case KEYC_IC: + outkey = 2; + break; + case KEYC_DC: + outkey = 3; + break; + case KEYC_PPAGE: + outkey = 5; + break; + case KEYC_NPAGE: + outkey = 6; + break; + case KEYC_HOME: + outkey = 7; + break; + case KEYC_END: + outkey = 8; + break; + case KEYC_F1: + outkey = 11; + break; + case KEYC_F2: + outkey = 12; + break; + case KEYC_F3: + outkey = 13; + break; + case KEYC_F4: + outkey = 14; + break; + case KEYC_F5: + outkey = 15; + break; + case KEYC_F6: + outkey = 17; + break; + case KEYC_F7: + outkey = 18; + break; + case KEYC_F8: + outkey = 19; + break; + case KEYC_F9: + outkey = 20; + break; + case KEYC_F10: + outkey = 21; + break; + case KEYC_F11: + outkey = 23; + break; + case KEYC_F12: + outkey = 24; + break; + default: + goto missing; + } + } + switch (key & KEYC_MASK_MODIFIERS) { + case KEYC_SHIFT: + modifier = '2'; + break; + case KEYC_META: + modifier = '3'; + break; + case KEYC_SHIFT|KEYC_META: + modifier = '4'; + break; + case KEYC_CTRL: + modifier = '5'; + break; + case KEYC_SHIFT|KEYC_CTRL: + modifier = '6'; + break; + case KEYC_META|KEYC_CTRL: + modifier = '7'; + break; + case KEYC_SHIFT|KEYC_META|KEYC_CTRL: + modifier = '8'; + break; + } + xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier); + bufferevent_write(bev, tmp, strlen(tmp)); return (0); + +missing: + log_debug("key 0x%llx missing", key); + return (-1); } /* Get mouse event string. */ From 3e60ab1cafaa00859bdb797918424910f374dee9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 13:15:12 +0100 Subject: [PATCH 0365/1006] Send conventional \033 sequences for keys with just Meta even if they came in as an extended CSI u key sequence. It is much more useful for applications that don't understand CSI u to receive \033> for M-S-. rather than \033[62;3u. --- input-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index e66b2f7e..611592b4 100644 --- a/input-keys.c +++ b/input-keys.c @@ -459,7 +459,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) * If this is a normal 7-bit key, just send it, with a leading escape * if necessary. If it is a UTF-8 key, split it and send it. */ - justkey = (key & ~KEYC_META); + justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META)); if (justkey <= 0x7f) { if (key & KEYC_META) bufferevent_write(bev, "\033", 1); From e23c73457a54249b7da2aa50297f288bbfe750a4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 15:05:49 +0100 Subject: [PATCH 0366/1006] Stop at end of buffer. --- tty-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index 99200689..3764421b 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -878,7 +878,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, } if (end == len) return (1); - if (buf[end] != '~' && buf[end] != 'u') + if (end == sizeof tmp || (buf[end] != '~' && buf[end] != 'u')) return (-1); /* Copy to the buffer. */ From e6b17e77dbb4a757ca17d36086d7d64f097a361a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 15:11:08 +0100 Subject: [PATCH 0367/1006] C-M-S keys need the implied flag also. --- input-keys.c | 2 +- tty-keys.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/input-keys.c b/input-keys.c index 611592b4..b7c02ccd 100644 --- a/input-keys.c +++ b/input-keys.c @@ -340,7 +340,7 @@ static const key_code input_key_modifiers[] = { KEYC_CTRL, KEYC_SHIFT|KEYC_CTRL, KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, - KEYC_SHIFT|KEYC_META|KEYC_CTRL + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }; /* Input key comparison function. */ diff --git a/tty-keys.c b/tty-keys.c index 3764421b..41253d67 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -231,7 +231,7 @@ static const key_code tty_default_xterm_modifiers[] = { KEYC_CTRL, KEYC_SHIFT|KEYC_CTRL, KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, - KEYC_SHIFT|KEYC_META|KEYC_CTRL + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }; /* From 6d92b99dbcb6a616c88df1e6abbae0a96296e7a4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 16:15:24 +0100 Subject: [PATCH 0368/1006] Add a terminal feature for enable/disable extended keys (supported by xterm and mintty) and add an option to make tmux send it. --- options-table.c | 8 ++++++++ tmux.1 | 8 ++++++++ tmux.h | 4 +++- tty-features.c | 17 +++++++++++++++-- tty-term.c | 2 ++ tty.c | 12 +++++------- 6 files changed, 41 insertions(+), 10 deletions(-) diff --git a/options-table.c b/options-table.c index 7e379382..1341ed2e 100644 --- a/options-table.c +++ b/options-table.c @@ -251,6 +251,14 @@ const struct options_table_entry options_table[] = { "clients." }, + { .name = "extended-keys", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, + .default_num = 0, + .text = "Whether to request extended key sequences from terminals " + "that support it." + }, + { .name = "focus-events", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, diff --git a/tmux.1 b/tmux.1 index 17793911..e0c5667d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3225,6 +3225,12 @@ sessions. .Op Ic on | off .Xc If enabled, the server will exit when there are no attached clients. +.It Xo Ic extended-keys +.Op Ic on | off +.Xc +When enabled, extended keys are requested from the terminal and if supported +are recognised by +.Nm . .It Xo Ic focus-events .Op Ic on | off .Xc @@ -5744,6 +5750,8 @@ Disable and enable bracketed paste. These are set automatically if the .Em XT capability is present. +.It Em \&Dseks , \&Eneks +Disable and enable extended keys. .It Em \&Dsfcs , \&Enfcs Disable and enable focus reporting. These are set automatically if the diff --git a/tmux.h b/tmux.h index 07d10bcc..d814aa6c 100644 --- a/tmux.h +++ b/tmux.h @@ -292,6 +292,7 @@ enum tty_code_code { TTYC_DL, TTYC_DL1, TTYC_DSBP, + TTYC_DSEKS, TTYC_DSFCS, TTYC_DSMG, TTYC_E3, @@ -301,6 +302,7 @@ enum tty_code_code { TTYC_EL1, TTYC_ENACS, TTYC_ENBP, + TTYC_ENEKS, TTYC_ENFCS, TTYC_ENMG, TTYC_FSL, @@ -1286,7 +1288,7 @@ struct tty { /* 0x8 unused */ #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 -#define TTY_FOCUS 0x40 +/* 0x40 unused */ #define TTY_BLOCK 0x80 #define TTY_HAVEDA 0x100 #define TTY_HAVEXDA 0x200 diff --git a/tty-features.c b/tty-features.c index 30d3d1a0..26344b90 100644 --- a/tty-features.c +++ b/tty-features.c @@ -190,6 +190,18 @@ static const struct tty_feature tty_feature_sync = { 0 }; +/* Terminal supports extended keys. */ +static const char *tty_feature_extkeys_capabilities[] = { + "Eneks=\\E[>4;1m", + "Dseks=\\E[>4m", + NULL +}; +static const struct tty_feature tty_feature_extkeys = { + "extkeys", + tty_feature_extkeys_capabilities, + 0 +}; + /* Terminal supports DECSLRM margins. */ static const char *tty_feature_margins_capabilities[] = { "Enmg=\\E[?69h", @@ -218,6 +230,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_ccolour, &tty_feature_clipboard, &tty_feature_cstyle, + &tty_feature_extkeys, &tty_feature_focus, &tty_feature_margins, &tty_feature_overline, @@ -321,7 +334,7 @@ tty_default_features(int *feat, const char *name, u_int version) } table[] = { #define TTY_FEATURES_BASE_MODERN_XTERM "256,RGB,bpaste,clipboard,strikethrough,title" { .name = "mintty", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,margins,overline" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,margins,overline" }, { .name = "tmux", .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,overline,usstyle" @@ -333,7 +346,7 @@ tty_default_features(int *feat, const char *name, u_int version) .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,margins,sync" }, { .name = "XTerm", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,margins,rectfill" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,focus,margins,rectfill" } }; u_int i; diff --git a/tty-term.c b/tty-term.c index e8ac6634..36d8a2e6 100644 --- a/tty-term.c +++ b/tty-term.c @@ -86,6 +86,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DSEKS] = { TTYCODE_STRING, "Dseks" }, [TTYC_DSFCS] = { TTYCODE_STRING, "Dsfcs" }, [TTYC_DSBP] = { TTYCODE_STRING, "Dsbp" }, [TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" }, @@ -96,6 +97,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_EL] = { TTYCODE_STRING, "el" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, [TTYC_ENBP] = { TTYCODE_STRING, "Enbp" }, + [TTYC_ENEKS] = { TTYCODE_STRING, "Eneks" }, [TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" }, [TTYC_ENMG] = { TTYCODE_STRING, "Enmg" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, diff --git a/tty.c b/tty.c index 99996dfa..1c025b6f 100644 --- a/tty.c +++ b/tty.c @@ -329,10 +329,10 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1006l\033[?1005l"); } - if (options_get_number(global_options, "focus-events")) { - tty->flags |= TTY_FOCUS; + if (options_get_number(global_options, "focus-events")) tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); - } + if (options_get_number(global_options, "extended-keys")) + tty_raw(tty, tty_term_string(tty->term, TTYC_ENEKS)); if (tty->term->flags & TERM_VT100LIKE) tty_puts(tty, "\033[?7727h"); @@ -415,12 +415,10 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, "\033[?1006l\033[?1005l"); } - if (tty->flags & TTY_FOCUS) { - tty->flags &= ~TTY_FOCUS; - tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); - } if (tty->term->flags & TERM_VT100LIKE) tty_raw(tty, "\033[?7727l"); + tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); + tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS)); if (tty_use_margin(tty)) tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); From 3a4f3ee087ab5273e5d49ab33abf9135c3a21636 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 16:17:20 +0100 Subject: [PATCH 0369/1006] Mask off flags bits in menu keys. --- menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu.c b/menu.c index 1969cef4..7f6933c5 100644 --- a/menu.c +++ b/menu.c @@ -226,7 +226,7 @@ menu_key_cb(struct client *c, struct key_event *event) goto chosen; } } - switch (event->key) { + switch (event->key & ~KEYC_MASK_FLAGS) { case KEYC_UP: case 'k': if (old == -1) From c364a7142c10af9a82b0ef19a3ef204b605a225d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 17:40:24 +0100 Subject: [PATCH 0370/1006] Only forward extended keys if the application has requested them, even though we use the CSI u sequence and xterm uses CSI 27 ~ - this is what mintty does as well. --- input-keys.c | 2 ++ input.c | 17 +++++++++++++++++ tmux.h | 5 +++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/input-keys.c b/input-keys.c index b7c02ccd..07f102b8 100644 --- a/input-keys.c +++ b/input-keys.c @@ -496,6 +496,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) } /* No builtin key sequence; construct an extended key sequence. */ + if (~s->mode & MODE_KEXTENDED) + goto missing; outkey = (key & KEYC_MASK_KEY); if (outkey >= KEYC_BASE) { switch (outkey) { diff --git a/input.c b/input.c index 70681e61..a8ca9478 100644 --- a/input.c +++ b/input.c @@ -241,6 +241,8 @@ enum input_csi_type { INPUT_CSI_HPA, INPUT_CSI_ICH, INPUT_CSI_IL, + INPUT_CSI_MODOFF, + INPUT_CSI_MODSET, INPUT_CSI_RCP, INPUT_CSI_REP, INPUT_CSI_RM, @@ -288,7 +290,9 @@ static const struct input_table_entry input_csi_table[] = { { 'h', "?", INPUT_CSI_SM_PRIVATE }, { 'l', "", INPUT_CSI_RM }, { 'l', "?", INPUT_CSI_RM_PRIVATE }, + { 'm', ">", INPUT_CSI_MODSET }, { 'm', "", INPUT_CSI_SGR }, + { 'n', ">", INPUT_CSI_MODOFF }, { 'n', "", INPUT_CSI_DSR }, { 'q', " ", INPUT_CSI_DECSCUSR }, { 'q', ">", INPUT_CSI_XDA }, @@ -1380,6 +1384,19 @@ input_csi_dispatch(struct input_ctx *ictx) if (n != -1 && m != -1) screen_write_cursormove(sctx, m - 1, n - 1, 1); break; + case INPUT_CSI_MODSET: + n = input_get(ictx, 0, 1, 1); + m = input_get(ictx, 1, 1, 1); + if (n == 0 || (n == 4 && m == 0)) + screen_write_mode_clear(sctx, MODE_KEXTENDED); + else if (n == 4 && (m == 1 || m == 2)) + screen_write_mode_set(sctx, MODE_KEXTENDED); + break; + case INPUT_CSI_MODOFF: + n = input_get(ictx, 0, 1, 1); + if (n == 4) + screen_write_mode_clear(sctx, MODE_KEXTENDED); + break; case INPUT_CSI_WINOPS: input_csi_dispatch_winops(ictx); break; diff --git a/tmux.h b/tmux.h index d814aa6c..65164ccc 100644 --- a/tmux.h +++ b/tmux.h @@ -579,8 +579,8 @@ struct msg_write_close { #define MODE_CURSOR 0x1 #define MODE_INSERT 0x2 #define MODE_KCURSOR 0x4 -#define MODE_KKEYPAD 0x8 /* set = application, clear = number */ -#define MODE_WRAP 0x10 /* whether lines wrap */ +#define MODE_KKEYPAD 0x8 +#define MODE_WRAP 0x10 #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 #define MODE_BLINKING 0x80 @@ -591,6 +591,7 @@ struct msg_write_close { #define MODE_MOUSE_ALL 0x1000 #define MODE_ORIGIN 0x2000 #define MODE_CRLF 0x4000 +#define MODE_KEXTENDED 0x8000 #define ALL_MODES 0xffffff #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) From dcf537519fd15d447040f9dbd07e3a2fc1b9546e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 17:48:21 +0100 Subject: [PATCH 0371/1006] Fix default values for new escape sequences. --- input.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/input.c b/input.c index a8ca9478..ebd2bc27 100644 --- a/input.c +++ b/input.c @@ -1385,15 +1385,15 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_cursormove(sctx, m - 1, n - 1, 1); break; case INPUT_CSI_MODSET: - n = input_get(ictx, 0, 1, 1); - m = input_get(ictx, 1, 1, 1); + n = input_get(ictx, 0, 0, 0); + m = input_get(ictx, 1, 0, 0); if (n == 0 || (n == 4 && m == 0)) screen_write_mode_clear(sctx, MODE_KEXTENDED); else if (n == 4 && (m == 1 || m == 2)) screen_write_mode_set(sctx, MODE_KEXTENDED); break; case INPUT_CSI_MODOFF: - n = input_get(ictx, 0, 1, 1); + n = input_get(ictx, 0, 0, 0); if (n == 4) screen_write_mode_clear(sctx, MODE_KEXTENDED); break; From 0b828b91a5f9bdf8d62e4352a842ec98a32b0b92 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 17:49:07 +0100 Subject: [PATCH 0372/1006] Only send XDA on 0. --- input.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index ebd2bc27..ffa22995 100644 --- a/input.c +++ b/input.c @@ -1609,7 +1609,9 @@ input_csi_dispatch(struct input_ctx *ictx) screen_set_cursor_style(s, n); break; case INPUT_CSI_XDA: - input_reply(ictx, "\033P>|tmux %s\033\\", getversion()); + n = input_get(ictx, 0, 0, 0); + if (n != 0) + input_reply(ictx, "\033P>|tmux %s\033\\", getversion()); break; } From 7317a0865cd5bcdff065be83e2105336f0038eae Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 17:49:58 +0100 Subject: [PATCH 0373/1006] Get == and != the right way round. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index ffa22995..57726233 100644 --- a/input.c +++ b/input.c @@ -1610,7 +1610,7 @@ input_csi_dispatch(struct input_ctx *ictx) break; case INPUT_CSI_XDA: n = input_get(ictx, 0, 0, 0); - if (n != 0) + if (n == 0) input_reply(ictx, "\033P>|tmux %s\033\\", getversion()); break; From ca60aabab5ddfccc6c2d35dc3c7aca0e4e9aee85 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 18:25:44 +0100 Subject: [PATCH 0374/1006] Translate special CSI u keys on input. --- tty-keys.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index 41253d67..d51d461e 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -896,7 +896,65 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, *size = end + 1; /* Store the key and modifiers. */ - *key = number; + switch (number) { + case 2: + *key = KEYC_IC; + break; + case 3: + *key = KEYC_DC; + break; + case 5: + *key = KEYC_PPAGE; + break; + case 6: + *key = KEYC_NPAGE; + break; + case 7: + *key = KEYC_HOME; + break; + case 8: + *key = KEYC_END; + break; + case 11: + *key = KEYC_F1; + break; + case 12: + *key = KEYC_F2; + break; + case 13: + *key = KEYC_F3; + break; + case 14: + *key = KEYC_F4; + break; + case 15: + *key = KEYC_F5; + break; + case 17: + *key = KEYC_F6; + break; + case 18: + *key = KEYC_F7; + break; + case 19: + *key = KEYC_F8; + break; + case 20: + *key = KEYC_F9; + break; + case 21: + *key = KEYC_F10; + break; + case 23: + *key = KEYC_F11; + break; + case 24: + *key = KEYC_F12; + break; + default: + *key = number; + break; + } switch (modifiers) { case 2: (*key) |= KEYC_SHIFT; From 401f1977501894e89f13d09508acc38e1c98f2d0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 18:58:13 +0100 Subject: [PATCH 0375/1006] Er, misread this and it is not needed. --- input-keys.c | 60 ---------------------------------------------------- tty-keys.c | 60 +--------------------------------------------------- 2 files changed, 1 insertion(+), 119 deletions(-) diff --git a/input-keys.c b/input-keys.c index 07f102b8..a6bd6eca 100644 --- a/input-keys.c +++ b/input-keys.c @@ -499,66 +499,6 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) if (~s->mode & MODE_KEXTENDED) goto missing; outkey = (key & KEYC_MASK_KEY); - if (outkey >= KEYC_BASE) { - switch (outkey) { - case KEYC_IC: - outkey = 2; - break; - case KEYC_DC: - outkey = 3; - break; - case KEYC_PPAGE: - outkey = 5; - break; - case KEYC_NPAGE: - outkey = 6; - break; - case KEYC_HOME: - outkey = 7; - break; - case KEYC_END: - outkey = 8; - break; - case KEYC_F1: - outkey = 11; - break; - case KEYC_F2: - outkey = 12; - break; - case KEYC_F3: - outkey = 13; - break; - case KEYC_F4: - outkey = 14; - break; - case KEYC_F5: - outkey = 15; - break; - case KEYC_F6: - outkey = 17; - break; - case KEYC_F7: - outkey = 18; - break; - case KEYC_F8: - outkey = 19; - break; - case KEYC_F9: - outkey = 20; - break; - case KEYC_F10: - outkey = 21; - break; - case KEYC_F11: - outkey = 23; - break; - case KEYC_F12: - outkey = 24; - break; - default: - goto missing; - } - } switch (key & KEYC_MASK_MODIFIERS) { case KEYC_SHIFT: modifier = '2'; diff --git a/tty-keys.c b/tty-keys.c index d51d461e..41253d67 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -896,65 +896,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, *size = end + 1; /* Store the key and modifiers. */ - switch (number) { - case 2: - *key = KEYC_IC; - break; - case 3: - *key = KEYC_DC; - break; - case 5: - *key = KEYC_PPAGE; - break; - case 6: - *key = KEYC_NPAGE; - break; - case 7: - *key = KEYC_HOME; - break; - case 8: - *key = KEYC_END; - break; - case 11: - *key = KEYC_F1; - break; - case 12: - *key = KEYC_F2; - break; - case 13: - *key = KEYC_F3; - break; - case 14: - *key = KEYC_F4; - break; - case 15: - *key = KEYC_F5; - break; - case 17: - *key = KEYC_F6; - break; - case 18: - *key = KEYC_F7; - break; - case 19: - *key = KEYC_F8; - break; - case 20: - *key = KEYC_F9; - break; - case 21: - *key = KEYC_F10; - break; - case 23: - *key = KEYC_F11; - break; - case 24: - *key = KEYC_F12; - break; - default: - *key = number; - break; - } + *key = number; switch (modifiers) { case 2: (*key) |= KEYC_SHIFT; From 67090dd91de2a2bf9e04dfd6a084cb4e113e3753 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 19:10:06 +0100 Subject: [PATCH 0376/1006] XTerm not xterm. --- tty-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index 41253d67..5a2b3817 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1293,7 +1293,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, else if (strncmp(tmp, "tmux ", 5) == 0) tty_default_features(&c->term_features, "tmux", 0); else if (strncmp(tmp, "XTerm(", 6) == 0) - tty_default_features(&c->term_features, "xterm", 0); + tty_default_features(&c->term_features, "XTerm", 0); else if (strncmp(tmp, "mintty ", 7) == 0) tty_default_features(&c->term_features, "mintty", 0); log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); From e8ca5a4c7d336df422c0986fc9aee0ac52b8993a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 19:17:56 +0100 Subject: [PATCH 0377/1006] List needs to be sorted. --- input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/input.c b/input.c index 57726233..44b1b948 100644 --- a/input.c +++ b/input.c @@ -290,10 +290,10 @@ static const struct input_table_entry input_csi_table[] = { { 'h', "?", INPUT_CSI_SM_PRIVATE }, { 'l', "", INPUT_CSI_RM }, { 'l', "?", INPUT_CSI_RM_PRIVATE }, - { 'm', ">", INPUT_CSI_MODSET }, { 'm', "", INPUT_CSI_SGR }, - { 'n', ">", INPUT_CSI_MODOFF }, + { 'm', ">", INPUT_CSI_MODSET }, { 'n', "", INPUT_CSI_DSR }, + { 'n', ">", INPUT_CSI_MODOFF }, { 'q', " ", INPUT_CSI_DECSCUSR }, { 'q', ">", INPUT_CSI_XDA }, { 'r', "", INPUT_CSI_DECSTBM }, From c2167c5ee81022ddf9f02cfe189fcea736026ced Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 15 May 2020 22:52:55 +0100 Subject: [PATCH 0378/1006] On select-window, make this client the latest client for the window. --- cmd-select-window.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd-select-window.c b/cmd-select-window.c index 377e3633..c85f36be 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -85,6 +85,7 @@ static enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *c = cmdq_get_client(item); struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct winlink *wl = target->wl; @@ -141,6 +142,8 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) } cmdq_insert_hook(s, item, current, "after-select-window"); } + if (c->session != NULL) + s->curw->window->latest = c; recalculate_sizes(); return (CMD_RETURN_NORMAL); From 740f047a850cb189d67dc17938e86fcd2bdfb1c6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 16 May 2020 07:32:46 +0100 Subject: [PATCH 0379/1006] Need to update features after all the sequences come in. --- tty.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tty.c b/tty.c index 1c025b6f..770c8b78 100644 --- a/tty.c +++ b/tty.c @@ -286,6 +286,8 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data) struct client *c = tty->client; log_debug("%s: start timer fired", c->name); + if ((tty->flags & (TTY_HAVEDA|TTY_HAVEXDA)) == 0) + tty_update_features(tty); tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); } @@ -329,13 +331,6 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1006l\033[?1005l"); } - if (options_get_number(global_options, "focus-events")) - tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); - if (options_get_number(global_options, "extended-keys")) - tty_raw(tty, tty_term_string(tty->term, TTYC_ENEKS)); - if (tty->term->flags & TERM_VT100LIKE) - tty_puts(tty, "\033[?7727h"); - evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); @@ -469,6 +464,12 @@ tty_update_features(struct tty *tty) if (tty_use_margin(tty)) tty_putcode(tty, TTYC_ENMG); + if (options_get_number(global_options, "extended-keys")) + tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS)); + if (options_get_number(global_options, "focus-events")) + tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); + if (tty->term->flags & TERM_VT100LIKE) + tty_puts(tty, "\033[?7727h"); } void From 53c84fd4aa87b4ff80e79ef79155f13c69f1aebc Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 16 May 2020 07:39:22 +0100 Subject: [PATCH 0380/1006] If the application has not requested extended keys, then C-1 sends 1 not nothing. --- input-keys.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index a6bd6eca..59b3495e 100644 --- a/input-keys.c +++ b/input-keys.c @@ -496,8 +496,12 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) } /* No builtin key sequence; construct an extended key sequence. */ - if (~s->mode & MODE_KEXTENDED) + if (~s->mode & MODE_KEXTENDED) { + if ((key & KEYC_MASK_MODIFIERS) == KEYC_CTRL && + (key & KEYC_MASK_KEY) < KEYC_BASE) + return (input_key(s, bev, key & ~KEYC_CTRL)); goto missing; + } outkey = (key & KEYC_MASK_KEY); switch (key & KEYC_MASK_MODIFIERS) { case KEYC_SHIFT: From 57fe03dc5a132ee131de457afa5724ec735ae811 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 16 May 2020 14:57:36 +0100 Subject: [PATCH 0381/1006] Move lazy resize from the pane to the window, there is no point in resizing the window unless it is the current window, and if we do and don't resize the pane until later there are problems if the size changes from A to B then back to A. --- resize.c | 25 ++++++++++++++++---- server-client.c | 62 ++++++++++++++++++++++++++----------------------- tmux.h | 6 +++++ window.c | 5 ++-- 4 files changed, 62 insertions(+), 36 deletions(-) diff --git a/resize.c b/resize.c index 15d146d8..68717e35 100644 --- a/resize.c +++ b/resize.c @@ -61,6 +61,7 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) tty_update_window_offset(w); server_redraw_window(w); notify_window("window-layout-changed", w); + w->flags &= ~WINDOW_RESIZE; } static int @@ -346,16 +347,30 @@ recalculate_size(struct window *w) changed = 0; break; } - if (changed && w->sx == sx && w->sy == sy) - changed = 0; + if (w->flags & WINDOW_RESIZE) { + if (changed && w->new_sx == sx && w->new_sy == sy) + changed = 0; + } else { + if (changed && w->sx == sx && w->sy == sy) + changed = 0; + } if (!changed) { tty_update_window_offset(w); return; } - log_debug("%s: @%u changed to %u,%u (%ux%u)", __func__, w->id, sx, sy, - xpixel, ypixel); - resize_window(w, sx, sy, xpixel, ypixel); + log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy); + if (type == WINDOW_SIZE_MANUAL) + resize_window(w, sx, sy, xpixel, ypixel); + else { + w->new_sx = sx; + w->new_sy = sy; + w->new_xpixel = xpixel; + w->new_ypixel = ypixel; + + w->flags |= WINDOW_RESIZE; + tty_update_window_offset(w); + } } void diff --git a/server-client.c b/server-client.c index 87a4f533..24a564c0 100644 --- a/server-client.c +++ b/server-client.c @@ -31,8 +31,9 @@ #include "tmux.h" static void server_client_free(int, short, void *); -static void server_client_check_focus(struct window_pane *); -static void server_client_check_resize(struct window_pane *); +static void server_client_check_pane_focus(struct window_pane *); +static void server_client_check_pane_resize(struct window_pane *); +static void server_client_check_window_resize(struct window *); static key_code server_client_check_mouse(struct client *, struct key_event *); static void server_client_repeat_timer(int, short, void *); static void server_client_click_timer(int, short, void *); @@ -1339,10 +1340,13 @@ server_client_loop(void) struct client *c; struct window *w; struct window_pane *wp; - struct winlink *wl; - struct session *s; - int focus, attached, resize; + int focus; + /* Check for window resize. This is done before redrawing. */ + RB_FOREACH(w, windows, &windows) + server_client_check_window_resize(w); + + /* Check clients. */ TAILQ_FOREACH(c, &clients, entry) { server_client_check_exit(c); if (c->session != NULL) { @@ -1354,34 +1358,14 @@ server_client_loop(void) /* * Any windows will have been redrawn as part of clients, so clear * their flags now. Also check pane focus and resize. - * - * As an optimization, panes in windows that are in an attached session - * but not the current window are not resized (this reduces the amount - * of work needed when, for example, resizing an X terminal a - * lot). Windows in no attached session are resized immediately since - * that is likely to have come from a command like split-window and be - * what the user wanted. */ focus = options_get_number(global_options, "focus-events"); RB_FOREACH(w, windows, &windows) { - attached = resize = 0; - TAILQ_FOREACH(wl, &w->winlinks, wentry) { - s = wl->session; - if (s->attached != 0) - attached = 1; - if (s->attached != 0 && s->curw == wl) { - resize = 1; - break; - } - } - if (!attached) - resize = 1; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { if (focus) - server_client_check_focus(wp); - if (resize) - server_client_check_resize(wp); + server_client_check_pane_focus(wp); + server_client_check_pane_resize(wp); } wp->flags &= ~PANE_REDRAW; } @@ -1389,6 +1373,26 @@ server_client_loop(void) } } +/* Check if window needs to be resized. */ +static void +server_client_check_window_resize(struct window *w) +{ + struct winlink *wl; + + if (~w->flags & WINDOW_RESIZE) + return; + + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + if (wl->session->attached != 0 && wl->session->curw == wl) + break; + } + if (wl == NULL) + return; + + log_debug("%s: resizing window @%u", __func__, w->id); + resize_window(w, w->new_sx, w->new_sy, w->new_xpixel, w->new_ypixel); +} + /* Check if we need to force a resize. */ static int server_client_resize_force(struct window_pane *wp) @@ -1470,7 +1474,7 @@ server_client_resize_event(__unused int fd, __unused short events, void *data) /* Check if pane should be resized. */ static void -server_client_check_resize(struct window_pane *wp) +server_client_check_pane_resize(struct window_pane *wp) { if (~wp->flags & PANE_RESIZE) return; @@ -1488,7 +1492,7 @@ server_client_check_resize(struct window_pane *wp) /* Check whether pane should be focused. */ static void -server_client_check_focus(struct window_pane *wp) +server_client_check_pane_focus(struct window_pane *wp) { struct client *c; int push; diff --git a/tmux.h b/tmux.h index 65164ccc..a9786990 100644 --- a/tmux.h +++ b/tmux.h @@ -1012,12 +1012,18 @@ struct window { u_int xpixel; u_int ypixel; + u_int new_sx; + u_int new_sy; + u_int new_xpixel; + u_int new_ypixel; + int flags; #define WINDOW_BELL 0x1 #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 #define WINDOW_ZOOMED 0x8 #define WINDOW_WASZOOMED 0x10 +#define WINDOW_RESIZE 0x20 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) int alerts_queued; diff --git a/window.c b/window.c index 7cb098dd..b35ab8ab 100644 --- a/window.c +++ b/window.c @@ -438,13 +438,15 @@ window_pane_send_resize(struct window_pane *wp, int yadjust) { struct window *w = wp->window; struct winsize ws; + u_int sy = wp->sy + yadjust; if (wp->fd == -1) return; + log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, sy); memset(&ws, 0, sizeof ws); ws.ws_col = wp->sx; - ws.ws_row = wp->sy + yadjust; + ws.ws_row = sy; ws.ws_xpixel = w->xpixel * ws.ws_col; ws.ws_ypixel = w->ypixel * ws.ws_row; if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) @@ -1001,7 +1003,6 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); - wp->flags |= (PANE_RESIZE|PANE_RESIZED); } From 0dd19442061ac92b6d9b61a91e4b262c2f08921f Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:10:29 +0000 Subject: [PATCH 0382/1006] Tweak the default choose modes formats: - Only show pane title if it is not default and not empty. - Add a prettier time format and use that instead of long ctime(). - Remove clutter and change the order. --- format.c | 127 ++++++++++++++++++++++++++++++++++++------------ tmux.1 | 4 ++ window-buffer.c | 2 +- window-client.c | 3 +- window-tree.c | 8 +-- 5 files changed, 105 insertions(+), 39 deletions(-) diff --git a/format.c b/format.c index f0a5a3b1..c43baf05 100644 --- a/format.c +++ b/format.c @@ -100,6 +100,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_SESSIONS 0x80 #define FORMAT_WINDOWS 0x100 #define FORMAT_PANES 0x200 +#define FORMAT_PRETTY 0x400 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 @@ -1322,6 +1323,52 @@ format_quote(const char *s) return (out); } +/* Make a prettier time. */ +static char * +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) + now = t; + age = now - t; + + localtime_r(&now, &now_tm); + localtime_r(&t, &tm); + + /* Last 24 hours. */ + if (age < 24 * 3600) { + strftime(s, sizeof s, "%H:%M", &tm); + return (xstrdup(s)); + } + + /* This month or last 28 days. */ + if ((tm.tm_year == now_tm.tm_year && tm.tm_mon == now_tm.tm_mon) || + age < 28 * 24 * 3600) { + strftime(s, sizeof s, "%a%d", &tm); + return (xstrdup(s)); + } + + /* 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); + return (xstrdup(s)); + } + + /* Older than that. */ + strftime(s, sizeof s, "%h%y", &tm); + return (xstrdup(s)); +} + /* Find a format entry. */ static char * format_find(struct format_tree *ft, const char *key, int modifiers) @@ -1331,40 +1378,31 @@ format_find(struct format_tree *ft, const char *key, int modifiers) static char s[64]; struct options_entry *o; int idx; - char *found, *saved; + char *found = NULL, *saved; + const char *errstr; + time_t t = 0; - if (~modifiers & FORMAT_TIMESTRING) { - o = options_parse_get(global_options, key, &idx, 0); - if (o == NULL && ft->wp != NULL) - o = options_parse_get(ft->wp->options, key, &idx, 0); - if (o == NULL && ft->w != NULL) - o = options_parse_get(ft->w->options, key, &idx, 0); - if (o == NULL) - o = options_parse_get(global_w_options, key, &idx, 0); - if (o == NULL && ft->s != NULL) - o = options_parse_get(ft->s->options, key, &idx, 0); - if (o == NULL) - o = options_parse_get(global_s_options, key, &idx, 0); - if (o != NULL) { - found = options_tostring(o, idx, 1); - goto found; - } + o = options_parse_get(global_options, key, &idx, 0); + if (o == NULL && ft->wp != NULL) + o = options_parse_get(ft->wp->options, key, &idx, 0); + if (o == NULL && ft->w != NULL) + o = options_parse_get(ft->w->options, key, &idx, 0); + if (o == NULL) + o = options_parse_get(global_w_options, key, &idx, 0); + if (o == NULL && ft->s != NULL) + o = options_parse_get(ft->s->options, key, &idx, 0); + if (o == NULL) + o = options_parse_get(global_s_options, key, &idx, 0); + if (o != NULL) { + found = options_tostring(o, idx, 1); + goto found; } - found = NULL; - fe_find.key = (char *) key; + fe_find.key = (char *)key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe != NULL) { - if (modifiers & FORMAT_TIMESTRING) { - if (fe->t == 0) - return (NULL); - ctime_r(&fe->t, s); - s[strcspn(s, "\n")] = '\0'; - found = xstrdup(s); - goto found; - } if (fe->t != 0) { - xasprintf(&found, "%lld", (long long)fe->t); + t = fe->t; goto found; } if (fe->value == NULL && fe->cb != NULL) @@ -1390,7 +1428,28 @@ format_find(struct format_tree *ft, const char *key, int modifiers) return (NULL); found: - if (found == NULL) + if (modifiers & FORMAT_TIMESTRING) { + if (t == 0 && found != NULL) { + t = strtonum(found, 0, INT64_MAX, &errstr); + if (errstr != NULL) + t = 0; + free(found); + } + if (t == 0) + return (NULL); + if (modifiers & FORMAT_PRETTY) + found = format_pretty_time(t); + else { + ctime_r(&t, s); + s[strcspn(s, "\n")] = '\0'; + found = xstrdup(s); + } + return (found); + } + + if (t != 0) + xasprintf(&found, "%lld", (long long)t); + else if (found == NULL) return (NULL); if (modifiers & FORMAT_BASENAME) { saved = found; @@ -1532,7 +1591,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lbdtqETSWP<>", cp[0]) != NULL && + if (strchr("lbdqETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -1553,7 +1612,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) } /* Now try single character with arguments. */ - if (strchr("mCs=pe", cp[0]) == NULL) + if (strchr("mCst=pe", cp[0]) == NULL) break; c = cp[0]; @@ -1978,7 +2037,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, case 'e': if (fm->argc < 1 || fm->argc > 3) break; - mexp = fm; + mexp = fm; break; case 'l': modifiers |= FORMAT_LITERAL; @@ -1991,6 +2050,10 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, break; case 't': modifiers |= FORMAT_TIMESTRING; + if (fm->argc < 1) + break; + if (strchr(fm->argv[0], 'p') != NULL) + modifiers |= FORMAT_PRETTY; break; case 'q': modifiers |= FORMAT_QUOTE; diff --git a/tmux.1 b/tmux.1 index d1c44216..71876d83 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4353,6 +4353,10 @@ gives .Ql #{t:window_activity} gives .Ql Sun Oct 25 09:25:02 2015 . +Adding +.Ql p ( +.Ql `t/p` ) +will use shorter but less accurate time format for times in the past. The .Ql b:\& and diff --git a/window-buffer.c b/window-buffer.c index 37707233..dae8899e 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -37,7 +37,7 @@ static void window_buffer_key(struct window_mode_entry *, #define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'" #define WINDOW_BUFFER_DEFAULT_FORMAT \ - "#{buffer_size} bytes (#{t:buffer_created})" + "#{t/p:buffer_created}: #{buffer_sample}" static const struct menu_item window_buffer_menu_items[] = { { "Paste", 'p', NULL }, diff --git a/window-client.c b/window-client.c index 4688cbf3..cd424dd7 100644 --- a/window-client.c +++ b/window-client.c @@ -37,8 +37,7 @@ static void window_client_key(struct window_mode_entry *, #define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'" #define WINDOW_CLIENT_DEFAULT_FORMAT \ - "session #{session_name} " \ - "(#{client_width}x#{client_height}, #{t:client_activity})" + "#{t/p:client_activity}: session #{session_name}" static const struct menu_item window_client_menu_items[] = { { "Detach", 'd', NULL }, diff --git a/window-tree.c b/window-tree.c index 156aafd9..2eff4b8a 100644 --- a/window-tree.c +++ b/window-tree.c @@ -38,13 +38,13 @@ static void window_tree_key(struct window_mode_entry *, #define WINDOW_TREE_DEFAULT_FORMAT \ "#{?pane_format," \ "#{?pane_marked,#[reverse],}" \ - "#{pane_current_command}#{?pane_active,*,}#{?pane_marked,M,} \"#{pane_title}\"" \ + "#{pane_current_command}#{?pane_active,*,}#{?pane_marked,M,}" \ + "#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \ "," \ "#{?window_format," \ "#{?window_marked_flag,#[reverse],}" \ - "#{window_name}#{window_flags} " \ - "(#{window_panes} panes)" \ - "#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \ + "#{window_name}#{window_flags}" \ + "#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \ "," \ "#{session_windows} windows" \ "#{?session_grouped, " \ From 471f697423df33d535a7cc1fac5f01f718969e34 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:13:37 +0000 Subject: [PATCH 0383/1006] Add an attribute for ACS. --- attributes.c | 4 +++- style.c | 4 ++-- tmux.1 | 5 ++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/attributes.c b/attributes.c index ca88a056..b839f06d 100644 --- a/attributes.c +++ b/attributes.c @@ -31,7 +31,8 @@ attributes_tostring(int attr) if (attr == 0) return ("none"); - len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s", + len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + (attr & GRID_ATTR_CHARSET) ? "acs," : "", (attr & GRID_ATTR_BRIGHT) ? "bright," : "", (attr & GRID_ATTR_DIM) ? "dim," : "", (attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "", @@ -62,6 +63,7 @@ attributes_fromstring(const char *str) const char *name; int attr; } table[] = { + { "acs", GRID_ATTR_CHARSET }, { "bright", GRID_ATTR_BRIGHT }, { "bold", GRID_ATTR_BRIGHT }, { "dim", GRID_ATTR_DIM }, diff --git a/style.c b/style.c index 6ba4c524..da3b4c78 100644 --- a/style.c +++ b/style.c @@ -26,7 +26,7 @@ #include "tmux.h" /* Mask for bits not included in style. */ -#define STYLE_ATTR_MASK (~GRID_ATTR_CHARSET) +#define STYLE_ATTR_MASK (~0) /* Default style. */ static struct style style_default = { @@ -247,7 +247,7 @@ style_tostring(struct style *sy) colour_tostring(gc->bg)); comma = ","; } - if (gc->attr != 0 && gc->attr != GRID_ATTR_CHARSET) { + if (gc->attr != 0) { xsnprintf(s + off, sizeof s - off, "%s%s", comma, attributes_tostring(gc->attr)); comma = ","; diff --git a/tmux.1 b/tmux.1 index 71876d83..a519b6b5 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4661,7 +4661,8 @@ for the terminal default colour; or a hexadecimal RGB string such as Set the background colour. .It Ic none Set no attributes (turn off any active attributes). -.It Xo Ic bright +.It Xo Ic acs , +.Ic bright (or .Ic bold ) , .Ic dim , @@ -4681,6 +4682,8 @@ Set an attribute. Any of the attributes may be prefixed with .Ql no to unset. +.Ic acs +is the terminal alternate character set. .It Xo Ic align=left (or .Ic noalign ) , From 9dd58470e41bfb5b9d74028eed73f502e71152f0 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:16:25 +0000 Subject: [PATCH 0384/1006] Remove support for iTerm2's DSR 1337 extension and use the CSI > q extension now supported by a few different terminals. --- input.c | 16 +++++++-------- tmux.h | 2 +- tty-features.c | 2 +- tty-keys.c | 54 +++++++++++++++++++++++++++++--------------------- tty.c | 8 ++++---- 5 files changed, 45 insertions(+), 37 deletions(-) diff --git a/input.c b/input.c index 919cf15d..b62b1858 100644 --- a/input.c +++ b/input.c @@ -254,6 +254,7 @@ enum input_csi_type { INPUT_CSI_TBC, INPUT_CSI_VPA, INPUT_CSI_WINOPS, + INPUT_CSI_XDA, }; /* Control (CSI) command table. */ @@ -290,6 +291,7 @@ static const struct input_table_entry input_csi_table[] = { { 'm', "", INPUT_CSI_SGR }, { 'n', "", INPUT_CSI_DSR }, { 'q', " ", INPUT_CSI_DECSCUSR }, + { 'q', ">", INPUT_CSI_XDA }, { 'r', "", INPUT_CSI_DECSTBM }, { 's', "", INPUT_CSI_SCP }, { 't', "", INPUT_CSI_WINOPS }, @@ -1324,7 +1326,6 @@ input_csi_dispatch(struct input_ctx *ictx) struct input_table_entry *entry; int i, n, m; u_int cx, bg = ictx->cell.cell.bg; - char *copy, *cp; if (ictx->flags & INPUT_DISCARD) return (0); @@ -1456,13 +1457,6 @@ input_csi_dispatch(struct input_ctx *ictx) case 6: input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); break; - case 1337: /* Terminal version, from iTerm2. */ - copy = xstrdup(getversion()); - for (cp = copy; *cp != '\0'; cp++) - *cp = toupper((u_char)*cp); - input_reply(ictx, "\033[TMUX %sn", copy); - free(copy); - break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; @@ -1597,6 +1591,12 @@ input_csi_dispatch(struct input_ctx *ictx) if (n != -1) screen_set_cursor_style(s, n); break; + case INPUT_CSI_XDA: + n = input_get(ictx, 0, 0, 0); + if (n != 0) + input_reply(ictx, "\033P>|tmux %s\033\\", getversion()); + break; + } ictx->last = -1; diff --git a/tmux.h b/tmux.h index 67dc8631..c58be54f 100644 --- a/tmux.h +++ b/tmux.h @@ -1247,7 +1247,7 @@ struct tty { #define TTY_FOCUS 0x40 #define TTY_BLOCK 0x80 #define TTY_HAVEDA 0x100 -#define TTY_HAVEDSR 0x200 +#define TTY_HAVEXDA 0x200 #define TTY_SYNCING 0x400 int flags; diff --git a/tty-features.c b/tty-features.c index 1996c750..a7f2a4b0 100644 --- a/tty-features.c +++ b/tty-features.c @@ -33,7 +33,7 @@ * - alternate escape (under XT). * * Also: - * - XT is used to decide whether to send DA and DSR; + * - XT is used to decide whether to send DA and XDA; * - DECSLRM and DECFRA use a flag instead of capabilities; * - UTF-8 is a separate flag on the client; needed for unattached clients. */ diff --git a/tty-keys.c b/tty-keys.c index aa775d69..9e8428f2 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -52,7 +52,7 @@ static int tty_keys_clipboard(struct tty *, const char *, size_t, size_t *); static int tty_keys_device_attributes(struct tty *, const char *, size_t, size_t *); -static int tty_keys_device_status_report(struct tty *, const char *, +static int tty_keys_extended_device_attributes(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ @@ -612,8 +612,8 @@ tty_keys_next(struct tty *tty) goto partial_key; } - /* Is this a device status report response? */ - switch (tty_keys_device_status_report(tty, buf, len, &size)) { + /* Is this an extended device attributes response? */ + switch (tty_keys_extended_device_attributes(tty, buf, len, &size)) { case 0: /* yes */ key = KEYC_UNKNOWN; goto complete_key; @@ -936,7 +936,7 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, *size = 0; - /* First three bytes are always \033]52;. */ + /* First five bytes are always \033]52;. */ if (buf[0] != '\033') return (-1); if (len == 1) @@ -1040,9 +1040,11 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, return (1); /* Copy the rest up to a 'c'. */ - for (i = 0; i < (sizeof tmp) - 1 && buf[3 + i] != 'c'; i++) { + for (i = 0; i < (sizeof tmp) - 1; i++) { if (3 + i == len) return (1); + if (buf[3 + i] == 'c') + break; tmp[i] = buf[3 + i]; } if (i == (sizeof tmp) - 1) @@ -1101,48 +1103,54 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, } /* - * Handle device status report input. Returns 0 for success, -1 for failure, 1 - * for partial. + * Handle extended device attributes input. Returns 0 for success, -1 for + * failure, 1 for partial. */ static int -tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, - size_t *size) +tty_keys_extended_device_attributes(struct tty *tty, const char *buf, + size_t len, size_t *size) { struct client *c = tty->client; u_int i; char tmp[64]; *size = 0; - if (tty->flags & TTY_HAVEDSR) + if (tty->flags & TTY_HAVEXDA) return (-1); - /* First three bytes are always \033[. */ + /* First four bytes are always \033P>|. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); - if (buf[1] != '[') + if (buf[1] != 'P') return (-1); if (len == 2) return (1); - if (buf[2] != 'I' && buf[2] != 'T') + if (buf[2] != '>') return (-1); if (len == 3) return (1); + if (buf[3] != '|') + return (-1); + if (len == 4) + return (1); - /* Copy the rest up to a 'n'. */ - for (i = 0; i < (sizeof tmp) - 1 && buf[2 + i] != 'n'; i++) { - if (2 + i == len) + /* Copy the rest up to a '\033\\'. */ + for (i = 0; i < (sizeof tmp) - 1; i++) { + if (4 + i == len) return (1); - tmp[i] = buf[2 + i]; + if (buf[4 + i - 1] == '\033' && buf[4 + i] == '\\') + break; + tmp[i] = buf[4 + i]; } if (i == (sizeof tmp) - 1) return (-1); - tmp[i] = '\0'; - *size = 3 + i; + tmp[i - 1] = '\0'; + *size = 5 + i; /* Add terminal features. */ - if (strncmp(tmp, "ITERM2 ", 7) == 0) { + if (strncmp(tmp, "ITerm2 ", 7) == 0) { tty_add_features(&c->term_features, "256," "RGB," @@ -1152,7 +1160,7 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, "sync," "title", ","); - } else if (strncmp(tmp, "TMUX ", 5) == 0) { + } else if (strncmp(tmp, "tmux ", 5) == 0) { tty_add_features(&c->term_features, "256," "RGB," @@ -1163,10 +1171,10 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, "usstyle", ","); } - log_debug("%s: received DSR %.*s", c->name, (int)*size, buf); + log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); tty_update_features(tty); - tty->flags |= TTY_HAVEDSR; + tty->flags |= TTY_HAVEXDA; return (0); } diff --git a/tty.c b/tty.c index 98a557b5..5d7d2c0d 100644 --- a/tty.c +++ b/tty.c @@ -287,7 +287,7 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data) struct client *c = tty->client; log_debug("%s: start timer fired", c->name); - tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); + tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); } void @@ -361,10 +361,10 @@ tty_send_requests(struct tty *tty) if (tty_term_flag(tty->term, TTYC_XT)) { if (~tty->flags & TTY_HAVEDA) tty_puts(tty, "\033[>c"); - if (~tty->flags & TTY_HAVEDSR) - tty_puts(tty, "\033[1337n"); + if (~tty->flags & TTY_HAVEXDA) + tty_puts(tty, "\033[>q"); } else - tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); + tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); } void From 41dec585df8723a8e268a80930d7f9ce395829dc Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:18:39 +0000 Subject: [PATCH 0385/1006] Response is iTerm2 not not ITerm2. --- tty-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index 9e8428f2..8c778b2a 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1150,7 +1150,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, *size = 5 + i; /* Add terminal features. */ - if (strncmp(tmp, "ITerm2 ", 7) == 0) { + if (strncmp(tmp, "iTerm2 ", 7) == 0) { tty_add_features(&c->term_features, "256," "RGB," From 4e0a718666e3c24e69be107d0a294a9ae9f59388 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:22:51 +0000 Subject: [PATCH 0386/1006] Add extension terminfo(5) capabilities for margins. --- tmux.1 | 2 ++ tmux.h | 10 +++++++--- tty-features.c | 15 ++++++++++++--- tty-term.c | 8 ++++++-- tty.c | 13 +++++-------- window-copy.c | 1 + 6 files changed, 33 insertions(+), 16 deletions(-) diff --git a/tmux.1 b/tmux.1 index a519b6b5..daa0739c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5565,6 +5565,8 @@ to change the cursor colour from inside .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed +.It Em \&Cmg, \&Clmg, \&Dsmg , \&Enmg +Set, clear, disable or enable DECSLRM margins. .It Em \&Smol Enable the overline attribute. .It Em \&Smulx diff --git a/tmux.h b/tmux.h index c58be54f..97024310 100644 --- a/tmux.h +++ b/tmux.h @@ -254,6 +254,8 @@ enum tty_code_code { TTYC_BOLD, TTYC_CIVIS, TTYC_CLEAR, + TTYC_CLMG, + TTYC_CMG, TTYC_CNORM, TTYC_COLORS, TTYC_CR, @@ -274,12 +276,14 @@ enum tty_code_code { TTYC_DIM, TTYC_DL, TTYC_DL1, + TTYC_DSMG, TTYC_E3, TTYC_ECH, TTYC_ED, TTYC_EL, TTYC_EL1, TTYC_ENACS, + TTYC_ENMG, TTYC_FSL, TTYC_HOME, TTYC_HPA, @@ -445,11 +449,11 @@ enum tty_code_code { TTYC_SITM, TTYC_SMACS, TTYC_SMCUP, - TTYC_SMOL, TTYC_SMKX, + TTYC_SMOL, TTYC_SMSO, - TTYC_SMULX, TTYC_SMUL, + TTYC_SMULX, TTYC_SMXX, TTYC_SS, TTYC_SYNC, @@ -458,7 +462,7 @@ enum tty_code_code { TTYC_U8, TTYC_VPA, TTYC_XENL, - TTYC_XT, + TTYC_XT }; /* Message codes. */ diff --git a/tty-features.c b/tty-features.c index a7f2a4b0..9eb446d4 100644 --- a/tty-features.c +++ b/tty-features.c @@ -34,7 +34,7 @@ * * Also: * - XT is used to decide whether to send DA and XDA; - * - DECSLRM and DECFRA use a flag instead of capabilities; + * - DECFRA uses a flag instead of capabilities; * - UTF-8 is a separate flag on the client; needed for unattached clients. */ @@ -84,7 +84,7 @@ static const char *tty_feature_rgb_capabilities[] = { static struct tty_feature tty_feature_rgb = { "RGB", tty_feature_rgb_capabilities, - (TERM_256COLOURS|TERM_RGBCOLOURS) + TERM_256COLOURS|TERM_RGBCOLOURS }; /* Terminal supports 256 colours. */ @@ -159,9 +159,16 @@ static struct tty_feature tty_feature_sync = { }; /* Terminal supports DECSLRM margins. */ +static const char *tty_feature_margins_capabilities[] = { + "Enmg=\\E[?69h", + "Dsmg=\\E[?69l", + "Clmg=\\E[s", + "Cmg=\\E[%i%p1%d;%p2%ds", + NULL +}; static struct tty_feature tty_feature_margins = { "margins", - NULL, + tty_feature_margins_capabilities, TERM_DECSLRM }; @@ -194,6 +201,8 @@ tty_add_features(int *feat, const char *s, const char *separators) char *next, *loop, *copy; u_int i; + log_debug("%s: %s", __func__, s); + loop = copy = xstrdup(s); while ((next = strsep(&loop, separators)) != NULL) { for (i = 0; i < nitems(tty_features); i++) { diff --git a/tty-term.c b/tty-term.c index f3dbae5c..78ba5a37 100644 --- a/tty-term.c +++ b/tty-term.c @@ -61,6 +61,8 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_BOLD] = { TTYCODE_STRING, "bold" }, [TTYC_CIVIS] = { TTYCODE_STRING, "civis" }, [TTYC_CLEAR] = { TTYCODE_STRING, "clear" }, + [TTYC_CLMG] = { TTYCODE_STRING, "Clmg" }, + [TTYC_CMG] = { TTYCODE_STRING, "Cmg" }, [TTYC_CNORM] = { TTYCODE_STRING, "cnorm" }, [TTYC_COLORS] = { TTYCODE_NUMBER, "colors" }, [TTYC_CR] = { TTYCODE_STRING, "Cr" }, @@ -81,12 +83,14 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" }, [TTYC_E3] = { TTYCODE_STRING, "E3" }, [TTYC_ECH] = { TTYCODE_STRING, "ech" }, [TTYC_ED] = { TTYCODE_STRING, "ed" }, [TTYC_EL1] = { TTYCODE_STRING, "el1" }, [TTYC_EL] = { TTYCODE_STRING, "el" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, + [TTYC_ENMG] = { TTYCODE_STRING, "Enmg" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, [TTYC_HOME] = { TTYCODE_STRING, "home" }, [TTYC_HPA] = { TTYCODE_STRING, "hpa" }, @@ -237,8 +241,8 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_OP] = { TTYCODE_STRING, "op" }, [TTYC_REV] = { TTYCODE_STRING, "rev" }, [TTYC_RGB] = { TTYCODE_FLAG, "RGB" }, - [TTYC_RI] = { TTYCODE_STRING, "ri" }, [TTYC_RIN] = { TTYCODE_STRING, "rin" }, + [TTYC_RI] = { TTYCODE_STRING, "ri" }, [TTYC_RMACS] = { TTYCODE_STRING, "rmacs" }, [TTYC_RMCUP] = { TTYCODE_STRING, "rmcup" }, [TTYC_RMKX] = { TTYCODE_STRING, "rmkx" }, @@ -265,7 +269,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_U8] = { TTYCODE_NUMBER, "U8" }, [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, - [TTYC_XT] = { TTYCODE_FLAG, "XT" }, + [TTYC_XT] = { TTYCODE_FLAG, "XT" } }; u_int diff --git a/tty.c b/tty.c index 5d7d2c0d..5d84c9e8 100644 --- a/tty.c +++ b/tty.c @@ -426,7 +426,7 @@ tty_stop_tty(struct tty *tty) } if (tty_use_margin(tty)) - tty_raw(tty, "\033[?69l"); /* DECLRMM */ + tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); setblocking(tty->fd, 1); @@ -473,7 +473,7 @@ tty_update_features(struct tty *tty) tty_term_apply_overrides(tty->term); if (tty_use_margin(tty)) - tty_puts(tty, "\033[?69h"); /* DECLRMM */ + tty_putcode(tty, TTYC_ENMG); } void @@ -2028,7 +2028,7 @@ tty_invalidate(struct tty *tty) if (tty->flags & TTY_STARTED) { if (tty_use_margin(tty)) - tty_puts(tty, "\033[?69h"); /* DECLRMM */ + tty_putcode(tty, TTYC_ENMG); tty_putcode(tty, TTYC_SGR0); tty->mode = ALL_MODES; @@ -2105,8 +2105,6 @@ tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) static void tty_margin(struct tty *tty, u_int rleft, u_int rright) { - char s[64]; - if (!tty_use_margin(tty)) return; if (tty->rleft == rleft && tty->rright == rright) @@ -2118,10 +2116,9 @@ tty_margin(struct tty *tty, u_int rleft, u_int rright) tty->rright = rright; if (rleft == 0 && rright == tty->sx - 1) - snprintf(s, sizeof s, "\033[s"); + tty_putcode(tty, TTYC_CLMG); else - snprintf(s, sizeof s, "\033[%u;%us", rleft + 1, rright + 1); - tty_puts(tty, s); + tty_putcode2(tty, TTYC_CMG, rleft, rright); tty->cx = tty->cy = UINT_MAX; } diff --git a/window-copy.c b/window-copy.c index 2bda5d56..a803f3b3 100644 --- a/window-copy.c +++ b/window-copy.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "tmux.h" From a29196ca6a6401053d3a17d0ffe2d560918683a4 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:26:33 +0000 Subject: [PATCH 0387/1006] Build list of paths and weed out duplicates before loading configs, and add TMUX_SOCK like TMUX_PATH for the socket directory. --- cfg.c | 55 ++++--------------------- tmux.c | 108 ++++++++++++++++++++++++++++++++++++++++--------- tmux.h | 7 +++- tty-features.c | 2 +- 4 files changed, 105 insertions(+), 67 deletions(-) diff --git a/cfg.c b/cfg.c index f67a765c..7c01f614 100644 --- a/cfg.c +++ b/cfg.c @@ -67,45 +67,12 @@ set_cfg_file(const char *path) cfg_file = xstrdup(path); } -static char * -expand_cfg_file(const char *path, const char *home) -{ - char *expanded, *name; - const char *end; - struct environ_entry *value; - - if (strncmp(path, "~/", 2) == 0) { - if (home == NULL) - return (NULL); - xasprintf(&expanded, "%s%s", home, path + 1); - return (expanded); - } - - if (*path == '$') { - end = strchr(path, '/'); - if (end == NULL) - name = xstrdup(path + 1); - else - name = xstrndup(path + 1, end - path - 1); - value = environ_find(global_environ, name); - free(name); - if (value == NULL) - return (NULL); - if (end == NULL) - end = ""; - xasprintf(&expanded, "%s%s", value->value, end); - return (expanded); - } - - return (xstrdup(path)); -} - void start_cfg(void) { - const char *home = find_home(); - struct client *c; - char *path, *copy, *next, *expanded; + struct client *c; + char **paths; + u_int i, n; /* * Configuration files are loaded without a client, so commands are run @@ -124,18 +91,12 @@ start_cfg(void) } if (cfg_file == NULL) { - path = copy = xstrdup(TMUX_CONF); - while ((next = strsep(&path, ":")) != NULL) { - expanded = expand_cfg_file(next, home); - if (expanded == NULL) { - log_debug("couldn't expand %s", next); - continue; - } - log_debug("expanded %s to %s", next, expanded); - load_cfg(expanded, c, NULL, CMD_PARSE_QUIET, NULL); - free(expanded); + expand_paths(TMUX_CONF, &paths, &n); + for (i = 0; i < n; i++) { + load_cfg(paths[i], c, NULL, CMD_PARSE_QUIET, NULL); + free(paths[i]); } - free(copy); + free(paths); } else load_cfg(cfg_file, c, NULL, 0, NULL); diff --git a/tmux.c b/tmux.c index 515f1543..db86dc52 100644 --- a/tmux.c +++ b/tmux.c @@ -109,34 +109,104 @@ areshell(const char *shell) return (0); } +static char * +expand_path(const char *path, const char *home) +{ + char *expanded, *name; + const char *end; + struct environ_entry *value; + + if (strncmp(path, "~/", 2) == 0) { + if (home == NULL) + return (NULL); + xasprintf(&expanded, "%s%s", home, path + 1); + return (expanded); + } + + if (*path == '$') { + end = strchr(path, '/'); + if (end == NULL) + name = xstrdup(path + 1); + else + name = xstrndup(path + 1, end - path - 1); + value = environ_find(global_environ, name); + free(name); + if (value == NULL) + return (NULL); + if (end == NULL) + end = ""; + xasprintf(&expanded, "%s%s", value->value, end); + return (expanded); + } + + return (xstrdup(path)); +} + +void +expand_paths(const char *s, char ***paths, u_int *n) +{ + const char *home = find_home(); + char *copy, *next, *tmp, resolved[PATH_MAX], *expanded; + u_int i; + + *paths = NULL; + *n = 0; + + copy = tmp = xstrdup(s); + while ((next = strsep(&tmp, ":")) != NULL) { + expanded = expand_path(next, home); + if (expanded == NULL) { + log_debug("%s: invalid path: %s", __func__, next); + continue; + } + if (realpath(expanded, resolved) == NULL) { + log_debug("%s: realpath(\"%s\") failed: %s", __func__, + expanded, strerror(errno)); + free(expanded); + continue; + } + free(expanded); + for (i = 0; i < *n; i++) { + if (strcmp(resolved, (*paths)[i]) == 0) + break; + } + if (i != *n) { + log_debug("%s: duplicate path: %s", __func__, resolved); + continue; + } + *paths = xreallocarray(*paths, (*n) + 1, sizeof *paths); + (*paths)[(*n)++] = xstrdup(resolved); + } + free(copy); +} + static char * make_label(const char *label, char **cause) { - char *base, resolved[PATH_MAX], *path, *s; - struct stat sb; - uid_t uid; + char **paths, *path, *base; + u_int i, n; + struct stat sb; + uid_t uid; *cause = NULL; - if (label == NULL) label = "default"; uid = getuid(); - if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') - xasprintf(&base, "%s/tmux-%ld", s, (long)uid); - else - xasprintf(&base, "%s/tmux-%ld", _PATH_TMP, (long)uid); - if (realpath(base, resolved) == NULL && - strlcpy(resolved, base, sizeof resolved) >= sizeof resolved) { - errno = ERANGE; - free(base); - goto fail; + expand_paths(TMUX_SOCK, &paths, &n); + if (n == 0) { + xasprintf(cause, "no suitable socket path"); + return (NULL); } - free(base); + path = paths[0]; /* can only have one socket! */ + for (i = 1; i < n; i++) + free(paths[i]); + free(paths); - if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST) + xasprintf(&base, "%s/tmux-%ld", path, (long)uid); + if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) goto fail; - if (lstat(resolved, &sb) != 0) + if (lstat(base, &sb) != 0) goto fail; if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; @@ -146,11 +216,13 @@ make_label(const char *label, char **cause) errno = EACCES; goto fail; } - xasprintf(&path, "%s/%s", resolved, label); + xasprintf(&path, "%s/%s", base, label); + free(base); return (path); fail: - xasprintf(cause, "error creating %s (%s)", resolved, strerror(errno)); + xasprintf(cause, "error creating %s (%s)", base, strerror(errno)); + free(base); return (NULL); } diff --git a/tmux.h b/tmux.h index 97024310..cf8bbbf5 100644 --- a/tmux.h +++ b/tmux.h @@ -63,10 +63,13 @@ struct winlink; /* Client-server protocol version. */ #define PROTOCOL_VERSION 8 -/* Default configuration files. */ +/* Default configuration files and socket paths. */ #ifndef TMUX_CONF #define TMUX_CONF "/etc/tmux.conf:~/.tmux.conf" #endif +#ifndef TMUX_SOCK +#define TMUX_SOCK "$TMUX_TMPDIR:" _PATH_TMP +#endif /* Minimum layout cell size, NOT including border lines. */ #define PANE_MINIMUM 1 @@ -1746,6 +1749,8 @@ const char *sig2name(int); const char *find_cwd(void); const char *find_home(void); const char *getversion(void); +void expand_paths(const char *, char ***, u_int *); + /* proc.c */ struct imsg; diff --git a/tty-features.c b/tty-features.c index 9eb446d4..7505c96b 100644 --- a/tty-features.c +++ b/tty-features.c @@ -27,7 +27,7 @@ * Still hardcoded: * - bracket paste (sent if application asks for it); * - mouse (under kmous capability); - * - focus events (under focus-events option); + * - focus events (under XT and focus-events option); * - default colours (under AX or op capabilities); * - AIX colours (under colors >= 16); * - alternate escape (under XT). From 26312a7774f9752ac391017be0705e3a62dc525b Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:30:17 +0000 Subject: [PATCH 0388/1006] Move terminal features into a single file. --- tty-keys.c | 55 +++++++++++++++--------------------------------------- 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 8c778b2a..f5a3418f 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1070,28 +1070,13 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, ","); break; case 'M': /* mintty */ - tty_add_features(&c->term_features, - "256," - "RGB," - "title", - ","); + tty_default_features(&c->term_features, "mintty", 0); break; case 'T': /* tmux */ - tty_add_features(&c->term_features, - "256," - "RGB," - "ccolour," - "cstyle," - "overline," - "title," - "usstyle", - ","); + tty_default_features(&c->term_features, "tmux", 0); break; case 'U': /* rxvt-unicode */ - tty_add_features(&c->term_features, - "256," - "title", - ","); + tty_default_features(&c->term_features, "rxvt-unicode", 0); break; } log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); @@ -1112,7 +1097,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, { struct client *c = tty->client; u_int i; - char tmp[64]; + char tmp[128]; *size = 0; if (tty->flags & TTY_HAVEXDA) @@ -1150,29 +1135,19 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, *size = 5 + i; /* Add terminal features. */ - if (strncmp(tmp, "iTerm2 ", 7) == 0) { - tty_add_features(&c->term_features, - "256," - "RGB," - "clipboard," - "cstyle," - "margins," - "sync," - "title", - ","); - } else if (strncmp(tmp, "tmux ", 5) == 0) { - tty_add_features(&c->term_features, - "256," - "RGB," - "ccolour," - "cstyle," - "overline," - "title," - "usstyle", - ","); - } + if (strncmp(tmp, "iTerm2 ", 7) == 0) + tty_default_features(&c->term_features, "iTerm2", 0); + else if (strncmp(tmp, "tmux ", 5) == 0) + tty_default_features(&c->term_features, "tmux", 0); + else if (strncmp(tmp, "XTerm(", 6) == 0) + tty_default_features(&c->term_features, "xterm", 0); + else if (strncmp(tmp, "mintty ", 7) == 0) + tty_default_features(&c->term_features, "mintty", 0); log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); + free(c->term_type); + c->term_type = xstrdup(tmp); + tty_update_features(tty); tty->flags |= TTY_HAVEXDA; From aebeeec1e988bd74e273bbf6e79f8950895c38fd Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:39:40 +0000 Subject: [PATCH 0389/1006] Add feature and capabilities for focus reporting. Also document AX and XT even though they aren't tmux's, and add some bits for rxvt. --- tmux.1 | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index daa0739c..7918c780 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4463,7 +4463,8 @@ The following variables are available, where appropriate: .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_termname" Ta "" Ta "Terminal name of client" -.It Li "client_termfeatures" Ta "" Ta "Terminal features of client" +.It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" +.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" @@ -5555,6 +5556,10 @@ It is not normally necessary to set these manually, instead the .Ic terminal-features option should be used. .Bl -tag -width Ds +.It Em \&AX +An existing extension that tells +.Nm +the terminal supports default colours. .It Em \&Cs , Cr Set the cursor colour. The first takes a single string argument and is used to set the colour; @@ -5567,6 +5572,19 @@ $ printf '\e033]12;red\e033\e\e' .Ed .It Em \&Cmg, \&Clmg, \&Dsmg , \&Enmg Set, clear, disable or enable DECSLRM margins. +These are set automatically if the terminal reports it is +.Em VT420 +compatible. +.It Em \&Dsbp , \&Enbp +Disable and enable bracketed paste. +These are set automatically if the +.Em XT +capability is present. +.It Em \&Dsfcs , \&Enfcs +Disable and enable focus reporting. +These are set automatically if the +.Em XT +capability is present. .It Em \&Smol Enable the overline attribute. .It Em \&Smulx @@ -5617,6 +5635,11 @@ See the option above and the .Xr xterm 1 man page. +.It Em \&XT +This is an existing extension capability that tmux uses to mean that the +terminal supports the +.Xr xterm 1 +title set sequences and to automatically set some of the capabilities above. .El .Sh CONTROL MODE .Nm From 21a39c997b82b50b0307e836e4f11f9db6a84e55 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:42:06 +0000 Subject: [PATCH 0390/1006] Do not redraw or update mode if nothing has changed. --- screen-redraw.c | 5 +++++ server-client.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 8e74fe97..5ca6024d 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -434,7 +434,11 @@ screen_redraw_screen(struct client *c) return; flags = screen_redraw_update(c, c->flags); + if ((flags & CLIENT_ALLREDRAWFLAGS) == 0) + return; + screen_redraw_set_context(c, &ctx); + tty_update_mode(&c->tty, c->tty.mode, NULL); tty_sync_start(&c->tty); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { @@ -470,6 +474,7 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) return; screen_redraw_set_context(c, &ctx); + tty_update_mode(&c->tty, c->tty.mode, NULL); tty_sync_start(&c->tty); screen_redraw_draw_pane(&ctx, wp); diff --git a/server-client.c b/server-client.c index eb29aebb..ed138f8e 100644 --- a/server-client.c +++ b/server-client.c @@ -296,7 +296,9 @@ server_client_lost(struct client *c) if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); + free(c->term_name); + free(c->term_type); status_free(c); @@ -1780,7 +1782,6 @@ server_client_check_redraw(struct client *c) if (!redraw) continue; log_debug("%s: redrawing pane %%%u", __func__, wp->id); - tty_update_mode(tty, mode, NULL); screen_redraw_pane(c, wp); } c->redraw_panes = 0; @@ -1788,7 +1789,6 @@ server_client_check_redraw(struct client *c) } if (c->flags & CLIENT_ALLREDRAWFLAGS) { - tty_update_mode(tty, mode, NULL); if (options_get_number(s->options, "set-titles")) server_client_set_title(c); screen_redraw_screen(c); From 7dbe623156e7b0e32e10e5e6445b7b7e448cc3a2 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:46:14 +0000 Subject: [PATCH 0391/1006] Instead of having a default set of terminals in terminal-overrides that get XT added and using that as a marker for xterm(1)-like, assume that if the terminfo(5) entry already has XT or the clear capability starts with CSI then the terminal is VT100-like and it should be safe to send DA requests. The DA responses trigger additional features being added. --- options-table.c | 2 +- tty-features.c | 105 ++++++++++++++++++++++++++++++++++++++++-------- tty-term.c | 30 +++++++++----- 3 files changed, 110 insertions(+), 27 deletions(-) diff --git a/options-table.c b/options-table.c index 23e5af6f..eaa7c2a7 100644 --- a/options-table.c +++ b/options-table.c @@ -261,7 +261,7 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, - .default_str = "tmux*:XT,screen*:XT,xterm*:XT", + .default_str = "", .separator = "," }, diff --git a/tty-features.c b/tty-features.c index 7505c96b..30d3d1a0 100644 --- a/tty-features.c +++ b/tty-features.c @@ -25,15 +25,12 @@ /* * Still hardcoded: - * - bracket paste (sent if application asks for it); * - mouse (under kmous capability); - * - focus events (under XT and focus-events option); * - default colours (under AX or op capabilities); * - AIX colours (under colors >= 16); - * - alternate escape (under XT). + * - alternate escape (if terminal is VT100-like). * * Also: - * - XT is used to decide whether to send DA and XDA; * - DECFRA uses a flag instead of capabilities; * - UTF-8 is a separate flag on the client; needed for unattached clients. */ @@ -51,7 +48,7 @@ static const char *tty_feature_title_capabilities[] = { "fsl=\\a", NULL }; -static struct tty_feature tty_feature_title = { +static const struct tty_feature tty_feature_title = { "title", tty_feature_title_capabilities, 0 @@ -62,7 +59,7 @@ static const char *tty_feature_clipboard_capabilities[] = { "Ms=\\E]52;%p1%s;%p2%s\\a", NULL }; -static struct tty_feature tty_feature_clipboard = { +static const struct tty_feature tty_feature_clipboard = { "clipboard", tty_feature_clipboard_capabilities, 0 @@ -81,7 +78,7 @@ static const char *tty_feature_rgb_capabilities[] = { "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", NULL }; -static struct tty_feature tty_feature_rgb = { +static const struct tty_feature tty_feature_rgb = { "RGB", tty_feature_rgb_capabilities, TERM_256COLOURS|TERM_RGBCOLOURS @@ -94,7 +91,7 @@ static const char *tty_feature_256_capabilities[] = { "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", NULL }; -static struct tty_feature tty_feature_256 = { +static const struct tty_feature tty_feature_256 = { "256", tty_feature_256_capabilities, TERM_256COLOURS @@ -105,7 +102,7 @@ static const char *tty_feature_overline_capabilities[] = { "Smol=\\E[53m", NULL }; -static struct tty_feature tty_feature_overline = { +static const struct tty_feature tty_feature_overline = { "overline", tty_feature_overline_capabilities, 0 @@ -117,19 +114,43 @@ static const char *tty_feature_usstyle_capabilities[] = { "Setulc=\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m", NULL }; -static struct tty_feature tty_feature_usstyle = { +static const struct tty_feature tty_feature_usstyle = { "usstyle", tty_feature_usstyle_capabilities, 0 }; +/* Terminal supports bracketed paste. */ +static const char *tty_feature_bpaste_capabilities[] = { + "Enbp=\\E[?2004h", + "Dsbp=\\E[?2004l", + NULL +}; +static const struct tty_feature tty_feature_bpaste = { + "bpaste", + tty_feature_bpaste_capabilities, + 0 +}; + +/* Terminal supports focus reporting. */ +static const char *tty_feature_focus_capabilities[] = { + "Enfcs=\\E[?1004h", + "Dsfcs=\\E[?1004l", + NULL +}; +static const struct tty_feature tty_feature_focus = { + "focus", + tty_feature_focus_capabilities, + 0 +}; + /* Terminal supports cursor styles. */ static const char *tty_feature_cstyle_capabilities[] = { "Ss=\\E[%p1%d q", "Se=\\E[2 q", NULL }; -static struct tty_feature tty_feature_cstyle = { +static const struct tty_feature tty_feature_cstyle = { "cstyle", tty_feature_cstyle_capabilities, 0 @@ -141,18 +162,29 @@ static const char *tty_feature_ccolour_capabilities[] = { "Cr=\\E]112\\a", NULL }; -static struct tty_feature tty_feature_ccolour = { +static const struct tty_feature tty_feature_ccolour = { "ccolour", tty_feature_ccolour_capabilities, 0 }; +/* Terminal supports strikethrough. */ +static const char *tty_feature_strikethrough_capabilities[] = { + "smxx=\\E[9m", + NULL +}; +static const struct tty_feature tty_feature_strikethrough = { + "strikethrough", + tty_feature_strikethrough_capabilities, + 0 +}; + /* Terminal supports synchronized updates. */ static const char *tty_feature_sync_capabilities[] = { "Sync=\\EP=%p1%ds\\E\\\\", NULL }; -static struct tty_feature tty_feature_sync = { +static const struct tty_feature tty_feature_sync = { "sync", tty_feature_sync_capabilities, 0 @@ -166,14 +198,14 @@ static const char *tty_feature_margins_capabilities[] = { "Cmg=\\E[%i%p1%d;%p2%ds", NULL }; -static struct tty_feature tty_feature_margins = { +static const struct tty_feature tty_feature_margins = { "margins", tty_feature_margins_capabilities, TERM_DECSLRM }; /* Terminal supports DECFRA rectangle fill. */ -static struct tty_feature tty_feature_rectfill = { +static const struct tty_feature tty_feature_rectfill = { "rectfill", NULL, TERM_DECFRA @@ -182,13 +214,16 @@ static struct tty_feature tty_feature_rectfill = { /* Available terminal features. */ static const struct tty_feature *tty_features[] = { &tty_feature_256, - &tty_feature_clipboard, + &tty_feature_bpaste, &tty_feature_ccolour, + &tty_feature_clipboard, &tty_feature_cstyle, + &tty_feature_focus, &tty_feature_margins, &tty_feature_overline, &tty_feature_rectfill, &tty_feature_rgb, + &tty_feature_strikethrough, &tty_feature_sync, &tty_feature_title, &tty_feature_usstyle @@ -201,7 +236,7 @@ tty_add_features(int *feat, const char *s, const char *separators) char *next, *loop, *copy; u_int i; - log_debug("%s: %s", __func__, s); + log_debug("adding terminal features %s", s); loop = copy = xstrdup(s); while ((next = strsep(&loop, separators)) != NULL) { @@ -275,3 +310,39 @@ tty_apply_features(struct tty_term *term, int feat) term->features |= feat; return (1); } + +void +tty_default_features(int *feat, const char *name, u_int version) +{ + static struct { + const char *name; + u_int version; + const char *features; + } table[] = { +#define TTY_FEATURES_BASE_MODERN_XTERM "256,RGB,bpaste,clipboard,strikethrough,title" + { .name = "mintty", + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,margins,overline" + }, + { .name = "tmux", + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,overline,usstyle" + }, + { .name = "rxvt-unicode", + .features = "256,bpaste,ccolour,cstyle,title" + }, + { .name = "iTerm2", + .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,margins,sync" + }, + { .name = "XTerm", + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,margins,rectfill" + } + }; + u_int i; + + for (i = 0; i < nitems(table); i++) { + if (strcmp(table[i].name, name) != 0) + continue; + if (version != 0 && version < table[i].version) + continue; + tty_add_features(feat, table[i].features, ","); + } +} diff --git a/tty-term.c b/tty-term.c index 78ba5a37..67ca1bac 100644 --- a/tty-term.c +++ b/tty-term.c @@ -83,6 +83,8 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DSFCS] = { TTYCODE_STRING, "Dsfcs" }, + [TTYC_DSBP] = { TTYCODE_STRING, "Dsbp" }, [TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" }, [TTYC_E3] = { TTYCODE_STRING, "E3" }, [TTYC_ECH] = { TTYCODE_STRING, "ech" }, @@ -90,6 +92,8 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_EL1] = { TTYCODE_STRING, "el1" }, [TTYC_EL] = { TTYCODE_STRING, "el" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, + [TTYC_ENBP] = { TTYCODE_STRING, "Enbp" }, + [TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" }, [TTYC_ENMG] = { TTYCODE_STRING, "Enmg" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, [TTYC_HOME] = { TTYCODE_STRING, "home" }, @@ -545,21 +549,29 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) goto error; } - /* These can be emulated so one of the two is required. */ - if (!tty_term_has(term, TTYC_CUD1) && !tty_term_has(term, TTYC_CUD)) { - xasprintf(cause, "terminal does not support cud1 or cud"); - goto error; + /* + * If TERM has XT or clear starts with CSI then it is safe to assume + * the terminal is derived from the VT100. This controls whether device + * attributes requests are sent to get more information. + * + * This is a bit of a hack but there aren't that many alternatives. + * Worst case tmux will just fall back to using whatever terminfo(5) + * says without trying to correct anything that is missing. + * + * Also add few features that VT100-like terminals should either + * support or safely ignore. + */ + s = tty_term_string(term, TTYC_CLEAR); + if (tty_term_flag(term, TTYC_XT) || strncmp(s, "\033[", 2) == 0) { + term->flags |= TERM_VT100LIKE; + tty_add_features(feat, "bpaste,focus,title", ","); } /* Add RGB feature if terminal has RGB colours. */ if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) && (!tty_term_has(term, TTYC_SETRGBF) || !tty_term_has(term, TTYC_SETRGBB))) - tty_add_features(feat, "RGB", ":,"); - - /* Add feature if terminal has XT. */ - if (tty_term_flag(term, TTYC_XT)) - tty_add_features(feat, "title", ":,"); + tty_add_features(feat, "RGB", ","); /* Apply the features and overrides again. */ tty_apply_features(term, *feat); From 428137d8765f6aeb56503d8d37e3b1c9b33994ce Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:49:50 +0000 Subject: [PATCH 0392/1006] Instead of forbidding invalid session names, sanitize them like window names. --- cmd-new-session.c | 35 ++++++++++++++--------------------- cmd-rename-session.c | 12 ++++-------- session.c | 19 ++++++++++++++----- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 353f7bed..f08155c0 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -70,13 +70,14 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmdq_get_client(item); - struct session *s, *as, *groupwith; + struct session *s, *as, *groupwith = NULL; struct environ *env; struct options *oo; struct termios tio, *tiop; - struct session_group *sg; - const char *errstr, *template, *group, *prefix, *tmp; + struct session_group *sg = NULL; + const char *errstr, *template, *group, *tmp; char *cause, *cwd = NULL, *cp, *newname = NULL; + char *name, *prefix = NULL; int detached, already_attached, is_control = 0; u_int sx, sy, dsx, dsy; struct spawn_context sc; @@ -98,11 +99,9 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) tmp = args_get(args, 's'); if (tmp != NULL) { - newname = format_single(item, tmp, c, NULL, NULL, NULL); - if (!session_check_name(newname)) { - cmdq_error(item, "bad session name: %s", newname); - goto fail; - } + name = format_single(item, tmp, c, NULL, NULL, NULL); + newname = session_check_name(name); + free(name); } if (args_has(args, 'A')) { if (newname != NULL) @@ -126,24 +125,16 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) group = args_get(args, 't'); if (group != NULL) { groupwith = target->s; - if (groupwith == NULL) { - if (!session_check_name(group)) { - cmdq_error(item, "bad group name: %s", group); - goto fail; - } + if (groupwith == NULL) sg = session_group_find(group); - } else + else sg = session_group_contains(groupwith); if (sg != NULL) - prefix = sg->name; + prefix = xstrdup(sg->name); else if (groupwith != NULL) - prefix = groupwith->name; + prefix = xstrdup(groupwith->name); else - prefix = group; - } else { - groupwith = NULL; - sg = NULL; - prefix = NULL; + prefix = session_check_name(group); } /* Set -d if no client. */ @@ -353,10 +344,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) free(cwd); free(newname); + free(prefix); return (CMD_RETURN_NORMAL); fail: free(cwd); free(newname); + free(prefix); return (CMD_RETURN_ERROR); } diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 4b2c3d88..51b8ffc8 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -49,19 +49,15 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); struct session *s = target->s; - char *newname; + char *newname, *tmp; - newname = format_single_from_target(item, args->argv[0]); + tmp = format_single_from_target(item, args->argv[0]); + newname = session_check_name(tmp); + free(tmp); if (strcmp(newname, s->name) == 0) { free(newname); return (CMD_RETURN_NORMAL); } - - if (!session_check_name(newname)) { - cmdq_error(item, "bad session name: %s", newname); - free(newname); - return (CMD_RETURN_ERROR); - } if (session_find(newname) != NULL) { cmdq_error(item, "duplicate session: %s", newname); free(newname); diff --git a/session.c b/session.c index be9c8e07..93d50b47 100644 --- a/session.c +++ b/session.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "tmux.h" @@ -123,7 +124,6 @@ session_create(const char *prefix, const char *name, const char *cwd, s->cwd = xstrdup(cwd); - s->curw = NULL; TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); @@ -142,7 +142,6 @@ session_create(const char *prefix, const char *name, const char *cwd, s->name = xstrdup(name); s->id = next_session_id++; } else { - s->name = NULL; do { s->id = next_session_id++; free(s->name); @@ -232,11 +231,20 @@ session_destroy(struct session *s, int notify, const char *from) session_remove_ref(s, __func__); } -/* Check a session name is valid: not empty and no colons or periods. */ -int +/* Sanitize session name. */ +char * session_check_name(const char *name) { - return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); + char *copy, *cp, *new_name; + + copy = xstrdup(name); + for (cp = copy; *cp != '\0'; cp++) { + if (*cp == ':' || *cp == '.') + *cp = '_'; + } + utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); + free(copy); + return (new_name); } /* Lock session if it has timed out. */ @@ -556,6 +564,7 @@ session_group_remove(struct session *s) TAILQ_REMOVE(&sg->sessions, s, gentry); if (TAILQ_EMPTY(&sg->sessions)) { RB_REMOVE(session_groups, &session_groups, sg); + free((void *)sg->name); free(sg); } } From 5bf96c2f2c40e93b8e66d7100f7b3dc9074a1ca6 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:53:23 +0000 Subject: [PATCH 0393/1006] Use a grid cell not a style for the pane style. --- grid.c | 23 ++++++++++++++++------- menu.c | 4 ++-- style.c | 29 ++-------------------------- tmux.h | 24 +++++++++++++++++++----- tty.c | 57 ++++++++++++++++++++++++-------------------------------- window.c | 18 +++++++++--------- 6 files changed, 72 insertions(+), 83 deletions(-) diff --git a/grid.c b/grid.c index 0cef412b..81f3709c 100644 --- a/grid.c +++ b/grid.c @@ -211,19 +211,28 @@ grid_check_y(struct grid *gd, const char *from, u_int py) return (0); } +/* Check if two styles are (visibly) the same. */ +int +grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + if (gc1->fg != gc2->fg || gc1->bg != gc2->bg) + return (0); + if (gc1->attr != gc2->attr || gc1->flags != gc2->flags) + return (0); + return (1); +} + /* Compare grid cells. Return 1 if equal, 0 if not. */ int -grid_cells_equal(const struct grid_cell *gca, const struct grid_cell *gcb) +grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) { - if (gca->fg != gcb->fg || gca->bg != gcb->bg) + if (!grid_cells_look_equal(gc1, gc2)) return (0); - if (gca->attr != gcb->attr || gca->flags != gcb->flags) + if (gc1->data.width != gc2->data.width) return (0); - if (gca->data.width != gcb->data.width) + if (gc1->data.size != gc2->data.size) return (0); - if (gca->data.size != gcb->data.size) - return (0); - return (memcmp(gca->data.data, gcb->data.data, gca->data.size) == 0); + return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0); } /* Free one line. */ diff --git a/menu.c b/menu.c index 39cb50c7..fd744e7d 100644 --- a/menu.c +++ b/menu.c @@ -73,7 +73,7 @@ menu_add_item(struct menu *menu, const struct menu_item *item, return; if (fs != NULL) - s = format_single(qitem, item->name, c, fs->s, fs->wl, fs->wp); + s = format_single_from_state(qitem, item->name, c, fs); else s = format_single(qitem, item->name, c, NULL, NULL, NULL); if (*s == '\0') { /* no item if empty after format expanded */ @@ -91,7 +91,7 @@ menu_add_item(struct menu *menu, const struct menu_item *item, cmd = item->command; if (cmd != NULL) { if (fs != NULL) - s = format_single(qitem, cmd, c, fs->s, fs->wl, fs->wp); + s = format_single_from_state(qitem, cmd, c, fs); else s = format_single(qitem, cmd, c, NULL, NULL, NULL); } else diff --git a/style.c b/style.c index da3b4c78..2ac78e97 100644 --- a/style.c +++ b/style.c @@ -59,6 +59,7 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) return (0); style_copy(&saved, sy); + log_debug("%s: %s", __func__, in); do { while (*in != '\0' && strchr(delimiters, *in) != NULL) in++; @@ -71,6 +72,7 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) memcpy(tmp, in, end); tmp[end] = '\0'; + log_debug("%s: %s", __func__, tmp); if (strcasecmp(tmp, "default") == 0) { sy->gc.fg = base->fg; sy->gc.bg = base->bg; @@ -285,30 +287,3 @@ style_copy(struct style *dst, struct style *src) { memcpy(dst, src, sizeof *dst); } - -/* Check if two styles are (visibly) the same. */ -int -style_equal(struct style *sy1, struct style *sy2) -{ - struct grid_cell *gc1 = &sy1->gc; - struct grid_cell *gc2 = &sy2->gc; - - if (gc1->fg != gc2->fg) - return (0); - if (gc1->bg != gc2->bg) - return (0); - if ((gc1->attr & STYLE_ATTR_MASK) != (gc2->attr & STYLE_ATTR_MASK)) - return (0); - if (sy1->fill != sy2->fill) - return (0); - if (sy1->align != sy2->align) - return (0); - return (1); -} - -/* Is this style default? */ -int -style_is_default(struct style *sy) -{ - return (style_equal(sy, &style_default)); -} diff --git a/tmux.h b/tmux.h index cf8bbbf5..4e19c4ef 100644 --- a/tmux.h +++ b/tmux.h @@ -279,6 +279,8 @@ enum tty_code_code { TTYC_DIM, TTYC_DL, TTYC_DL1, + TTYC_DSBP, + TTYC_DSFCS, TTYC_DSMG, TTYC_E3, TTYC_ECH, @@ -286,6 +288,8 @@ enum tty_code_code { TTYC_EL, TTYC_EL1, TTYC_ENACS, + TTYC_ENBP, + TTYC_ENFCS, TTYC_ENMG, TTYC_FSL, TTYC_HOME, @@ -924,8 +928,8 @@ struct window_pane { struct input_ctx *ictx; - struct style cached_style; - struct style cached_active_style; + struct grid_cell cached_gc; + struct grid_cell cached_active_gc; int *palette; int pipe_fd; @@ -1196,6 +1200,7 @@ struct tty_term { #define TERM_DECSLRM 0x4 #define TERM_DECFRA 0x8 #define TERM_RGBCOLOURS 0x10 +#define TERM_VT100LIKE 0x20 int flags; LIST_ENTRY(tty_term) entry; @@ -1504,6 +1509,7 @@ struct client { char *term_name; int term_features; + char *term_type; char *ttyname; struct tty tty; @@ -1819,7 +1825,14 @@ char *format_expand(struct format_tree *, const char *); char *format_single(struct cmdq_item *, const char *, struct client *, struct session *, struct winlink *, struct window_pane *); +char *format_single_from_state(struct cmdq_item *, const char *, + struct client *, struct cmd_find_state *); char *format_single_from_target(struct cmdq_item *, const char *); +struct format_tree *format_create_defaults(struct cmdq_item *, struct client *, + struct session *, struct winlink *, struct window_pane *); +struct format_tree *format_create_from_state(struct cmdq_item *, + struct client *, struct cmd_find_state *); +struct format_tree *format_create_from_target(struct cmdq_item *); void format_defaults(struct format_tree *, struct client *, struct session *, struct winlink *, struct window_pane *); void format_defaults_window(struct format_tree *, struct window *); @@ -2028,6 +2041,7 @@ const char *tty_term_describe(struct tty_term *, enum tty_code_code); void tty_add_features(int *, const char *, const char *); const char *tty_get_features(int); int tty_apply_features(struct tty_term *, int); +void tty_default_features(int *, const char *, u_int); /* tty-acs.c */ int tty_acs_needed(struct tty *); @@ -2349,6 +2363,8 @@ int attributes_fromstring(const char *); extern const struct grid_cell grid_default_cell; void grid_empty_line(struct grid *, u_int, u_int); int grid_cells_equal(const struct grid_cell *, const struct grid_cell *); +int grid_cells_look_equal(const struct grid_cell *, + const struct grid_cell *); struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); int grid_compare(struct grid *, struct grid *); @@ -2713,7 +2729,7 @@ struct session *session_create(const char *, const char *, const char *, void session_destroy(struct session *, int, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); -int session_check_name(const char *); +char *session_check_name(const char *); void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); @@ -2804,10 +2820,8 @@ int style_parse(struct style *,const struct grid_cell *, const char *style_tostring(struct style *); void style_apply(struct grid_cell *, struct options *, const char *); -int style_equal(struct style *, struct style *); void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); -int style_is_default(struct style *); /* spawn.c */ struct winlink *spawn_window(struct spawn_context *, char **); diff --git a/tty.c b/tty.c index 5d84c9e8..99b433ae 100644 --- a/tty.c +++ b/tty.c @@ -330,13 +330,12 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1006l\033[?1005l"); } - if (tty_term_flag(tty->term, TTYC_XT)) { - if (options_get_number(global_options, "focus-events")) { - tty->flags |= TTY_FOCUS; - tty_puts(tty, "\033[?1004h"); - } - tty_puts(tty, "\033[?7727h"); + if (options_get_number(global_options, "focus-events")) { + tty->flags |= TTY_FOCUS; + tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); } + if (tty->term->flags & TERM_VT100LIKE) + tty_puts(tty, "\033[?7727h"); evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); @@ -358,7 +357,7 @@ tty_send_requests(struct tty *tty) if (~tty->flags & TTY_STARTED) return; - if (tty_term_flag(tty->term, TTYC_XT)) { + if (tty->term->flags & TERM_VT100LIKE) { if (~tty->flags & TTY_HAVEDA) tty_puts(tty, "\033[>c"); if (~tty->flags & TTY_HAVEXDA) @@ -407,7 +406,7 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); } if (tty->mode & MODE_BRACKETPASTE) - tty_raw(tty, "\033[?2004l"); + tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP)); if (*tty->ccolour != '\0') tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); @@ -417,13 +416,12 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, "\033[?1006l\033[?1005l"); } - if (tty_term_flag(tty->term, TTYC_XT)) { - if (tty->flags & TTY_FOCUS) { - tty->flags &= ~TTY_FOCUS; - tty_raw(tty, "\033[?1004l"); - } - tty_raw(tty, "\033[?7727l"); + if (tty->flags & TTY_FOCUS) { + tty->flags &= ~TTY_FOCUS; + tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); } + if (tty->term->flags & TERM_VT100LIKE) + tty_raw(tty, "\033[?7727l"); if (tty_use_margin(tty)) tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); @@ -676,7 +674,8 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; - log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); + if (changed != 0) + log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); if (changed & MODE_BLINKING) { if (tty_term_has(tty->term, TTYC_CVVIS)) @@ -729,9 +728,9 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } if (changed & MODE_BRACKETPASTE) { if (mode & MODE_BRACKETPASTE) - tty_puts(tty, "\033[?2004h"); + tty_putcode(tty, TTYC_ENBP); else - tty_puts(tty, "\033[?2004l"); + tty_putcode(tty, TTYC_DSBP); } tty->mode = mode; } @@ -2691,27 +2690,19 @@ static void tty_default_colours(struct grid_cell *gc, struct window_pane *wp) { struct options *oo = wp->options; - struct style *style, *active_style; int c; if (wp->flags & PANE_STYLECHANGED) { wp->flags &= ~PANE_STYLECHANGED; - - active_style = options_get_style(oo, "window-active-style"); - style = options_get_style(oo, "window-style"); - - style_copy(&wp->cached_active_style, active_style); - style_copy(&wp->cached_style, style); - } else { - active_style = &wp->cached_active_style; - style = &wp->cached_style; + style_apply(&wp->cached_active_gc, oo, "window-active-style"); + style_apply(&wp->cached_gc, oo, "window-style"); } if (gc->fg == 8) { - if (wp == wp->window->active && active_style->gc.fg != 8) - gc->fg = active_style->gc.fg; + if (wp == wp->window->active && wp->cached_active_gc.fg != 8) + gc->fg = wp->cached_active_gc.fg; else - gc->fg = style->gc.fg; + gc->fg = wp->cached_gc.fg; if (gc->fg != 8) { c = window_pane_get_palette(wp, gc->fg); @@ -2721,10 +2712,10 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) } if (gc->bg == 8) { - if (wp == wp->window->active && active_style->gc.bg != 8) - gc->bg = active_style->gc.bg; + if (wp == wp->window->active && wp->cached_active_gc.bg != 8) + gc->bg = wp->cached_active_gc.bg; else - gc->bg = style->gc.bg; + gc->bg = wp->cached_gc.bg; if (gc->bg != 8) { c = window_pane_get_palette(wp, gc->bg); diff --git a/window.c b/window.c index b0194eaf..fbc4bb19 100644 --- a/window.c +++ b/window.c @@ -488,8 +488,8 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) void window_redraw_active_switch(struct window *w, struct window_pane *wp) { - struct style *sy1, *sy2; - int c1, c2; + struct grid_cell *gc1, *gc2; + int c1, c2; if (wp == w->active) return; @@ -499,18 +499,18 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp) * If the active and inactive styles or palettes are different, * need to redraw the panes. */ - sy1 = &wp->cached_style; - sy2 = &wp->cached_active_style; - if (!style_equal(sy1, sy2)) + gc1 = &wp->cached_gc; + gc2 = &wp->cached_active_gc; + if (!grid_cells_look_equal(gc1, gc2)) wp->flags |= PANE_REDRAW; else { - c1 = window_pane_get_palette(wp, sy1->gc.fg); - c2 = window_pane_get_palette(wp, sy2->gc.fg); + c1 = window_pane_get_palette(wp, gc1->fg); + c2 = window_pane_get_palette(wp, gc2->fg); if (c1 != c2) wp->flags |= PANE_REDRAW; else { - c1 = window_pane_get_palette(wp, sy1->gc.bg); - c2 = window_pane_get_palette(wp, sy2->gc.bg); + c1 = window_pane_get_palette(wp, gc1->bg); + c2 = window_pane_get_palette(wp, gc2->bg); if (c1 != c2) wp->flags |= PANE_REDRAW; } From 0487029fc5f39d49e37c8821f4427565b9969671 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 14:55:38 +0000 Subject: [PATCH 0394/1006] Call format_defaults_window for panes as well. --- format.c | 62 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/format.c b/format.c index c43baf05..a2b2822f 100644 --- a/format.c +++ b/format.c @@ -2479,25 +2479,59 @@ format_single(struct cmdq_item *item, const char *fmt, struct client *c, struct format_tree *ft; char *expanded; - if (item != NULL) - ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); - else - ft = format_create(NULL, item, FORMAT_NONE, 0); - format_defaults(ft, c, s, wl, wp); - + ft = format_create_defaults(item, c, s, wl, wp); expanded = format_expand(ft, fmt); format_free(ft); return (expanded); } +/* Expand a single string using state. */ +char * +format_single_from_state(struct cmdq_item *item, const char *fmt, + struct client *c, struct cmd_find_state *fs) +{ + return (format_single(item, fmt, c, fs->s, fs->wl, fs->wp)); +} + /* Expand a single string using target. */ char * format_single_from_target(struct cmdq_item *item, const char *fmt) { - struct cmd_find_state *target = cmdq_get_target(item); - struct client *tc = cmdq_get_target_client(item); + struct client *tc = cmdq_get_target_client(item); - return (format_single(item, fmt, tc, target->s, target->wl, target->wp)); + return (format_single_from_state(item, fmt, tc, cmdq_get_target(item))); +} + +/* Create and add defaults. */ +struct format_tree * +format_create_defaults(struct cmdq_item *item, struct client *c, + struct session *s, struct winlink *wl, struct window_pane *wp) +{ + struct format_tree *ft; + + if (item != NULL) + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); + else + ft = format_create(NULL, item, FORMAT_NONE, 0); + format_defaults(ft, c, s, wl, wp); + return (ft); +} + +/* Create and add defaults using state. */ +struct format_tree * +format_create_from_state(struct cmdq_item *item, struct client *c, + struct cmd_find_state *fs) +{ + return (format_create_defaults(item, c, fs->s, fs->wl, fs->wp)); +} + +/* Create and add defaults using target. */ +struct format_tree * +format_create_from_target(struct cmdq_item *item) +{ + struct client *tc = cmdq_get_target_client(item); + + return (format_create_from_state(item, tc, cmdq_get_target(item))); } /* Set defaults for any of arguments that are not NULL. */ @@ -2516,7 +2550,7 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s, else log_debug("%s: s=none", __func__); if (wl != NULL) - log_debug("%s: wl=%u w=@%u", __func__, wl->idx, wl->window->id); + log_debug("%s: wl=%u", __func__, wl->idx); else log_debug("%s: wl=none", __func__); if (wp != NULL) @@ -2624,6 +2658,8 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_termname", "%s", c->term_name); format_add(ft, "client_termfeatures", "%s", tty_get_features(c->term_features)); + if (c->term_type != NULL) + format_add(ft, "client_termtype", "%s", c->term_type); format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); @@ -2688,11 +2724,9 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl) u_int ox, oy, sx, sy; if (ft->w == NULL) - ft->w = wl->window; + format_defaults_window(ft, w); ft->wl = wl; - format_defaults_window(ft, w); - if (c != NULL) { flag = tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); format_add(ft, "window_bigger", "%d", flag); @@ -2752,7 +2786,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) struct window_mode_entry *wme; if (ft->w == NULL) - ft->w = w; + format_defaults_window(ft, w); ft->wp = wp; format_add(ft, "history_size", "%u", gd->hsize); From f03b61131b3407929fea187a309fb336017791d1 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:01:30 +0000 Subject: [PATCH 0395/1006] Drop having a separate type for style options and make them all strings, which allows formats to be expanded. Any styles without a '#{' are still validated when they are set but any with a '#{' are not. Formats are not expanded usefully in many cases yet, that will be changed later. To make this work, a few other changes: - set-option -a with a style option automatically appends a ",". - OSC 10 and 11 don't set the window-style option anymore, instead the fg and bg are stored in the pane struct and act as the defaults that can be overridden by window-style. - status-fg and -bg now override status-style instead of trying to keep them in sync. --- cmd-rename-window.c | 1 + cmd-resize-pane.c | 1 - cmd-respawn-pane.c | 1 + cmd-select-pane.c | 32 ++-- cmd-set-option.c | 57 +++--- input.c | 29 +-- menu.c | 3 +- mode-tree.c | 2 +- names.c | 1 + options-table.c | 94 ++++++---- options.c | 98 +++++----- screen-redraw.c | 439 ++++++++++++++++++++++++++------------------ status.c | 16 +- style.c | 34 +++- tmux.h | 31 ++-- tty.c | 16 +- window-copy.c | 4 +- window.c | 5 + 18 files changed, 514 insertions(+), 350 deletions(-) diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 1fb17ad9..593e0b9e 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -54,6 +54,7 @@ cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item) window_set_name(wl->window, newname); options_set_number(wl->window->options, "automatic-rename", 0); + server_redraw_window_borders(wl->window); server_status_window(wl->window); free(newname); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 105f48e0..563c95fb 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -91,7 +91,6 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) else window_zoom(wp); server_redraw_window(w); - server_status_window(w); return (CMD_RETURN_NORMAL); } server_unzoom_window(w); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 498a89a7..9db280b4 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -90,6 +90,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) } wp->flags |= PANE_REDRAW; + server_redraw_window_borders(wp->window); server_status_window(wp->window); environ_free(sc.environ); diff --git a/cmd-select-pane.c b/cmd-select-pane.c index db110ff9..224370ab 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -91,9 +91,9 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) struct window *w = wl->window; struct session *s = target->s; struct window_pane *wp = target->wp, *lastwp, *markedwp; + struct options *oo = wp->options; char *title; const char *style; - struct style *sy; struct options_entry *o; if (entry == &cmd_last_pane_entry || args_has(args, 'l')) { @@ -147,22 +147,18 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'P') || args_has(args, 'g')) { - if ((style = args_get(args, 'P')) != NULL) { - o = options_set_style(wp->options, "window-style", 0, - style); - if (o == NULL) { - cmdq_error(item, "bad style: %s", style); - return (CMD_RETURN_ERROR); - } - options_set_style(wp->options, "window-active-style", 0, - style); - wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); - } - if (args_has(args, 'g')) { - sy = options_get_style(wp->options, "window-style"); - cmdq_print(item, "%s", style_tostring(sy)); + style = args_get(args, 'P'); + if (style != NULL) { + o = options_set_string(oo, "window-style", 0, "%s", style); + if (o == NULL) { + cmdq_error(item, "bad style: %s", style); + return (CMD_RETURN_ERROR); } + options_set_string(oo, "window-active-style", 0, "%s", style); + wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); + } + if (args_has(args, 'g')) { + cmdq_print(item, "%s", options_get_string(oo, "window-style")); return (CMD_RETURN_NORMAL); } @@ -197,8 +193,10 @@ 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)) + if (screen_set_title(&wp->base, title)) { + server_redraw_window_borders(wp->window); server_status_window(wp->window); + } free(title); return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-option.c b/cmd-set-option.c index 1752d093..e04aa7ff 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -93,7 +93,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) char *name, *argument, *value = NULL, *cause; int window, idx, already, error, ambiguous; int scope; - struct style *sy; window = (cmd_get_entry(self) == &cmd_set_window_option_entry); @@ -232,16 +231,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) tty_keys_build(&loop->tty); } } - if (strcmp(name, "status-fg") == 0 || strcmp(name, "status-bg") == 0) { - sy = options_get_style(oo, "status-style"); - sy->gc.fg = options_get_number(oo, "status-fg"); - sy->gc.bg = options_get_number(oo, "status-bg"); - } - if (strcmp(name, "status-style") == 0) { - sy = options_get_style(oo, "status-style"); - options_set_number(oo, "status-fg", sy->gc.fg); - options_set_number(oo, "status-bg", sy->gc.bg); - } if (strcmp(name, "status") == 0 || strcmp(name, "status-interval") == 0) status_timer_start_all(); @@ -282,6 +271,29 @@ fail: return (CMD_RETURN_ERROR); } +static int +cmd_set_option_check_string(const struct options_table_entry *oe, + const char *value, char **cause) +{ + struct style sy; + + if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) { + xasprintf(cause, "not a suitable shell: %s", value); + return (-1); + } + if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) { + xasprintf(cause, "value is invalid: %s", value); + return (-1); + } + if ((oe->flags & OPTIONS_TABLE_IS_STYLE) && + strstr(value, "#{") == NULL && + style_parse(&sy, &grid_default_cell, value) != 0) { + xasprintf(cause, "invalid style: %s", value); + return (-1); + } + return (0); +} + static int cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, struct options_entry *parent, const char *value) @@ -289,10 +301,9 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, const struct options_table_entry *oe; struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); - struct options_entry *o; long long number; const char *errstr, *new; - char *old; + char *old, *cause; key_code key; oe = options_table_entry(parent); @@ -308,17 +319,12 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, old = xstrdup(options_get_string(oo, oe->name)); options_set_string(oo, oe->name, append, "%s", value); new = options_get_string(oo, oe->name); - if (strcmp(oe->name, "default-shell") == 0 && - !checkshell(new)) { + if (cmd_set_option_check_string(oe, new, &cause) != 0) { + cmdq_error(item, "%s", cause); + free(cause); + options_set_string(oo, oe->name, 0, "%s", old); free(old); - cmdq_error(item, "not a suitable shell: %s", value); - return (-1); - } - if (oe->pattern != NULL && fnmatch(oe->pattern, new, 0) != 0) { - options_set_string(oo, oe->name, 0, "%s", old); - free(old); - cmdq_error(item, "value is invalid: %s", value); return (-1); } free(old); @@ -350,13 +356,6 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, return (cmd_set_option_flag(item, oe, oo, value)); case OPTIONS_TABLE_CHOICE: return (cmd_set_option_choice(item, oe, oo, value)); - case OPTIONS_TABLE_STYLE: - o = options_set_style(oo, oe->name, append, value); - if (o == NULL) { - cmdq_error(item, "bad style: %s", value); - return (-1); - } - return (0); case OPTIONS_TABLE_COMMAND: break; } diff --git a/input.c b/input.c index b62b1858..8485a17a 100644 --- a/input.c +++ b/input.c @@ -1861,8 +1861,10 @@ input_csi_dispatch_winops(struct input_ctx *ictx) case 0: case 2: screen_pop_title(sctx->s); - if (wp != NULL) + if (wp != NULL) { + server_redraw_window_borders(wp->window); server_status_window(wp->window); + } break; } break; @@ -2253,8 +2255,10 @@ input_exit_osc(struct input_ctx *ictx) switch (option) { case 0: case 2: - if (screen_set_title(sctx->s, p) && wp != NULL) - server_status_window(ictx->wp->window); + if (screen_set_title(sctx->s, p) && wp != NULL) { + server_redraw_window_borders(wp->window); + server_status_window(wp->window); + } break; case 4: input_osc_4(ictx, p); @@ -2262,8 +2266,10 @@ input_exit_osc(struct input_ctx *ictx) case 7: if (utf8_isvalid(p)) { screen_set_path(sctx->s, p); - if (wp != NULL) + if (wp != NULL) { + server_redraw_window_borders(wp->window); server_status_window(wp->window); + } } break; case 10: @@ -2314,8 +2320,10 @@ input_exit_apc(struct input_ctx *ictx) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); - if (screen_set_title(sctx->s, ictx->input_buf) && wp != NULL) + if (screen_set_title(sctx->s, ictx->input_buf) && wp != NULL) { + server_redraw_window_borders(wp->window); server_status_window(wp->window); + } } /* Rename string started. */ @@ -2355,6 +2363,7 @@ input_exit_rename(struct input_ctx *ictx) } window_set_name(wp->window, ictx->input_buf); options_set_number(wp->window->options, "automatic-rename", 0); + server_redraw_window_borders(wp->window); server_status_window(wp->window); } @@ -2486,7 +2495,6 @@ input_osc_10(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; u_int r, g, b; - char tmp[16]; if (wp == NULL) return; @@ -2495,9 +2503,7 @@ input_osc_10(struct input_ctx *ictx, const char *p) if (!input_osc_parse_colour(p, &r, &g, &b)) goto bad; - xsnprintf(tmp, sizeof tmp, "fg=#%02x%02x%02x", r, g, b); - options_set_style(wp->options, "window-style", 1, tmp); - options_set_style(wp->options, "window-active-style", 1, tmp); + wp->fg = colour_join_rgb(r, g, b); wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; @@ -2512,7 +2518,6 @@ input_osc_11(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; u_int r, g, b; - char tmp[16]; if (wp == NULL) return; @@ -2521,9 +2526,7 @@ input_osc_11(struct input_ctx *ictx, const char *p) if (!input_osc_parse_colour(p, &r, &g, &b)) goto bad; - xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x", r, g, b); - options_set_style(wp->options, "window-style", 1, tmp); - options_set_style(wp->options, "window-active-style", 1, tmp); + wp->bg = colour_join_rgb(r, g, b); wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; diff --git a/menu.c b/menu.c index fd744e7d..2f92af34 100644 --- a/menu.c +++ b/menu.c @@ -151,8 +151,7 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) u_int i, px = md->px, py = md->py; struct grid_cell gc; - memcpy(&gc, &grid_default_cell, sizeof gc); - style_apply(&gc, c->session->curw->window->options, "mode-style"); + style_apply(&gc, c->session->curw->window->options, "mode-style", NULL); screen_write_start(&ctx, NULL, s); screen_write_clearscreen(&ctx, 8); diff --git a/mode-tree.c b/mode-tree.c index 783ffcfa..645e2ae9 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -557,7 +557,7 @@ mode_tree_draw(struct mode_tree_data *mtd) memcpy(&gc0, &grid_default_cell, sizeof gc0); memcpy(&gc, &grid_default_cell, sizeof gc); - style_apply(&gc, oo, "mode-style"); + style_apply(&gc, oo, "mode-style", NULL); w = mtd->width; h = mtd->height; diff --git a/names.c b/names.c index 661ba06e..07c689d1 100644 --- a/names.c +++ b/names.c @@ -96,6 +96,7 @@ check_window_name(struct window *w) if (strcmp(name, w->name) != 0) { log_debug("@%u new name %s (was %s)", w->id, name, w->name); window_set_name(w, name); + server_redraw_window_borders(w); server_status_window(w); } else log_debug("@%u name not changed (still %s)", w->id, w->name); diff --git a/options-table.c b/options-table.c index eaa7c2a7..5c368586 100644 --- a/options-table.c +++ b/options-table.c @@ -401,15 +401,19 @@ const struct options_table_entry options_table[] = { }, { .name = "message-command-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "bg=black,fg=yellow" + .default_str = "bg=black,fg=yellow", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "message-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "bg=yellow,fg=black" + .default_str = "bg=yellow,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "mouse", @@ -473,13 +477,13 @@ const struct options_table_entry options_table[] = { { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, - .default_num = 2, + .default_num = 8, }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, - .default_num = 0, + .default_num = 8, }, { .name = "status-format", @@ -526,9 +530,11 @@ const struct options_table_entry options_table[] = { }, { .name = "status-left-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "status-position", @@ -555,15 +561,19 @@ const struct options_table_entry options_table[] = { }, { .name = "status-right-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "status-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "bg=green,fg=black" + .default_str = "bg=green,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "update-environment", @@ -666,9 +676,11 @@ const struct options_table_entry options_table[] = { }, { .name = "mode-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "bg=yellow,fg=black" + .default_str = "bg=yellow,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "monitor-activity", @@ -704,9 +716,11 @@ const struct options_table_entry options_table[] = { }, { .name = "pane-active-border-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "fg=green" + .default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "pane-base-index", @@ -732,9 +746,11 @@ const struct options_table_entry options_table[] = { }, { .name = "pane-border-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "remain-on-exit", @@ -750,9 +766,11 @@ const struct options_table_entry options_table[] = { }, { .name = "window-active-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-size", @@ -763,21 +781,27 @@ const struct options_table_entry options_table[] = { }, { .name = "window-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-status-activity-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "reverse" + .default_str = "reverse", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-status-bell-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "reverse" + .default_str = "reverse", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-status-current-format", @@ -787,9 +811,11 @@ const struct options_table_entry options_table[] = { }, { .name = "window-status-current-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-status-format", @@ -799,9 +825,11 @@ const struct options_table_entry options_table[] = { }, { .name = "window-status-last-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "window-status-separator", @@ -811,9 +839,11 @@ const struct options_table_entry options_table[] = { }, { .name = "window-status-style", - .type = OPTIONS_TABLE_STYLE, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "default" + .default_str = "default", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," }, { .name = "wrap-search", diff --git a/options.c b/options.c index 7402b724..39a0d08f 100644 --- a/options.c +++ b/options.c @@ -53,6 +53,9 @@ struct options_entry { const struct options_table_entry *tableentry; union options_value value; + int cached; + struct style style; + RB_ENTRY(options_entry) entry; }; @@ -73,9 +76,6 @@ static struct options_entry *options_add(struct options *, const char *); (o)->tableentry->type == OPTIONS_TABLE_COLOUR || \ (o)->tableentry->type == OPTIONS_TABLE_FLAG || \ (o)->tableentry->type == OPTIONS_TABLE_CHOICE)) -#define OPTIONS_IS_STYLE(o) \ - ((o)->tableentry != NULL && \ - (o)->tableentry->type == OPTIONS_TABLE_STYLE) #define OPTIONS_IS_COMMAND(o) \ ((o)->tableentry != NULL && \ (o)->tableentry->type == OPTIONS_TABLE_COMMAND) @@ -123,8 +123,6 @@ options_value_tostring(struct options_entry *o, union options_value *ov, if (OPTIONS_IS_COMMAND(o)) return (cmd_list_print(ov->cmdlist, 0)); - if (OPTIONS_IS_STYLE(o)) - return (xstrdup(style_tostring(&ov->style))); if (OPTIONS_IS_NUMBER(o)) { switch (o->tableentry->type) { case OPTIONS_TABLE_NUMBER: @@ -146,7 +144,6 @@ options_value_tostring(struct options_entry *o, union options_value *ov, s = xstrdup(o->tableentry->choices[ov->number]); break; case OPTIONS_TABLE_STRING: - case OPTIONS_TABLE_STYLE: case OPTIONS_TABLE_COMMAND: fatalx("not a number option type"); } @@ -258,10 +255,6 @@ options_default(struct options *oo, const struct options_table_entry *oe) case OPTIONS_TABLE_STRING: ov->string = xstrdup(oe->default_str); break; - case OPTIONS_TABLE_STYLE: - style_set(&ov->style, &grid_default_cell); - style_parse(&ov->style, &grid_default_cell, oe->default_str); - break; default: ov->number = oe->default_num; break; @@ -653,25 +646,13 @@ options_get_number(struct options *oo, const char *name) return (o->value.number); } -struct style * -options_get_style(struct options *oo, const char *name) -{ - struct options_entry *o; - - o = options_get(oo, name); - if (o == NULL) - fatalx("missing option %s", name); - if (!OPTIONS_IS_STYLE(o)) - fatalx("option %s is not a style", name); - return (&o->value.style); -} - struct options_entry * options_set_string(struct options *oo, const char *name, int append, const char *fmt, ...) { struct options_entry *o; va_list ap; + const char *separator = ""; char *s, *value; va_start(ap, fmt); @@ -680,7 +661,12 @@ options_set_string(struct options *oo, const char *name, int append, o = options_get_only(oo, name); if (o != NULL && append && OPTIONS_IS_STRING(o)) { - xasprintf(&value, "%s%s", o->value.string, s); + if (*name != '@') { + separator = o->tableentry->separator; + if (separator == NULL) + separator = ""; + } + xasprintf(&value, "%s%s%s", o->value.string, separator, s); free(s); } else value = s; @@ -696,6 +682,7 @@ options_set_string(struct options *oo, const char *name, int append, fatalx("option %s is not a string", name); free(o->value.string); o->value.string = value; + o->cached = 0; return (o); } @@ -720,35 +707,6 @@ options_set_number(struct options *oo, const char *name, long long value) return (o); } -struct options_entry * -options_set_style(struct options *oo, const char *name, int append, - const char *value) -{ - struct options_entry *o; - struct style sy; - - if (*name == '@') - fatalx("user option %s must be a string", name); - - o = options_get_only(oo, name); - if (o != NULL && append && OPTIONS_IS_STYLE(o)) - style_copy(&sy, &o->value.style); - else - style_set(&sy, &grid_default_cell); - if (style_parse(&sy, &grid_default_cell, value) == -1) - return (NULL); - if (o == NULL) { - o = options_default(oo, options_parent_table_entry(oo, name)); - if (o == NULL) - return (NULL); - } - - if (!OPTIONS_IS_STYLE(o)) - fatalx("option %s is not a style", name); - style_copy(&o->value.style, &sy); - return (o); -} - int options_scope_from_name(struct args *args, int window, const char *name, struct cmd_find_state *fs, struct options **oo, @@ -874,3 +832,37 @@ options_scope_from_flags(struct args *args, int window, return (OPTIONS_TABLE_SESSION); } } + +struct style * +options_string_to_style(struct options *oo, const char *name, + struct format_tree *ft) +{ + struct options_entry *o; + const char *s; + char *expanded; + + o = options_get(oo, name); + if (o == NULL || !OPTIONS_IS_STRING(o)) + return (NULL); + + if (o->cached) + return (&o->style); + s = o->value.string; + log_debug("%s: %s is '%s'", __func__, name, s); + + style_set(&o->style, &grid_default_cell); + o->cached = (strstr(s, "#{") == NULL); + + if (ft != NULL && !o->cached) { + expanded = format_expand(ft, s); + if (style_parse(&o->style, &grid_default_cell, expanded) != 0) { + free(expanded); + return (NULL); + } + free(expanded); + } else { + if (style_parse(&o->style, &grid_default_cell, s) != 0) + return (NULL); + } + return (&o->style); +} diff --git a/screen-redraw.c b/screen-redraw.c index 5ca6024d..19ed6305 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -45,54 +45,197 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *, #define CELL_BORDERS " xqlkmjwvtun~" -/* Check if cell is on the border of a particular pane. */ +const struct grid_cell screen_redraw_border_cell = { + { { ' ' }, 0, 1, 1 }, GRID_ATTR_CHARSET, 0, 8, 8, 0 +}; + +enum screen_redraw_border_type { + SCREEN_REDRAW_OUTSIDE, + SCREEN_REDRAW_INSIDE, + SCREEN_REDRAW_BORDER +}; + +/* Return if window has only two panes. */ static int -screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py) +screen_redraw_two_panes(struct window *w, int direction) { - /* Inside pane. */ - if (px >= wp->xoff && px < wp->xoff + wp->sx && - py >= wp->yoff && py < wp->yoff + wp->sy) + struct window_pane *wp; + + wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); + if (wp == NULL) + return (0); /* one pane */ + if (TAILQ_NEXT(wp, entry) != NULL) + return (0); /* more than two panes */ + if (direction == 0 && wp->xoff == 0) return (0); + if (direction == 1 && wp->yoff == 0) + return (0); + return (1); +} + +/* Check if cell is on the border of a pane. */ +static enum screen_redraw_border_type +screen_redraw_pane_border(struct window_pane *wp, u_int px, u_int py, + int pane_status) +{ + u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy; + + /* Inside pane. */ + if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey) + return (SCREEN_REDRAW_INSIDE); /* Left/right borders. */ - if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= wp->yoff + wp->sy) { - if (wp->xoff != 0 && px == wp->xoff - 1) - return (1); - if (px == wp->xoff + wp->sx) - return (2); + if (pane_status == PANE_STATUS_OFF) { + if (screen_redraw_two_panes(wp->window, 0)) { + if (wp->xoff == 0 && px == wp->sx && py <= wp->sy / 2) + return (SCREEN_REDRAW_BORDER); + if (wp->xoff != 0 && + px == wp->xoff - 1 && + py > wp->sy / 2) + return (SCREEN_REDRAW_BORDER); + } else { + if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { + if (wp->xoff != 0 && px == wp->xoff - 1) + return (SCREEN_REDRAW_BORDER); + if (px == ex) + return (SCREEN_REDRAW_BORDER); + } + } + } else { + if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { + if (wp->xoff != 0 && px == wp->xoff - 1) + return (SCREEN_REDRAW_BORDER); + if (px == ex) + return (SCREEN_REDRAW_BORDER); + } } /* Top/bottom borders. */ - if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= wp->xoff + wp->sx) { - if (wp->yoff != 0 && py == wp->yoff - 1) - return (3); - if (py == wp->yoff + wp->sy) - return (4); + if (pane_status == PANE_STATUS_OFF) { + if (screen_redraw_two_panes(wp->window, 1)) { + if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2) + return (SCREEN_REDRAW_BORDER); + if (wp->yoff != 0 && + py == wp->yoff - 1 && + px > wp->sx / 2) + return (SCREEN_REDRAW_BORDER); + } else { + if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { + if (wp->yoff != 0 && py == wp->yoff - 1) + return (SCREEN_REDRAW_BORDER); + if (py == ey) + return (SCREEN_REDRAW_BORDER); + } + } + } else if (pane_status == PANE_STATUS_TOP) { + if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { + if (wp->yoff != 0 && py == wp->yoff - 1) + return (SCREEN_REDRAW_BORDER); + } + } else { + if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { + if (py == ey) + return (SCREEN_REDRAW_BORDER); + } } /* Outside pane. */ - return (-1); + return (SCREEN_REDRAW_OUTSIDE); } -/* Check if a cell is on the pane border. */ +/* Check if a cell is on a border. */ static int -screen_redraw_cell_border(struct client *c, u_int px, u_int py) +screen_redraw_cell_border(struct client *c, u_int px, u_int py, int pane_status) { struct window *w = c->session->curw->window; struct window_pane *wp; - int retval; + + /* Outside the window? */ + if (px > w->sx || py > w->sy) + return (0); + + /* On the window border? */ + if (px == w->sx || py == w->sy) + return (1); /* Check all the panes. */ TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; - if ((retval = screen_redraw_cell_border1(wp, px, py)) != -1) - return (!!retval); + switch (screen_redraw_pane_border(wp, px, py, pane_status)) { + case SCREEN_REDRAW_INSIDE: + return (0); + case SCREEN_REDRAW_BORDER: + return (1); + case SCREEN_REDRAW_OUTSIDE: + break; + } } return (0); } +/* Work out type of border cell from surrounding cells. */ +static int +screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, + int pane_status) +{ + struct window *w = c->session->curw->window; + u_int sx = w->sx, sy = w->sy; + int borders = 0; + + /* + * Construct a bitmask of whether the cells to the left (bit 4), right, + * top, and bottom (bit 1) of this cell are borders. + */ + if (px == 0 || screen_redraw_cell_border(c, px - 1, py, pane_status)) + borders |= 8; + if (px <= sx && screen_redraw_cell_border(c, px + 1, py, pane_status)) + borders |= 4; + if (pane_status == PANE_STATUS_TOP) { + if (py != 0 && + screen_redraw_cell_border(c, px, py - 1, pane_status)) + borders |= 2; + } else { + if (py == 0 || + screen_redraw_cell_border(c, px, py - 1, pane_status)) + borders |= 2; + } + if (py <= sy && screen_redraw_cell_border(c, px, py + 1, pane_status)) + borders |= 1; + + /* + * Figure out what kind of border this cell is. Only one bit set + * doesn't make sense (can't have a border cell with no others + * connected). + */ + switch (borders) { + case 15: /* 1111, left right top bottom */ + return (CELL_JOIN); + case 14: /* 1110, left right top */ + return (CELL_BOTTOMJOIN); + case 13: /* 1101, left right bottom */ + return (CELL_TOPJOIN); + case 12: /* 1100, left right */ + return (CELL_TOPBOTTOM); + case 11: /* 1011, left top bottom */ + return (CELL_RIGHTJOIN); + case 10: /* 1010, left top */ + return (CELL_BOTTOMRIGHT); + case 9: /* 1001, left bottom */ + return (CELL_TOPRIGHT); + case 7: /* 0111, right top bottom */ + return (CELL_LEFTJOIN); + case 6: /* 0110, right top */ + return (CELL_BOTTOMLEFT); + case 5: /* 0101, right bottom */ + return (CELL_TOPLEFT); + case 3: /* 0011, top bottom */ + return (CELL_LEFTRIGHT); + } + return (CELL_OUTSIDE); +} + /* Check if cell inside a pane. */ static int screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, @@ -100,18 +243,21 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, { struct window *w = c->session->curw->window; struct window_pane *wp; - int borders; + int border; u_int right, line; *wpp = NULL; if (px > w->sx || py > w->sy) return (CELL_OUTSIDE); + if (px == w->sx || py == w->sy) /* window border */ + return (screen_redraw_type_of_cell(c, px, py, pane_status)); if (pane_status != PANE_STATUS_OFF) { - TAILQ_FOREACH(wp, &w->panes, entry) { + wp = w->active; + do { if (!window_pane_visible(wp)) - continue; + goto next1; if (pane_status == PANE_STATUS_TOP) line = wp->yoff - 1; @@ -121,129 +267,51 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, if (py == line && px >= wp->xoff + 2 && px <= right) return (CELL_INSIDE); - } + + next1: + wp = TAILQ_NEXT(wp, entry); + if (wp == NULL) + wp = TAILQ_FIRST(&w->panes); + } while (wp != w->active); } - TAILQ_FOREACH(wp, &w->panes, entry) { + wp = w->active; + do { if (!window_pane_visible(wp)) - continue; + goto next2; *wpp = wp; - /* If outside the pane and its border, skip it. */ - if ((wp->xoff != 0 && px < wp->xoff - 1) || - px > wp->xoff + wp->sx || - (wp->yoff != 0 && py < wp->yoff - 1) || - py > wp->yoff + wp->sy) - continue; - - /* If definitely inside, return so. */ - if (!screen_redraw_cell_border(c, px, py)) + /* + * If definitely inside, return. If not on border, skip. + * Otherwise work out the cell. + */ + border = screen_redraw_pane_border(wp, px, py, pane_status); + if (border == SCREEN_REDRAW_INSIDE) return (CELL_INSIDE); + if (border == SCREEN_REDRAW_OUTSIDE) + goto next2; + return (screen_redraw_type_of_cell(c, px, py, pane_status)); - /* - * Construct a bitmask of whether the cells to the left (bit - * 4), right, top, and bottom (bit 1) of this cell are borders. - */ - borders = 0; - if (px == 0 || screen_redraw_cell_border(c, px - 1, py)) - borders |= 8; - if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py)) - borders |= 4; - if (pane_status == PANE_STATUS_TOP) { - if (py != 0 && screen_redraw_cell_border(c, px, py - 1)) - borders |= 2; - } else { - if (py == 0 || screen_redraw_cell_border(c, px, py - 1)) - borders |= 2; - } - if (py <= w->sy && screen_redraw_cell_border(c, px, py + 1)) - borders |= 1; - - /* - * Figure out what kind of border this cell is. Only one bit - * set doesn't make sense (can't have a border cell with no - * others connected). - */ - switch (borders) { - case 15: /* 1111, left right top bottom */ - return (CELL_JOIN); - case 14: /* 1110, left right top */ - return (CELL_BOTTOMJOIN); - case 13: /* 1101, left right bottom */ - return (CELL_TOPJOIN); - case 12: /* 1100, left right */ - return (CELL_TOPBOTTOM); - case 11: /* 1011, left top bottom */ - return (CELL_RIGHTJOIN); - case 10: /* 1010, left top */ - return (CELL_BOTTOMRIGHT); - case 9: /* 1001, left bottom */ - return (CELL_TOPRIGHT); - case 7: /* 0111, right top bottom */ - return (CELL_LEFTJOIN); - case 6: /* 0110, right top */ - return (CELL_BOTTOMLEFT); - case 5: /* 0101, right bottom */ - return (CELL_TOPLEFT); - case 3: /* 0011, top bottom */ - return (CELL_LEFTRIGHT); - } - } + next2: + wp = TAILQ_NEXT(wp, entry); + if (wp == NULL) + wp = TAILQ_FIRST(&w->panes); + } while (wp != w->active); return (CELL_OUTSIDE); } /* Check if the border of a particular pane. */ static int -screen_redraw_check_is(u_int px, u_int py, int type, int pane_status, - struct window *w, struct window_pane *wantwp, struct window_pane *wp) +screen_redraw_check_is(u_int px, u_int py, int pane_status, + struct window_pane *wp) { - int border; + enum screen_redraw_border_type border; - /* Is this off the active pane border? */ - border = screen_redraw_cell_border1(wantwp, px, py); - if (border == 0 || border == -1) - return (0); - if (pane_status == PANE_STATUS_TOP && border == 4) - return (0); - if (pane_status == PANE_STATUS_BOTTOM && border == 3) - return (0); - - /* If there are more than two panes, that's enough. */ - if (window_count_panes(w) != 2) + border = screen_redraw_pane_border(wp, px, py, pane_status); + if (border == SCREEN_REDRAW_BORDER) return (1); - - /* Else if the cell is not a border cell, forget it. */ - if (wp == NULL || (type == CELL_OUTSIDE || type == CELL_INSIDE)) - return (1); - - /* With status lines mark the entire line. */ - if (pane_status != PANE_STATUS_OFF) - return (1); - - /* Check if the pane covers the whole width. */ - if (wp->xoff == 0 && wp->sx == w->sx) { - /* This can either be the top pane or the bottom pane. */ - if (wp->yoff == 0) { /* top pane */ - if (wp == wantwp) - return (px <= wp->sx / 2); - return (px > wp->sx / 2); - } - return (0); - } - - /* Check if the pane covers the whole height. */ - if (wp->yoff == 0 && wp->sy == w->sy) { - /* This can either be the left pane or the right pane. */ - if (wp->xoff == 0) { /* left pane */ - if (wp == wantwp) - return (py <= wp->sy / 2); - return (py > wp->sy / 2); - } - return (0); - } - - return (1); + return (0); } /* Update pane status. */ @@ -259,15 +327,14 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, struct screen_write_ctx ctx; struct screen old; - if (wp == w->active) - style_apply(&gc, w->options, "pane-active-border-style"); - else - style_apply(&gc, w->options, "pane-border-style"); - - fmt = options_get_string(w->options, "pane-border-format"); - ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); - format_defaults(ft, c, NULL, NULL, wp); + format_defaults(ft, c, c->session, c->session->curw, wp); + + if (wp == w->active) + style_apply(&gc, w->options, "pane-active-border-style", ft); + else + style_apply(&gc, w->options, "pane-border-style", ft); + fmt = options_get_string(w->options, "pane-border-format"); expanded = format_expand_time(ft, fmt); if (wp->sx < 4) @@ -482,39 +549,73 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) tty_reset(&c->tty); } -/* Draw a border cell. */ -static void -screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j, - struct grid_cell *m_active_gc, struct grid_cell *active_gc, - struct grid_cell *m_other_gc, struct grid_cell *other_gc) +/* Get border cell style. */ +static const struct grid_cell * +screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, + u_int y, struct window_pane *wp) { struct client *c = ctx->c; struct session *s = c->session; struct window *w = s->curw->window; + struct options *oo = w->options; + struct grid_cell *gc; + struct format_tree *ft; + + if (wp->border_gc_set) + return (&wp->border_gc); + wp->border_gc_set = 1; + + ft = format_create_defaults(NULL, c, s, s->curw, wp); + gc = &wp->border_gc; + + if (screen_redraw_check_is(x, y, ctx->pane_status, w->active)) { + style_apply(gc, oo, "pane-active-border-style", ft); + gc->attr |= GRID_ATTR_CHARSET; + } else { + style_apply(gc, oo, "pane-border-style", ft); + gc->attr |= GRID_ATTR_CHARSET; + } + + format_free(ft); + return (gc); +} + +/* Draw a border cell. */ +static void +screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) +{ + struct client *c = ctx->c; + struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; - struct window_pane *active = w->active; - struct window_pane *marked = marked_pane.wp; u_int type, x = ctx->ox + i, y = ctx->oy + j; - int flag, pane_status = ctx->pane_status; + int pane_status = ctx->pane_status; + const struct grid_cell *gc; + struct grid_cell copy; if (c->overlay_check != NULL && !c->overlay_check(c, x, y)) return; + type = screen_redraw_check_cell(c, x, y, pane_status, &wp); if (type == CELL_INSIDE) return; - flag = screen_redraw_check_is(x, y, type, pane_status, w, active, wp); - if (server_is_marked(s, s->curw, marked_pane.wp) && - screen_redraw_check_is(x, y, type, pane_status, w, marked, wp)) { - if (flag) - tty_attributes(tty, m_active_gc, NULL); - else - tty_attributes(tty, m_other_gc, NULL); - } else if (flag) - tty_attributes(tty, active_gc, NULL); - else - tty_attributes(tty, other_gc, NULL); + if (wp == NULL) + gc = &screen_redraw_border_cell; + else { + gc = screen_redraw_draw_borders_style(ctx, x, y, wp); + if (gc == NULL) + return; + + if (server_is_marked(s, s->curw, marked_pane.wp) && + screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) { + memcpy(©, gc, sizeof copy); + copy.attr ^= GRID_ATTR_REVERSE; + gc = © + } + } + + tty_attributes(tty, gc, NULL); if (ctx->statustop) tty_cursor(tty, i, ctx->statuslines + j); else @@ -529,27 +630,17 @@ screen_redraw_draw_borders(struct screen_redraw_ctx *ctx) struct client *c = ctx->c; struct session *s = c->session; struct window *w = s->curw->window; - struct tty *tty = &c->tty; - struct options *oo = w->options; - struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; + struct window_pane *wp; u_int i, j; log_debug("%s: %s @%u", __func__, c->name, w->id); - style_apply(&other_gc, oo, "pane-border-style"); - style_apply(&active_gc, oo, "pane-active-border-style"); - active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; + TAILQ_FOREACH(wp, &w->panes, entry) + wp->border_gc_set = 0; - memcpy(&m_other_gc, &other_gc, sizeof m_other_gc); - m_other_gc.attr ^= GRID_ATTR_REVERSE; - memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); - m_active_gc.attr ^= GRID_ATTR_REVERSE; - - for (j = 0; j < tty->sy - ctx->statuslines; j++) { - for (i = 0; i < tty->sx; i++) { - screen_redraw_draw_borders_cell(ctx, i, j, - &m_active_gc, &active_gc, &m_other_gc, &other_gc); - } + for (j = 0; j < c->tty.sy - ctx->statuslines; j++) { + for (i = 0; i < c->tty.sx; i++) + screen_redraw_draw_borders_cell(ctx, i, j); } } diff --git a/status.c b/status.c index 6beadb81..c9da873e 100644 --- a/status.c +++ b/status.c @@ -321,7 +321,7 @@ status_redraw(struct client *c) struct screen_write_ctx ctx; struct grid_cell gc; u_int lines, i, n, width = c->tty.sx; - int flags, force = 0, changed = 0; + int flags, force = 0, changed = 0, fg, bg; struct options_entry *o; union options_value *ov; struct format_tree *ft; @@ -339,7 +339,13 @@ status_redraw(struct client *c) return (1); /* Set up default colour. */ - style_apply(&gc, s->options, "status-style"); + style_apply(&gc, s->options, "status-style", NULL); + fg = options_get_number(s->options, "status-fg"); + if (fg != 8) + gc.fg = fg; + bg = options_get_number(s->options, "status-bg"); + if (bg != 8) + gc.bg = bg; if (!grid_cells_equal(&gc, &sl->style)) { force = 1; memcpy(&sl->style, &gc, sizeof sl->style); @@ -490,7 +496,7 @@ status_message_redraw(struct client *c) if (len > c->tty.sx) len = c->tty.sx; - style_apply(&gc, s->options, "message-style"); + style_apply(&gc, s->options, "message-style", NULL); screen_write_start(&ctx, NULL, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); @@ -633,9 +639,9 @@ status_prompt_redraw(struct client *c) screen_init(sl->active, c->tty.sx, lines, 0); if (c->prompt_mode == PROMPT_COMMAND) - style_apply(&gc, s->options, "message-command-style"); + style_apply(&gc, s->options, "message-command-style", NULL); else - style_apply(&gc, s->options, "message-style"); + style_apply(&gc, s->options, "message-style", NULL); memcpy(&cursorgc, &gc, sizeof cursorgc); cursorgc.attr ^= GRID_ATTR_REVERSE; diff --git a/style.c b/style.c index 2ac78e97..3c615852 100644 --- a/style.c +++ b/style.c @@ -260,17 +260,37 @@ style_tostring(struct style *sy) return (s); } -/* Apply a style. */ +/* Apply a style on top of the given style. */ void -style_apply(struct grid_cell *gc, struct options *oo, const char *name) +style_add(struct grid_cell *gc, struct options *oo, const char *name, + struct format_tree *ft) { - struct style *sy; + struct style *sy; + struct format_tree *ft0 = NULL; - memcpy(gc, &grid_default_cell, sizeof *gc); - sy = options_get_style(oo, name); - gc->fg = sy->gc.fg; - gc->bg = sy->gc.bg; + if (ft == NULL) + ft = ft0 = format_create(NULL, NULL, 0, FORMAT_NOJOBS); + + sy = options_string_to_style(oo, name, ft); + if (sy == NULL) + sy = &style_default; + if (sy->gc.fg != 8) + gc->fg = sy->gc.fg; + if (sy->gc.bg != 8) + gc->bg = sy->gc.bg; gc->attr |= sy->gc.attr; + + if (ft0 != NULL) + format_free(ft0); +} + +/* Apply a style on top of the default style. */ +void +style_apply(struct grid_cell *gc, struct options *oo, const char *name, + struct format_tree *ft) +{ + memcpy(gc, &grid_default_cell, sizeof *gc); + style_add(gc, oo, name, ft); } /* Initialize style from cell. */ diff --git a/tmux.h b/tmux.h index 4e19c4ef..2e978813 100644 --- a/tmux.h +++ b/tmux.h @@ -896,6 +896,9 @@ struct window_pane { u_int xoff; u_int yoff; + int fg; + int bg; + int flags; #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 @@ -950,6 +953,9 @@ struct window_pane { size_t written; size_t skipped; + int border_gc_set; + struct grid_cell border_gc; + TAILQ_ENTRY(window_pane) entry; RB_ENTRY(window_pane) tree_entry; }; @@ -1662,7 +1668,6 @@ enum options_table_type { OPTIONS_TABLE_COLOUR, OPTIONS_TABLE_FLAG, OPTIONS_TABLE_CHOICE, - OPTIONS_TABLE_STYLE, OPTIONS_TABLE_COMMAND }; @@ -1674,12 +1679,13 @@ enum options_table_type { #define OPTIONS_TABLE_IS_ARRAY 0x1 #define OPTIONS_TABLE_IS_HOOK 0x2 +#define OPTIONS_TABLE_IS_STYLE 0x4 struct options_table_entry { const char *name; enum options_table_type type; int scope; - int flags; + int flags; u_int minimum; u_int maximum; @@ -1718,7 +1724,7 @@ struct spawn_context { const char *name; char **argv; int argc; - struct environ *environ; + struct environ *environ; int idx; const char *cwd; @@ -1898,18 +1904,17 @@ struct options_entry *options_match_get(struct options *, const char *, int *, int, int *); const char *options_get_string(struct options *, const char *); long long options_get_number(struct options *, const char *); -struct style *options_get_style(struct options *, const char *); struct options_entry * printflike(4, 5) options_set_string(struct options *, const char *, int, const char *, ...); struct options_entry *options_set_number(struct options *, const char *, long long); -struct options_entry *options_set_style(struct options *, const char *, int, - const char *); int options_scope_from_name(struct args *, int, const char *, struct cmd_find_state *, struct options **, char **); int options_scope_from_flags(struct args *, int, struct cmd_find_state *, struct options **, char **); +struct style *options_string_to_style(struct options *, const char *, + struct format_tree *); /* options-table.c */ extern const struct options_table_entry options_table[]; @@ -2136,7 +2141,7 @@ enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int, int, const char *, int); /* cmd-parse.c */ -void cmd_parse_empty(struct cmd_parse_input *); +void cmd_parse_empty(struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_string(const char *, struct cmd_parse_input *); @@ -2227,8 +2232,8 @@ void file_fire_done(struct client_file *); void file_fire_read(struct client_file *); int file_can_print(struct client *); void printflike(2, 3) file_print(struct client *, const char *, ...); -void file_vprint(struct client *, const char *, va_list); -void file_print_buffer(struct client *, void *, size_t); +void file_vprint(struct client *, const char *, va_list); +void file_print_buffer(struct client *, void *, size_t); void printflike(2, 3) file_error(struct client *, const char *, ...); void file_write(struct client *, const char *, int, const void *, size_t, client_file_cb, void *); @@ -2726,7 +2731,7 @@ struct session *session_find_by_id_str(const char *); struct session *session_find_by_id(u_int); struct session *session_create(const char *, const char *, const char *, struct environ *, struct options *, struct termios *); -void session_destroy(struct session *, int, const char *); +void session_destroy(struct session *, int, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); char *session_check_name(const char *); @@ -2795,7 +2800,7 @@ struct menu *menu_create(const char *); void menu_add_items(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); -void menu_add_item(struct menu *, const struct menu_item *, +void menu_add_item(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); void menu_free(struct menu *); @@ -2818,8 +2823,10 @@ int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, int style_parse(struct style *,const struct grid_cell *, const char *); const char *style_tostring(struct style *); +void style_add(struct grid_cell *, struct options *, + const char *, struct format_tree *); void style_apply(struct grid_cell *, struct options *, - const char *); + const char *, struct format_tree *); void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); diff --git a/tty.c b/tty.c index 99b433ae..e378b45d 100644 --- a/tty.c +++ b/tty.c @@ -2686,6 +2686,14 @@ tty_try_colour(struct tty *tty, int colour, const char *type) return (-1); } +static void +tty_window_default_style(struct grid_cell *gc, struct window_pane *wp) +{ + memcpy(gc, &grid_default_cell, sizeof *gc); + gc->fg = wp->fg; + gc->bg = wp->bg; +} + static void tty_default_colours(struct grid_cell *gc, struct window_pane *wp) { @@ -2694,8 +2702,12 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) if (wp->flags & PANE_STYLECHANGED) { wp->flags &= ~PANE_STYLECHANGED; - style_apply(&wp->cached_active_gc, oo, "window-active-style"); - style_apply(&wp->cached_gc, oo, "window-style"); + + tty_window_default_style(&wp->cached_active_gc, wp); + style_add(&wp->cached_active_gc, oo, "window-active-style", + NULL); + tty_window_default_style(&wp->cached_gc, wp); + style_add(&wp->cached_gc, oo, "window-style", NULL); } if (gc->fg == 8) { diff --git a/window-copy.c b/window-copy.c index a803f3b3..2c50a1cd 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3004,7 +3004,7 @@ window_copy_write_line(struct window_mode_entry *wme, size_t size = 0; u_int hsize = screen_hsize(data->backing); - style_apply(&gc, oo, "mode-style"); + style_apply(&gc, oo, "mode-style", NULL); gc.flags |= GRID_FLAG_NOPALETTE; if (py == 0 && s->rupper < s->rlower && !data->hide_position) { @@ -3311,7 +3311,7 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, } /* Set colours and selection. */ - style_apply(&gc, oo, "mode-style"); + style_apply(&gc, oo, "mode-style", NULL); gc.flags |= GRID_FLAG_NOPALETTE; screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, data->modekeys, &gc); diff --git a/window.c b/window.c index fbc4bb19..7fea9d80 100644 --- a/window.c +++ b/window.c @@ -865,6 +865,9 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->fd = -1; wp->event = NULL; + wp->fg = 8; + wp->bg = 8; + TAILQ_INIT(&wp->modes); wp->layout_cell = NULL; @@ -1096,6 +1099,7 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, wp->screen = wme->screen; wp->flags |= (PANE_REDRAW|PANE_CHANGED); + server_redraw_window_borders(wp->window); server_status_window(wp->window); notify_pane("pane-mode-changed", wp); @@ -1127,6 +1131,7 @@ window_pane_reset_mode(struct window_pane *wp) } wp->flags |= (PANE_REDRAW|PANE_CHANGED); + server_redraw_window_borders(wp->window); server_status_window(wp->window); notify_pane("pane-mode-changed", wp); } From 80e52545a0cccba20fb4d96ed09ea2c129a30c93 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:06:03 +0000 Subject: [PATCH 0396/1006] Improve command prompt completion: - Show a menu with completions if there are multiple. - Don't complete argument stuff (options, layouts) at start of text. - For -t and -s, if there is no : then complete sessions but if there is a :, show a menu of all windows in the session rather than trying to complete the window name which is a bit useless if there are duplicates. --- menu.c | 35 ++++ server-client.c | 10 +- status.c | 507 +++++++++++++++++++++++++++++++----------------- tmux.h | 1 + 4 files changed, 373 insertions(+), 180 deletions(-) diff --git a/menu.c b/menu.c index 2f92af34..e78999c6 100644 --- a/menu.c +++ b/menu.c @@ -240,6 +240,16 @@ menu_key_cb(struct client *c, struct key_event *event) } while ((name == NULL || *name == '-') && md->choice != old); c->flags |= CLIENT_REDRAWOVERLAY; return (0); + case KEYC_BSPACE: + if (~md->flags & MENU_TAB) + break; + return (1); + case '\011': /* Tab */ + if (~md->flags & MENU_TAB) + break; + if (md->choice == count - 1) + return (1); + /* FALLTHROUGH */ case KEYC_DOWN: case 'j': if (old == -1) @@ -253,6 +263,31 @@ menu_key_cb(struct client *c, struct key_event *event) } while ((name == NULL || *name == '-') && md->choice != old); c->flags |= CLIENT_REDRAWOVERLAY; return (0); + case 'g': + case KEYC_PPAGE: + case '\002': /* C-b */ + if (md->choice > 5) + md->choice -= 5; + else + md->choice = 0; + while (md->choice != count && (name == NULL || *name == '-')) + md->choice++; + if (md->choice == count) + md->choice = -1; + c->flags |= CLIENT_REDRAWOVERLAY; + break; + case 'G': + case KEYC_NPAGE: + if (md->choice > count - 6) + md->choice = count - 1; + else + md->choice += 5; + while (md->choice != -1 && (name == NULL || *name == '-')) + md->choice--; + c->flags |= CLIENT_REDRAWOVERLAY; + break; + case '\006': /* C-f */ + break; case '\r': goto chosen; case '\033': /* Escape */ diff --git a/server-client.c b/server-client.c index ed138f8e..b21dac1d 100644 --- a/server-client.c +++ b/server-client.c @@ -1296,10 +1296,6 @@ server_client_handle_key(struct client *c, struct key_event *event) */ if (~c->flags & CLIENT_READONLY) { status_message_clear(c); - if (c->prompt_string != NULL) { - if (status_prompt_key(c, event->key) == 0) - return (0); - } if (c->overlay_key != NULL) { switch (c->overlay_key(c, event)) { case 0: @@ -1310,6 +1306,10 @@ server_client_handle_key(struct client *c, struct key_event *event) } } server_client_clear_overlay(c); + if (c->prompt_string != NULL) { + if (status_prompt_key(c, event->key) == 0) + return (0); + } } /* @@ -1564,6 +1564,8 @@ server_client_reset_state(struct client *c) } else { s = wp->screen; 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); diff --git a/status.c b/status.c index c9da873e..0acf4233 100644 --- a/status.c +++ b/status.c @@ -37,9 +37,15 @@ static const char *status_prompt_up_history(u_int *); static const char *status_prompt_down_history(u_int *); static void status_prompt_add_history(const char *); -static char **status_prompt_complete_list(u_int *, const char *); -static char *status_prompt_complete_prefix(char **, u_int); -static char *status_prompt_complete(struct session *, const char *); +static char *status_prompt_complete(struct client *, const char *, u_int); + +struct status_prompt_menu { + struct client *c; + u_int start; + u_int size; + char **list; + char flag; +}; /* Status prompt history. */ #define PROMPT_HISTORY 100 @@ -919,15 +925,88 @@ status_prompt_paste(struct client *c) return (1); } +/* Finish completion. */ +static int +status_prompt_replace_complete(struct client *c, const char *s) +{ + char word[64], *allocated = NULL; + size_t size, n, off, idx, used; + struct utf8_data *first, *last, *ud; + + if (c->prompt_buffer[0].size == 0) + return (0); + size = utf8_strlen(c->prompt_buffer); + + idx = c->prompt_index; + if (idx != 0) + idx--; + + /* Find the word we are in. */ + first = &c->prompt_buffer[idx]; + while (first > c->prompt_buffer && !status_prompt_space(first)) + first--; + while (first->size != 0 && status_prompt_space(first)) + first++; + last = &c->prompt_buffer[idx]; + while (last->size != 0 && !status_prompt_space(last)) + last++; + while (last > c->prompt_buffer && status_prompt_space(last)) + last--; + if (last->size != 0) + last++; + if (last <= first) + return (0); + if (s == NULL) { + used = 0; + for (ud = first; ud < last; ud++) { + if (used + ud->size >= sizeof word) + break; + memcpy(word + used, ud->data, ud->size); + used += ud->size; + } + if (ud != last) + return (0); + word[used] = '\0'; + } + + /* Try to complete it. */ + if (s == NULL) { + allocated = status_prompt_complete(c, word, + first - c->prompt_buffer); + if (allocated == NULL) + return (0); + s = allocated; + } + + /* Trim out word. */ + n = size - (last - c->prompt_buffer) + 1; /* with \0 */ + memmove(first, last, n * sizeof *c->prompt_buffer); + size -= last - first; + + /* Insert the new word. */ + size += strlen(s); + off = first - c->prompt_buffer; + c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1, + sizeof *c->prompt_buffer); + first = c->prompt_buffer + off; + memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer); + for (idx = 0; idx < strlen(s); idx++) + utf8_set(&first[idx], s[idx]); + c->prompt_index = (first - c->prompt_buffer) + strlen(s); + + free(allocated); + return (1); +} + /* Handle keys in prompt. */ int status_prompt_key(struct client *c, key_code key) { struct options *oo = c->session->options; - char *s, *cp, word[64], prefix = '='; + char *s, *cp, prefix = '='; const char *histstr, *ws = NULL, *keystring; - size_t size, n, off, idx, used; - struct utf8_data tmp, *first, *last, *ud; + size_t size, idx; + struct utf8_data tmp; int keys; if (c->prompt_flags & PROMPT_KEY) { @@ -992,63 +1071,9 @@ process_key: } break; case '\011': /* Tab */ - if (c->prompt_buffer[0].size == 0) - break; - - idx = c->prompt_index; - if (idx != 0) - idx--; - - /* Find the word we are in. */ - first = &c->prompt_buffer[idx]; - while (first > c->prompt_buffer && !status_prompt_space(first)) - first--; - while (first->size != 0 && status_prompt_space(first)) - first++; - last = &c->prompt_buffer[idx]; - while (last->size != 0 && !status_prompt_space(last)) - last++; - while (last > c->prompt_buffer && status_prompt_space(last)) - last--; - if (last->size != 0) - last++; - if (last <= first) - break; - - used = 0; - for (ud = first; ud < last; ud++) { - if (used + ud->size >= sizeof word) - break; - memcpy(word + used, ud->data, ud->size); - used += ud->size; - } - if (ud != last) - break; - word[used] = '\0'; - - /* And try to complete it. */ - if ((s = status_prompt_complete(c->session, word)) == NULL) - break; - - /* Trim out word. */ - n = size - (last - c->prompt_buffer) + 1; /* with \0 */ - memmove(first, last, n * sizeof *c->prompt_buffer); - size -= last - first; - - /* Insert the new word. */ - size += strlen(s); - off = first - c->prompt_buffer; - c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1, - sizeof *c->prompt_buffer); - first = c->prompt_buffer + off; - memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer); - for (idx = 0; idx < strlen(s); idx++) - utf8_set(&first[idx], s[idx]); - - c->prompt_index = (first - c->prompt_buffer) + strlen(s); - free(s); - - goto changed; + if (status_prompt_replace_complete(c, NULL)) + goto changed; + break; case KEYC_BSPACE: case '\010': /* C-h */ if (c->prompt_index != 0) { @@ -1330,14 +1355,13 @@ status_prompt_add_history(const char *line) } /* Build completion list. */ -char ** -status_prompt_complete_list(u_int *size, const char *s) +static char ** +status_prompt_complete_list(u_int *size, const char *s, int at_start) { char **list = NULL; const char **layout, *value, *cp; const struct cmd_entry **cmdent; const struct options_table_entry *oe; - u_int idx; size_t slen = strlen(s), valuelen; struct options_entry *o; struct options_array_item *a; @@ -1353,18 +1377,6 @@ status_prompt_complete_list(u_int *size, const char *s) list[(*size)++] = xstrdup((*cmdent)->name); } } - for (oe = options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup(oe->name); - } - } - for (layout = layouts; *layout != NULL; layout++) { - if (strncmp(*layout, s, slen) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = xstrdup(*layout); - } - } o = options_get_only(global_options, "command-alias"); if (o != NULL) { a = options_array_first(o); @@ -1383,8 +1395,21 @@ status_prompt_complete_list(u_int *size, const char *s) a = options_array_next(a); } } - for (idx = 0; idx < (*size); idx++) - log_debug("complete %u: %s", idx, list[idx]); + if (at_start) + return (list); + + for (oe = options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, slen) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = xstrdup(oe->name); + } + } + for (layout = layouts; *layout != NULL; layout++) { + if (strncmp(*layout, s, slen) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = xstrdup(*layout); + } + } return (list); } @@ -1409,124 +1434,254 @@ status_prompt_complete_prefix(char **list, u_int size) return (out); } +/* Complete word menu callback. */ +static void +status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key, + void *data) +{ + struct status_prompt_menu *spm = data; + struct client *c = spm->c; + u_int i; + char *s; + + if (key != KEYC_NONE) { + idx += spm->start; + if (spm->flag == '\0') + s = xstrdup(spm->list[idx]); + else + xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); + if (status_prompt_replace_complete(c, s)) + c->flags |= CLIENT_REDRAWSTATUS; + free(s); + } + + for (i = 0; i < spm->size; i++) + free(spm->list[i]); + free(spm->list); +} + +/* Show complete word menu. */ +static int +status_prompt_complete_list_menu(struct client *c, char **list, u_int size, + u_int offset, char flag) +{ + struct menu *menu; + struct menu_item item; + struct status_prompt_menu *spm; + u_int lines = status_line_size(c), height, i; + u_int py; + + if (size <= 1) + return (0); + if (c->tty.sy - lines < 3) + return (0); + + spm = xmalloc(sizeof *spm); + spm->c = c; + spm->size = size; + spm->list = list; + spm->flag = flag; + + height = c->tty.sy - lines - 2; + if (height > 10) + height = 10; + if (height > size) + height = size; + spm->start = size - height; + + menu = menu_create(""); + for (i = spm->start; i < size; i++) { + item.name = list[i]; + item.key = '0' + (i - spm->start); + item.command = NULL; + menu_add_item(menu, &item, NULL, NULL, NULL); + } + + if (options_get_number(c->session->options, "status-position") == 0) + py = lines; + else + py = c->tty.sy - 3 - height; + offset += utf8_cstrwidth(c->prompt_string); + if (offset > 2) + offset -= 2; + else + offset = 0; + + if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, + py, c, NULL, status_prompt_menu_callback, spm) != 0) { + menu_free(menu); + free(spm); + return (0); + } + return (1); +} + +/* Show complete word menu. */ +static char * +status_prompt_complete_window_menu(struct client *c, struct session *s, + u_int offset, char flag) +{ + struct menu *menu; + struct menu_item item; + struct status_prompt_menu *spm; + struct winlink *wl; + char **list = NULL, *tmp; + u_int lines = status_line_size(c), height; + u_int py, size = 0; + + if (c->tty.sy - lines < 3) + return (NULL); + + spm = xmalloc(sizeof *spm); + spm->c = c; + spm->flag = flag; + + height = c->tty.sy - lines - 2; + if (height > 10) + height = 10; + spm->start = 0; + + menu = menu_create(""); + RB_FOREACH(wl, winlinks, &s->windows) { + list = xreallocarray(list, size + 1, sizeof *list); + xasprintf(&list[size++], "%s:%d", s->name, wl->idx); + + xasprintf(&tmp, "%s:%d (%s)", s->name, wl->idx, + wl->window->name); + item.name = tmp; + item.key = '0' + size - 1; + item.command = NULL; + menu_add_item(menu, &item, NULL, NULL, NULL); + free(tmp); + + if (size == height) + break; + } + if (size == 1) { + menu_free(menu); + xasprintf(&tmp, "-%c%s", flag, list[0]); + free(list[0]); + free(list); + return (tmp); + } + if (height > size) + height = size; + + spm->size = size; + spm->list = list; + + if (options_get_number(c->session->options, "status-position") == 0) + py = lines; + else + py = c->tty.sy - 3 - height; + offset += utf8_cstrwidth(c->prompt_string); + if (offset > 2) + offset -= 2; + else + offset = 0; + + if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, + py, c, NULL, status_prompt_menu_callback, spm) != 0) { + menu_free(menu); + free(spm); + return (NULL); + } + return (NULL); +} + +/* Sort complete list. */ +static int +status_prompt_complete_sort(const void *a, const void *b) +{ + const char **aa = (const char **)a, **bb = (const char **)b; + + return (strcmp(*aa, *bb)); +} + /* Complete word. */ static char * -status_prompt_complete(struct session *session, const char *s) +status_prompt_complete(struct client *c, const char *word, u_int offset) { - char **list = NULL; - const char *colon; + struct session *session, *loop; + const char *s, *colon; + size_t slen; + char **list = NULL, *copy = NULL, *out = NULL, *tmp; + char flag = '\0'; u_int size = 0, i; - struct session *s_loop; - struct winlink *wl; - struct window *w; - char *copy, *out, *tmp; - if (*s == '\0') + if (*word == '\0') return (NULL); - out = NULL; - if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) { - list = status_prompt_complete_list(&size, s); + if (strncmp(word, "-t", 2) != 0 && strncmp(word, "-s", 2) != 0) { + list = status_prompt_complete_list(&size, word, offset == 0); if (size == 0) out = NULL; else if (size == 1) xasprintf(&out, "%s ", list[0]); else out = status_prompt_complete_prefix(list, size); - for (i = 0; i < size; i++) - free(list[i]); - free(list); - return (out); - } - copy = xstrdup(s); - - colon = ":"; - if (copy[strlen(copy) - 1] == ':') - copy[strlen(copy) - 1] = '\0'; - else - colon = ""; - s = copy + 2; - - RB_FOREACH(s_loop, sessions, &sessions) { - if (strncmp(s_loop->name, s, strlen(s)) == 0) { - list = xreallocarray(list, size + 2, sizeof *list); - list[size++] = s_loop->name; - } - } - if (size == 1) { - out = xstrdup(list[0]); - if (session_find(list[0]) != NULL) - colon = ":"; - } else if (size != 0) - out = status_prompt_complete_prefix(list, size); - if (out != NULL) { - xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); - free(out); - out = tmp; goto found; } - colon = ""; - if (*s == ':') { - RB_FOREACH(wl, winlinks, &session->windows) { - xasprintf(&tmp, ":%s", wl->window->name); - if (strncmp(tmp, s, strlen(s)) == 0){ - list = xreallocarray(list, size + 1, - sizeof *list); - list[size++] = tmp; + s = word + 2; + slen = strlen(s); + + flag = word[1]; + offset += 2; + + colon = strchr(s, ':'); + + /* If there is no colon, complete as a session. */ + if (colon == NULL) { + RB_FOREACH(loop, sessions, &sessions) { + if (strncmp(loop->name, s, strlen(s)) != 0) continue; - } - free(tmp); - - xasprintf(&tmp, ":%d", wl->idx); - if (strncmp(tmp, s, strlen(s)) == 0) { - list = xreallocarray(list, size + 1, - sizeof *list); - list[size++] = tmp; - continue; - } - free(tmp); + list = xreallocarray(list, size + 2, sizeof *list); + xasprintf(&list[size++], "%s:", loop->name); } - } else { - RB_FOREACH(s_loop, sessions, &sessions) { - RB_FOREACH(wl, winlinks, &s_loop->windows) { - w = wl->window; - - xasprintf(&tmp, "%s:%s", s_loop->name, w->name); - if (strncmp(tmp, s, strlen(s)) == 0) { - list = xreallocarray(list, size + 1, - sizeof *list); - list[size++] = tmp; - continue; - } - free(tmp); - - xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx); - if (strncmp(tmp, s, strlen(s)) == 0) { - list = xreallocarray(list, size + 1, - sizeof *list); - list[size++] = tmp; - continue; - } - free(tmp); - } - } - } - if (size == 1) { - out = xstrdup(list[0]); - colon = " "; - } else if (size != 0) out = status_prompt_complete_prefix(list, size); - if (out != NULL) { - xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); - out = tmp; + if (out != NULL) { + xasprintf(&tmp, "-%c%s", flag, out); + free(out); + out = tmp; + } + goto found; } - for (i = 0; i < size; i++) - free((void *)list[i]); + /* If there is a colon but no period, find session and show a menu. */ + if (strchr(colon + 1, '.') == NULL) { + if (*s == ':') + session = c->session; + else { + copy = xstrdup(s); + *strchr(copy, ':') = '\0'; + session = session_find(copy); + free(copy); + if (session == NULL) + goto found; + } + out = status_prompt_complete_window_menu(c, session, offset, + flag); + if (out == NULL) + return (NULL); + } found: - free(copy); - free(list); + if (size != 0) { + qsort(list, size, sizeof *list, status_prompt_complete_sort); + for (i = 0; i < size; i++) + log_debug("complete %u: %s", i, list[i]); + } + + if (out != NULL && strcmp(word, out) == 0) { + free(out); + out = NULL; + } + if (out != NULL || + !status_prompt_complete_list_menu(c, list, size, offset, flag)) { + for (i = 0; i < size; i++) + free(list[i]); + free(list); + } return (out); } diff --git a/tmux.h b/tmux.h index 2e978813..486b8b4c 100644 --- a/tmux.h +++ b/tmux.h @@ -2796,6 +2796,7 @@ __dead void printflike(1, 2) fatalx(const char *, ...); /* menu.c */ #define MENU_NOMOUSE 0x1 +#define MENU_TAB 0x2 struct menu *menu_create(const char *); void menu_add_items(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, From 2391fe23ab09cfe8667abdcc3338a958ab3da31d Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:11:52 +0000 Subject: [PATCH 0397/1006] Copy mode search improvements: - Add styles for the search marking styles (copy-mode-match-style and copy-mode-current-match-style). - Show the current match (the one with the cursor on it) in a different style. - Copying without a selection will copy the current match if there is one. --- options-table.c | 16 ++++++ screen-write.c | 39 +------------ tmux.1 | 16 ++++++ tmux.h | 2 - window-copy.c | 148 +++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 167 insertions(+), 54 deletions(-) diff --git a/options-table.c b/options-table.c index 5c368586..a89758c5 100644 --- a/options-table.c +++ b/options-table.c @@ -656,6 +656,22 @@ const struct options_table_entry options_table[] = { .default_num = 1 }, + { .name = "copy-mode-match-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "bg=cyan,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," + }, + + { .name = "copy-mode-current-match-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "bg=magenta,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = "," + }, + { .name = "main-pane-height", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, diff --git a/screen-write.c b/screen-write.c index afa1e96a..e3e51020 100644 --- a/screen-write.c +++ b/screen-write.c @@ -344,44 +344,9 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, free(msg); } -/* Copy from another screen. Assumes target region is big enough. */ -void -screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, - u_int py, u_int nx, u_int ny, bitstr_t *mbs, const struct grid_cell *mgc) -{ - struct screen *s = ctx->s; - struct grid *gd = src->grid; - struct grid_cell gc; - u_int xx, yy, cx, cy, b; - - if (nx == 0 || ny == 0) - return; - - cx = s->cx; - cy = s->cy; - - for (yy = py; yy < py + ny; yy++) { - for (xx = px; xx < px + nx; xx++) { - grid_get_cell(gd, xx, yy, &gc); - if (mbs != NULL) { - b = (yy * screen_size_x(src)) + xx; - if (bit_test(mbs, b)) { - gc.attr = mgc->attr; - gc.fg = mgc->fg; - gc.bg = mgc->bg; - } - } - if (xx + gc.data.width <= px + nx) - screen_write_cell(ctx, &gc); - } - cy++; - screen_write_set_cursor(ctx, cx, cy); - } -} - /* - * Copy from another screen but without the selection stuff. Also assumes the - * target region is already big enough. + * Copy from another screen but without the selection stuff. Assumes the target + * region is already big enough. */ void screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, diff --git a/tmux.1 b/tmux.1 index 7918c780..5c873fdd 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3717,6 +3717,22 @@ If suffixed by .Ql % , this is a percentage of the window size. .Pp +.It Ic copy-mode-match-style Ar style +Set the style of search matches in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy-mode-current-match-style Ar style +Set the style of the current search match in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp .It Xo Ic mode-keys .Op Ic vi | emacs .Xc diff --git a/tmux.h b/tmux.h index 486b8b4c..e2443660 100644 --- a/tmux.h +++ b/tmux.h @@ -2434,8 +2434,6 @@ void screen_write_vnputs(struct screen_write_ctx *, ssize_t, const struct grid_cell *, const char *, va_list); void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *, u_char); -void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int, - u_int, u_int, u_int, bitstr_t *, const struct grid_cell *); void screen_write_fast_copy(struct screen_write_ctx *, struct screen *, u_int, u_int, u_int, u_int); void screen_write_hline(struct screen_write_ctx *, u_int, int, int); diff --git a/window-copy.c b/window-copy.c index 2c50a1cd..f5a07a10 100644 --- a/window-copy.c +++ b/window-copy.c @@ -257,12 +257,13 @@ struct window_copy_mode_data { int searchtype; int searchregex; char *searchstr; - bitstr_t *searchmark; + u_char *searchmark; u_int searchcount; int searchthis; int searchx; int searchy; int searcho; + u_char searchgen; int timeout; /* search has timed out */ #define WINDOW_COPY_SEARCH_TIMEOUT 10 @@ -362,10 +363,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->searchregex = 0; data->searchstr = NULL; } - data->searchmark = NULL; data->searchx = data->searchy = data->searcho = -1; - data->timeout = 0; - data->viewmode = 0; data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; @@ -2860,7 +2858,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, const struct grid_line *gl; int found, cis, which = -1; int cflags = REG_EXTENDED; - u_int px, py, b, nfound = 0, width; + u_int px, py, i, b, nfound = 0, width; u_int ssize = 1; char *sbuf; regex_t reg; @@ -2880,7 +2878,8 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, cis = window_copy_is_lowercase(data->searchstr); free(data->searchmark); - data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx); + data->searchmark = xcalloc(gd->hsize + gd->sy, gd->sx); + data->searchgen = 1; if (regex) { sbuf = xmalloc(ssize); @@ -2921,7 +2920,12 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, which = nfound; b = (py * gd->sx) + px; - bit_nset(data->searchmark, b, b + width - 1); + for (i = b; i < b + width; i++) + data->searchmark[i] = data->searchgen; + if (data->searchgen == UCHAR_MAX) + data->searchgen = 1; + else + data->searchgen++; px++; } @@ -2991,6 +2995,114 @@ window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) window_copy_redraw_screen(wme); } +static void +window_copy_match_start_end(struct window_copy_mode_data *data, u_int at, + u_int *start, u_int *end) +{ + struct grid *gd = data->backing->grid; + u_int last = (gd->hsize + gd->sy) * gd->sx - 1; + u_char mark = data->searchmark[at]; + + *start = *end = at; + while (*start != 0 && data->searchmark[*start] == mark) + (*start)--; + if (data->searchmark[*start] != mark) + (*start)++; + while (*end != last && data->searchmark[*end] == mark) + (*end)++; + if (data->searchmark[*end] != mark) + (*end)--; +} + +static char * +window_copy_match_at_cursor(struct window_copy_mode_data *data) +{ + struct grid *gd = data->backing->grid; + struct grid_cell gc; + u_int at, start, end, cy, px, py; + u_int sx = screen_size_x(data->backing); + char *buf = NULL; + size_t len = 0; + + if (data->searchmark == NULL) + return (NULL); + + cy = screen_hsize(data->backing) - data->oy + data->cy; + at = (cy * sx) + data->cx; + if (data->searchmark[at] == 0) + return (NULL); + window_copy_match_start_end(data, at, &start, &end); + + /* + * Cells will not be set in the marked array unless they are valid text + * and wrapping will be taken care of, so we can just copy. + */ + for (at = start; at <= end; at++) { + py = at / sx; + px = at % (py * sx); + + grid_get_cell(gd, px, py, &gc); + buf = xrealloc(buf, len + gc.data.size + 1); + memcpy(buf + len, gc.data.data, gc.data.size); + len += gc.data.size; + } + if (len != 0) + buf[len] = '\0'; + return (buf); +} + +static void +window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, + struct grid_cell *gc, const struct grid_cell *mgc, + const struct grid_cell *cgc) +{ + struct window_copy_mode_data *data = wme->data; + u_int mark, at, start, end, cy; + u_int sx = screen_size_x(data->backing); + + if (data->searchmark == NULL) + return; + mark = data->searchmark[(fy * sx) + fx]; + if (mark == 0) + return; + + cy = screen_hsize(data->backing) - data->oy + data->cy; + at = (cy * sx) + data->cx; + if (data->searchmark[at] == mark) { + window_copy_match_start_end(data, at, &start, &end); + if (at >= start && at <= end) { + gc->attr = cgc->attr; + gc->fg = cgc->fg; + gc->bg = cgc->bg; + } + return; + } + + gc->attr = mgc->attr; + gc->fg = mgc->fg; + gc->bg = mgc->bg; +} + +static void +window_copy_write_one(struct window_mode_entry *wme, + struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx, + const struct grid_cell *mgc, const struct grid_cell *cgc) +{ + struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->backing->grid; + struct grid_cell gc; + u_int fx; + + screen_write_cursormove(ctx, 0, py, 0); + for (fx = 0; fx < nx; fx++) { + grid_get_cell(gd, fx, fy, &gc); + if (fx + gc.data.width <= nx) { + window_copy_update_style(wme, fx, fy, &gc, mgc, cgc); + screen_write_cell(ctx, &gc); + } + } +} + static void window_copy_write_line(struct window_mode_entry *wme, struct screen_write_ctx *ctx, u_int py) @@ -2999,13 +3111,17 @@ window_copy_write_line(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct options *oo = wp->window->options; - struct grid_cell gc; + struct grid_cell gc, mgc, cgc; char hdr[512]; size_t size = 0; u_int hsize = screen_hsize(data->backing); style_apply(&gc, oo, "mode-style", NULL); gc.flags |= GRID_FLAG_NOPALETTE; + style_apply(&mgc, oo, "copy-mode-match-style", NULL); + mgc.flags |= GRID_FLAG_NOPALETTE; + style_apply(&cgc, oo, "copy-mode-current-match-style", NULL); + cgc.flags |= GRID_FLAG_NOPALETTE; if (py == 0 && s->rupper < s->rlower && !data->hide_position) { if (data->searchmark == NULL) { @@ -3035,15 +3151,13 @@ window_copy_write_line(struct window_mode_entry *wme, size = 0; if (size < screen_size_x(s)) { - screen_write_cursormove(ctx, 0, py, 0); - screen_write_copy(ctx, data->backing, 0, hsize - data->oy + py, - screen_size_x(s) - size, 1, data->searchmark, &gc); + window_copy_write_one(wme, ctx, py, hsize - data->oy + py, + screen_size_x(s) - size, &mgc, &cgc); } if (py == data->cy && data->cx == screen_size_x(s)) { - memcpy(&gc, &grid_default_cell, sizeof gc); screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0); - screen_write_putc(ctx, &gc, '$'); + screen_write_putc(ctx, &grid_default_cell, '$'); } } @@ -3354,8 +3468,12 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len) u_int firstsx, lastex, restex, restsx, selx; int keys; - if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) - return (NULL); + if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) { + buf = window_copy_match_at_cursor(data); + if (buf != NULL) + *len = strlen(buf); + return (buf); + } buf = xmalloc(1); off = 0; From 463864f5a25a0015a4f2795ba8a14e0e0bb2fa7a Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:16:36 +0000 Subject: [PATCH 0398/1006] Add -W and -T flags to command-prompt to only complete a window and a target, also complete aliases. --- cmd-command-prompt.c | 8 +++- key-bindings.c | 4 +- status.c | 112 +++++++++++++++++++++++++++++-------------- tmux.1 | 10 +++- tmux.h | 2 + 5 files changed, 95 insertions(+), 41 deletions(-) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index e53c4320..b8e3bd5c 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,8 +40,8 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1kiI:Np:t:", 0, 1 }, - .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " + .args = { "1kiI:Np:Tt:W", 0, 1 }, + .usage = "[-1kiNTW] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " "[template]", .flags = CMD_CLIENT_TFLAG, @@ -121,6 +121,10 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_INCREMENTAL; else if (args_has(args, 'k')) cdata->flags |= PROMPT_KEY; + else if (args_has(args, 'W')) + cdata->flags |= PROMPT_WINDOW; + else if (args_has(args, 'T')) + cdata->flags |= PROMPT_TARGET; status_prompt_set(tc, prompt, input, cmd_command_prompt_callback, cmd_command_prompt_free, cdata, cdata->flags); free(prompt); diff --git a/key-bindings.c b/key-bindings.c index 09a4eafa..85bfb788 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -243,12 +243,12 @@ key_bindings_init(void) "bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"", "bind -N 'Split window horizontally' % split-window -h", "bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window", - "bind -N 'Prompt for window index to select' \"'\" command-prompt -pindex \"select-window -t ':%%'\"", + "bind -N 'Prompt for window index to select' \"'\" command-prompt -Wpindex \"select-window -t ':%%'\"", "bind -N 'Switch to previous client' ( switch-client -p", "bind -N 'Switch to next client' ) switch-client -n", "bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"", "bind -N 'Delete the most recent paste buffer' - delete-buffer", - "bind -N 'Move the current window' . command-prompt \"move-window -t '%%'\"", + "bind -N 'Move the current window' . command-prompt -T \"move-window -t '%%'\"", "bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'", "bind -N 'Select window 0' 0 select-window -t:=0", "bind -N 'Select window 1' 1 select-window -t:=1", diff --git a/status.c b/status.c index 0acf4233..77b92717 100644 --- a/status.c +++ b/status.c @@ -38,6 +38,8 @@ static const char *status_prompt_down_history(u_int *); static void status_prompt_add_history(const char *); static char *status_prompt_complete(struct client *, const char *, u_int); +static char *status_prompt_complete_window_menu(struct client *, + struct session *, u_int, char); struct status_prompt_menu { struct client *c; @@ -933,13 +935,11 @@ status_prompt_replace_complete(struct client *c, const char *s) size_t size, n, off, idx, used; struct utf8_data *first, *last, *ud; - if (c->prompt_buffer[0].size == 0) - return (0); - size = utf8_strlen(c->prompt_buffer); - + /* Work out where the cursor currently is. */ idx = c->prompt_index; if (idx != 0) idx--; + size = utf8_strlen(c->prompt_buffer); /* Find the word we are in. */ first = &c->prompt_buffer[idx]; @@ -954,7 +954,7 @@ status_prompt_replace_complete(struct client *c, const char *s) last--; if (last->size != 0) last++; - if (last <= first) + if (last < first) return (0); if (s == NULL) { used = 0; @@ -1071,7 +1071,15 @@ process_key: } break; case '\011': /* Tab */ - if (status_prompt_replace_complete(c, NULL)) + if (c->prompt_flags & PROMPT_WINDOW) { + s = status_prompt_complete_window_menu(c, c->session, + 0, '\0'); + if (s != NULL) { + free(c->prompt_buffer); + c->prompt_buffer = utf8_fromcstr(s); + c->prompt_index = utf8_strlen(c->prompt_buffer); + } + } else if (status_prompt_replace_complete(c, NULL)) goto changed; break; case KEYC_BSPACE: @@ -1376,6 +1384,11 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start) list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = xstrdup((*cmdent)->name); } + if ((*cmdent)->alias != NULL && + strncmp((*cmdent)->alias, s, slen) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = xstrdup((*cmdent)->alias); + } } o = options_get_only(global_options, "command-alias"); if (o != NULL) { @@ -1450,7 +1463,12 @@ status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key, s = xstrdup(spm->list[idx]); else xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); - if (status_prompt_replace_complete(c, s)) + if (c->prompt_flags & PROMPT_WINDOW) { + free(c->prompt_buffer); + c->prompt_buffer = utf8_fromcstr(s); + c->prompt_index = utf8_strlen(c->prompt_buffer); + c->flags |= CLIENT_REDRAWSTATUS; + } else if (status_prompt_replace_complete(c, s)) c->flags |= CLIENT_REDRAWSTATUS; free(s); } @@ -1544,10 +1562,14 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, menu = menu_create(""); RB_FOREACH(wl, winlinks, &s->windows) { list = xreallocarray(list, size + 1, sizeof *list); - xasprintf(&list[size++], "%s:%d", s->name, wl->idx); - - xasprintf(&tmp, "%s:%d (%s)", s->name, wl->idx, - wl->window->name); + if (c->prompt_flags & PROMPT_WINDOW) { + xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); + xasprintf(&list[size++], "%d", wl->idx); + } else { + xasprintf(&tmp, "%s:%d (%s)", s->name, wl->idx, + wl->window->name); + xasprintf(&list[size++], "%s:%d", s->name, wl->idx); + } item.name = tmp; item.key = '0' + size - 1; item.command = NULL; @@ -1559,8 +1581,11 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, } if (size == 1) { menu_free(menu); - xasprintf(&tmp, "-%c%s", flag, list[0]); - free(list[0]); + if (flag != '\0') { + xasprintf(&tmp, "-%c%s", flag, list[0]); + free(list[0]); + } else + tmp = list[0]; free(list); return (tmp); } @@ -1598,21 +1623,45 @@ status_prompt_complete_sort(const void *a, const void *b) return (strcmp(*aa, *bb)); } +/* Complete a session. */ +static char * +status_prompt_complete_session(char ***list, u_int *size, const char *s, + char flag) +{ + struct session *loop; + char *out, *tmp; + + RB_FOREACH(loop, sessions, &sessions) { + if (*s != '\0' && strncmp(loop->name, s, strlen(s)) != 0) + continue; + *list = xreallocarray(*list, (*size) + 2, sizeof **list); + xasprintf(&(*list)[(*size)++], "%s:", loop->name); + } + out = status_prompt_complete_prefix(*list, *size); + if (out != NULL && flag != '\0') { + xasprintf(&tmp, "-%c%s", flag, out); + free(out); + out = tmp; + } + return (out); +} + /* Complete word. */ static char * status_prompt_complete(struct client *c, const char *word, u_int offset) { - struct session *session, *loop; + struct session *session; const char *s, *colon; - size_t slen; - char **list = NULL, *copy = NULL, *out = NULL, *tmp; + char **list = NULL, *copy = NULL, *out = NULL; char flag = '\0'; u_int size = 0, i; - if (*word == '\0') + if (*word == '\0' && (~c->prompt_flags & PROMPT_TARGET)) return (NULL); - if (strncmp(word, "-t", 2) != 0 && strncmp(word, "-s", 2) != 0) { + if ((~c->prompt_flags & PROMPT_TARGET) && + strncmp(word, "-t", 2) != 0 && + strncmp(word, "-s", 2) != 0) { list = status_prompt_complete_list(&size, word, offset == 0); if (size == 0) out = NULL; @@ -1623,28 +1672,19 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) goto found; } - s = word + 2; - slen = strlen(s); - - flag = word[1]; - offset += 2; - + if (c->prompt_flags & PROMPT_TARGET) { + s = word; + flag = '\0'; + } else { + s = word + 2; + flag = word[1]; + offset += 2; + } colon = strchr(s, ':'); /* If there is no colon, complete as a session. */ if (colon == NULL) { - RB_FOREACH(loop, sessions, &sessions) { - if (strncmp(loop->name, s, strlen(s)) != 0) - continue; - list = xreallocarray(list, size + 2, sizeof *list); - xasprintf(&list[size++], "%s:", loop->name); - } - out = status_prompt_complete_prefix(list, size); - if (out != NULL) { - xasprintf(&tmp, "-%c%s", flag, out); - free(out); - out = tmp; - } + out = status_prompt_complete_session(&list, &size, s, flag); goto found; } diff --git a/tmux.1 b/tmux.1 index 5c873fdd..4911493a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4969,7 +4969,7 @@ session option. Commands related to the status line are as follows: .Bl -tag -width Ds .It Xo Ic command-prompt -.Op Fl 1ikN +.Op Fl 1ikNTW .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client @@ -5028,6 +5028,14 @@ makes the prompt only accept numeric key presses. .Fl i executes the command every time the prompt input changes instead of when the user exits the command prompt. +.Fl T +tells +.Nm +that the prompt is for a target which affects what completions are offered when +.Em Tab +is pressed; +.Fl W +is similar but indicates the prompt is for a window. .Pp The following keys have a special meaning in the command prompt, depending on the value of the diff --git a/tmux.h b/tmux.h index e2443660..52477ed9 100644 --- a/tmux.h +++ b/tmux.h @@ -1602,6 +1602,8 @@ struct client { #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 #define PROMPT_KEY 0x10 +#define PROMPT_WINDOW 0x20 +#define PROMPT_TARGET 0x40 int prompt_flags; struct session *session; From 58fb81d19a9a99eb5dce42dc7b145b9679074fe5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:18:17 +0000 Subject: [PATCH 0399/1006] Complete partial window indexes properly. --- status.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/status.c b/status.c index 77b92717..397faf19 100644 --- a/status.c +++ b/status.c @@ -39,7 +39,7 @@ static void status_prompt_add_history(const char *); static char *status_prompt_complete(struct client *, const char *, u_int); static char *status_prompt_complete_window_menu(struct client *, - struct session *, u_int, char); + struct session *, const char *, u_int, char); struct status_prompt_menu { struct client *c; @@ -1071,15 +1071,7 @@ process_key: } break; case '\011': /* Tab */ - if (c->prompt_flags & PROMPT_WINDOW) { - s = status_prompt_complete_window_menu(c, c->session, - 0, '\0'); - if (s != NULL) { - free(c->prompt_buffer); - c->prompt_buffer = utf8_fromcstr(s); - c->prompt_index = utf8_strlen(c->prompt_buffer); - } - } else if (status_prompt_replace_complete(c, NULL)) + if (status_prompt_replace_complete(c, NULL)) goto changed; break; case KEYC_BSPACE: @@ -1537,7 +1529,7 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size, /* Show complete word menu. */ static char * status_prompt_complete_window_menu(struct client *c, struct session *s, - u_int offset, char flag) + const char *word, u_int offset, char flag) { struct menu *menu; struct menu_item item; @@ -1561,6 +1553,15 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, menu = menu_create(""); RB_FOREACH(wl, winlinks, &s->windows) { + if (word != NULL && *word != '\0') { + xasprintf(&tmp, "%d", wl->idx); + if (strncmp(tmp, word, strlen(word)) != 0) { + free(tmp); + continue; + } + free(tmp); + } + list = xreallocarray(list, size + 1, sizeof *list); if (c->prompt_flags & PROMPT_WINDOW) { xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); @@ -1579,6 +1580,10 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, if (size == height) break; } + if (size == 0) { + menu_free(menu); + return (NULL); + } if (size == 1) { menu_free(menu); if (flag != '\0') { @@ -1656,10 +1661,11 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) char flag = '\0'; u_int size = 0, i; - if (*word == '\0' && (~c->prompt_flags & PROMPT_TARGET)) + if (*word == '\0' && + ((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0)) return (NULL); - if ((~c->prompt_flags & PROMPT_TARGET) && + if (((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0) && strncmp(word, "-t", 2) != 0 && strncmp(word, "-s", 2) != 0) { list = status_prompt_complete_list(&size, word, offset == 0); @@ -1672,7 +1678,7 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) goto found; } - if (c->prompt_flags & PROMPT_TARGET) { + if (c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) { s = word; flag = '\0'; } else { @@ -1680,6 +1686,13 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) flag = word[1]; offset += 2; } + + /* If this is a window completion, open the window menu. */ + if (c->prompt_flags & PROMPT_WINDOW) { + out = status_prompt_complete_window_menu(c, c->session, s, + offset, '\0'); + goto found; + } colon = strchr(s, ':'); /* If there is no colon, complete as a session. */ @@ -1700,8 +1713,8 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) if (session == NULL) goto found; } - out = status_prompt_complete_window_menu(c, session, offset, - flag); + out = status_prompt_complete_window_menu(c, session, colon + 1, + offset, flag); if (out == NULL) return (NULL); } From a3cbc014c386fd1f171e3139ed71c67503e1ccb3 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:19:04 +0000 Subject: [PATCH 0400/1006] Use formats for status-style and message-style. --- status.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/status.c b/status.c index 397faf19..a4844294 100644 --- a/status.c +++ b/status.c @@ -346,8 +346,15 @@ status_redraw(struct client *c) if (c->tty.sy == 0 || lines == 0) return (1); + /* Create format tree. */ + flags = FORMAT_STATUS; + if (c->flags & CLIENT_STATUSFORCE) + flags |= FORMAT_FORCE; + ft = format_create(c, NULL, FORMAT_NONE, flags); + format_defaults(ft, c, NULL, NULL, NULL); + /* Set up default colour. */ - style_apply(&gc, s->options, "status-style", NULL); + style_apply(&gc, s->options, "status-style", ft); fg = options_get_number(s->options, "status-fg"); if (fg != 8) gc.fg = fg; @@ -367,13 +374,6 @@ status_redraw(struct client *c) } screen_write_start(&ctx, NULL, &sl->screen); - /* Create format tree. */ - flags = FORMAT_STATUS; - if (c->flags & CLIENT_STATUSFORCE) - flags |= FORMAT_FORCE; - ft = format_create(c, NULL, FORMAT_NONE, flags); - format_defaults(ft, c, NULL, NULL, NULL); - /* Write the status lines. */ o = options_get(s->options, "status-format"); if (o == NULL) { @@ -490,6 +490,7 @@ status_message_redraw(struct client *c) size_t len; u_int lines, offset; struct grid_cell gc; + struct format_tree *ft; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); @@ -504,7 +505,9 @@ status_message_redraw(struct client *c) if (len > c->tty.sx) len = c->tty.sx; - style_apply(&gc, s->options, "message-style", NULL); + ft = format_create_defaults(NULL, c, NULL, NULL, NULL); + style_apply(&gc, s->options, "message-style", ft); + format_free(ft); screen_write_start(&ctx, NULL, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); @@ -636,6 +639,7 @@ status_prompt_redraw(struct client *c) u_int i, lines, offset, left, start, width; u_int pcursor, pwidth; struct grid_cell gc, cursorgc; + struct format_tree *ft; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); @@ -646,10 +650,12 @@ status_prompt_redraw(struct client *c) lines = 1; screen_init(sl->active, c->tty.sx, lines, 0); + ft = format_create_defaults(NULL, c, NULL, NULL, NULL); if (c->prompt_mode == PROMPT_COMMAND) - style_apply(&gc, s->options, "message-command-style", NULL); + style_apply(&gc, s->options, "message-command-style", ft); else - style_apply(&gc, s->options, "message-style", NULL); + style_apply(&gc, s->options, "message-style", ft); + format_free(ft); memcpy(&cursorgc, &gc, sizeof cursorgc); cursorgc.attr ^= GRID_ATTR_REVERSE; From 78595457f965975cf5a24e8bbab6dcb153020b6e Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:24:28 +0000 Subject: [PATCH 0401/1006] Add 'e' key in buffer mode to open the buffer in an editor. --- cmd-display-menu.c | 2 +- format-draw.c | 2 +- job.c | 7 +++ options-table.c | 6 ++ paste.c | 9 +++ popup.c | 9 ++- server.c | 1 + tmux.1 | 5 ++ tmux.c | 1 + tmux.h | 5 +- window-buffer.c | 134 +++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 177 insertions(+), 4 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 0a5c7f78..ae322444 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -313,7 +313,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, - cmd, cwd, tc, target) != 0) + cmd, cwd, tc, target, NULL, NULL) != 0) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } diff --git a/format-draw.c b/format-draw.c index 3ac33ce4..4a4fc6bc 100644 --- a/format-draw.c +++ b/format-draw.c @@ -738,7 +738,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, /* * Draw the screens. How they are arranged depends on where the list - * appearsq. + * appears. */ switch (list_align) { case STYLE_ALIGN_DEFAULT: diff --git a/job.c b/job.c index 997a6574..efc0199e 100644 --- a/job.c +++ b/job.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -285,6 +286,12 @@ job_check_died(pid_t pid, int status) } if (job == NULL) return; + if (WIFSTOPPED(status)) { + if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) + return; + killpg(job->pid, SIGCONT); + return; + } log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); job->status = status; diff --git a/options-table.c b/options-table.c index a89758c5..5ca63a78 100644 --- a/options-table.c +++ b/options-table.c @@ -210,6 +210,12 @@ const struct options_table_entry options_table[] = { .default_str = "screen" }, + { .name = "editor", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = _PATH_VI + }, + { .name = "escape-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, diff --git a/paste.c b/paste.c index 0c9bc7b5..e98b5771 100644 --- a/paste.c +++ b/paste.c @@ -297,6 +297,15 @@ paste_set(char *data, size_t size, const char *name, char **cause) return (0); } +/* Set paste data without otherwise changing it. */ +void +paste_replace(struct paste_buffer *pb, char *data, size_t size) +{ + free(pb->data); + pb->data = data; + pb->size = size; +} + /* Convert start of buffer into a nice string. */ char * paste_make_sample(struct paste_buffer *pb) diff --git a/popup.c b/popup.c index 5d39e599..160b5aa3 100644 --- a/popup.c +++ b/popup.c @@ -40,6 +40,8 @@ struct popup_data { struct job *job; struct input_ctx *ictx; int status; + popup_close_cb cb; + void *arg; u_int px; u_int py; @@ -150,6 +152,9 @@ popup_free_cb(struct client *c) struct cmdq_item *item = pd->item; u_int i; + if (pd->cb != NULL) + pd->cb(pd->status, pd->arg); + if (item != NULL) { if (pd->ictx != NULL && cmdq_get_client(item) != NULL && @@ -403,7 +408,7 @@ int popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, u_int sy, u_int nlines, const char **lines, const char *shellcmd, const char *cmd, const char *cwd, struct client *c, - struct cmd_find_state *fs) + struct cmd_find_state *fs, popup_close_cb cb, void *arg) { struct popup_data *pd; u_int i; @@ -422,6 +427,8 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->c = c; pd->c->references++; + pd->cb = cb; + pd->arg = arg; pd->status = 128 + SIGHUP; if (fs != NULL) diff --git a/server.c b/server.c index 55430e73..be655b6a 100644 --- a/server.c +++ b/server.c @@ -481,4 +481,5 @@ server_child_stopped(pid_t pid, int status) } } } + job_check_died(pid, status); } diff --git a/tmux.1 b/tmux.1 index 4911493a..d0c4f25b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3113,6 +3113,10 @@ Set the time in milliseconds for which waits after an escape is input to determine if it is part of a function or meta key sequences. The default is 500 milliseconds. +.It Ic editor Ar shell-command +Set the command used when +.Nm +runs an editor. .It Xo Ic exit-empty .Op Ic on | off .Xc @@ -5339,6 +5343,7 @@ The following keys may be used in buffer mode: .It Li "P" Ta "Paste tagged buffers" .It Li "d" Ta "Delete selected buffer" .It Li "D" Ta "Delete tagged buffers" +.It Li "e" Ta "Open the buffer in an editor" .It Li "f" Ta "Enter a format to filter items" .It Li "O" Ta "Change sort field" .It Li "r" Ta "Reverse sort order" diff --git a/tmux.c b/tmux.c index db86dc52..137c7eeb 100644 --- a/tmux.c +++ b/tmux.c @@ -443,6 +443,7 @@ main(int argc, char **argv) /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { + options_set_string(global_options, "editor", 0, "%s", s); if (strrchr(s, '/') != NULL) s = strrchr(s, '/') + 1; if (strstr(s, "vi") != NULL) diff --git a/tmux.h b/tmux.h index 52477ed9..c2e7b099 100644 --- a/tmux.h +++ b/tmux.h @@ -1806,6 +1806,7 @@ void paste_free(struct paste_buffer *); void paste_add(const char *, char *, size_t); int paste_rename(const char *, const char *, char **); int paste_set(char *, size_t, const char *, char **); +void paste_replace(struct paste_buffer *, char *, size_t); char *paste_make_sample(struct paste_buffer *); /* format.c */ @@ -2813,12 +2814,14 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int, #define POPUP_WRITEKEYS 0x1 #define POPUP_CLOSEEXIT 0x2 #define POPUP_CLOSEEXITZERO 0x4 +typedef void (*popup_close_cb)(int, void *); u_int popup_width(struct cmdq_item *, u_int, const char **, struct client *, struct cmd_find_state *); u_int popup_height(u_int, const char **); int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, u_int, u_int, const char **, const char *, const char *, - const char *, struct client *, struct cmd_find_state *); + const char *, struct client *, struct cmd_find_state *, + popup_close_cb, void *); /* style.c */ int style_parse(struct style *,const struct grid_cell *, diff --git a/window-buffer.c b/window-buffer.c index dae8899e..53dfeca4 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -18,9 +18,12 @@ #include +#include +#include #include #include #include +#include #include #include "tmux.h" @@ -95,6 +98,13 @@ struct window_buffer_modedata { u_int item_size; }; +struct window_buffer_editdata { + u_int wp_id; + char *path; + char *name; + struct paste_buffer *pb; +}; + static struct window_buffer_itemdata * window_buffer_add_item(struct window_buffer_modedata *data) { @@ -353,6 +363,126 @@ window_buffer_do_paste(void *modedata, void *itemdata, struct client *c, mode_tree_run_command(c, NULL, data->command, item->name); } +static void +window_buffer_finish_edit(struct window_buffer_editdata *ed) +{ + unlink(ed->path); + free(ed->path); + free(ed->name); + free(ed); +} + +static void +window_buffer_edit_close_cb(int status, void *arg) +{ + struct window_buffer_editdata *ed = arg; + FILE *f; + off_t len; + char *buf; + size_t oldlen; + const char *oldbuf; + struct paste_buffer *pb; + struct window_pane *wp; + struct window_buffer_modedata *data; + struct window_mode_entry *wme; + + if (status != 0) { + window_buffer_finish_edit(ed); + return; + } + + pb = paste_get_name(ed->name); + if (pb == NULL || pb != ed->pb) { + window_buffer_finish_edit(ed); + return; + } + + f = fopen(ed->path, "r"); + if (f != NULL) { + fseeko(f, 0, SEEK_END); + len = ftello(f); + fseeko(f, 0, SEEK_SET); + + if (len > 0 && + (uintmax_t)len <= (uintmax_t)SIZE_MAX && + (buf = malloc(len)) != NULL && + fread(buf, len, 1, f) == 1) { + oldbuf = paste_buffer_data(pb, &oldlen); + if (oldlen != '\0' && + oldbuf[oldlen - 1] != '\n' && + buf[len - 1] == '\n') + len--; + if (len != 0) + paste_replace(pb, buf, len); + } + fclose(f); + } + + wp = window_pane_find_by_id(ed->wp_id); + if (wp != NULL) { + wme = TAILQ_FIRST(&wp->modes); + if (wme->mode == &window_buffer_mode) { + data = wme->data; + mode_tree_build(data->data); + mode_tree_draw(data->data); + } + wp->flags |= PANE_REDRAW; + } + window_buffer_finish_edit(ed); +} + +static void +window_buffer_start_edit(struct window_buffer_modedata *data, + struct window_buffer_itemdata *item, struct client *c) +{ + struct paste_buffer *pb; + int fd; + FILE *f; + const char *buf; + size_t len; + struct window_buffer_editdata *ed; + char *cmd; + char path[] = _PATH_TMP "tmux.XXXXXXXX"; + const char *editor; + u_int px, py, sx, sy; + + if ((pb = paste_get_name(item->name)) == NULL) + return; + buf = paste_buffer_data(pb, &len); + + editor = options_get_string(global_options, "editor"); + if (*editor == '\0') + return; + + fd = mkstemp(path); + if (fd == -1) + return; + f = fdopen(fd, "w"); + if (fwrite(buf, len, 1, f) != 1) { + fclose(f); + return; + } + fclose(f); + + ed = xcalloc(1, sizeof *ed); + ed->wp_id = data->wp->id; + ed->path = xstrdup(path); + ed->name = xstrdup(paste_buffer_name(pb)); + ed->pb = pb; + + sx = c->tty.sx * 9 / 10; + sy = c->tty.sy * 9 / 10; + px = (c->tty.sx / 2) - (sx / 2); + py = (c->tty.sy / 2) - (sy / 2); + + xasprintf(&cmd, "%s %s", editor, path); + if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, + 0, NULL, cmd, NULL, _PATH_TMP, c, NULL, window_buffer_edit_close_cb, + ed) != 0) + window_buffer_finish_edit(ed); + free(cmd); +} + static void window_buffer_key(struct window_mode_entry *wme, struct client *c, __unused struct session *s, __unused struct winlink *wl, key_code key, @@ -366,6 +496,10 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c, finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); switch (key) { + case 'e': + item = mode_tree_get_current(mtd); + window_buffer_start_edit(data, item, c); + break; case 'd': item = mode_tree_get_current(mtd); window_buffer_do_delete(data, item, c, key); From edeb81ba9e7ef3665f055cc54fee6cfdb3979fea Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:25:24 +0000 Subject: [PATCH 0402/1006] Add -e for new-session to set environment variables. --- cmd-new-session.c | 16 +++++++++++----- tmux.1 | 6 ++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index f08155c0..9815e1e1 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -39,10 +39,10 @@ const struct cmd_entry cmd_new_session_entry = { .name = "new-session", .alias = "new", - .args = { "Ac:dDEF:n:Ps:t:x:Xy:", 0, -1 }, - .usage = "[-AdDEPX] [-c start-directory] [-F format] [-n window-name] " - "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " - "[-y height] [command]", + .args = { "Ac:dDe:EF:n:Ps:t:x:Xy:", 0, -1 }, + .usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] " + "[-n window-name] [-s session-name] " + CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -75,7 +75,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct options *oo; struct termios tio, *tiop; struct session_group *sg = NULL; - const char *errstr, *template, *group, *tmp; + const char *errstr, *template, *group, *tmp, *add; char *cause, *cwd = NULL, *cp, *newname = NULL; char *name, *prefix = NULL; int detached, already_attached, is_control = 0; @@ -83,6 +83,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct spawn_context sc; enum cmd_retval retval; struct cmd_find_state fs; + struct args_value *value; if (cmd_get_entry(self) == &cmd_has_session_entry) { /* @@ -254,6 +255,11 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) env = environ_create(); if (c != NULL && !args_has(args, 'E')) environ_update(global_s_options, c->environ, env); + add = args_first_value(args, 'e', &value); + while (add != NULL) { + environ_put(env, add, 0); + add = args_next_value(&value); + } s = session_create(prefix, newname, cwd, env, oo, tiop); /* Spawn the initial window. */ diff --git a/tmux.1 b/tmux.1 index d0c4f25b..5b7bb3db 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1094,6 +1094,7 @@ Lock all clients attached to .It Xo Ic new-session .Op Fl AdDEPX .Op Fl c Ar start-directory +.Op Fl e Ar environment .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name @@ -1200,6 +1201,11 @@ If is used, the .Ic update-environment option will not be applied. +.Fl e +takes the form +.Ql VARIABLE=value +and sets an environment variable for the newly created session; it may be +specified multiple times. .It Xo Ic refresh-client .Op Fl cDlLRSU .Op Fl C Ar XxY From 379ca54c80837d09dff53ffa7b9ea3b80d87096b Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:27:08 +0000 Subject: [PATCH 0403/1006] Rename and tidy some stuff in struct tty_ctx. --- screen-write.c | 3 + tmux.h | 14 +++-- tty.c | 148 +++++++++++++++++++++++-------------------------- 3 files changed, 81 insertions(+), 84 deletions(-) diff --git a/screen-write.c b/screen-write.c index e3e51020..32424b39 100644 --- a/screen-write.c +++ b/screen-write.c @@ -112,6 +112,9 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, ttyctx->wp = ctx->wp; + ttyctx->sx = screen_size_x(s); + ttyctx->sy = screen_size_y(s); + ttyctx->ocx = s->cx; ttyctx->ocy = s->cy; diff --git a/tmux.h b/tmux.h index c2e7b099..e9a5db8a 100644 --- a/tmux.h +++ b/tmux.h @@ -1305,19 +1305,21 @@ struct tty_ctx { u_int orupper; u_int orlower; - /* Pane offset. */ + /* Target region (usually pane) offset and size. */ u_int xoff; u_int yoff; + u_int sx; + u_int sy; /* The background colour used for clearing (erasing). */ u_int bg; - /* Window offset and size. */ + /* Containing region (usually window) offset and size. */ int bigger; - u_int ox; - u_int oy; - u_int sx; - u_int sy; + u_int wox; + u_int woy; + u_int wsx; + u_int wsy; }; /* Saved message entry. */ diff --git a/tty.c b/tty.c index e378b45d..aaf4d639 100644 --- a/tty.c +++ b/tty.c @@ -75,9 +75,8 @@ static void tty_default_attributes(struct tty *, struct window_pane *, #define tty_use_margin(tty) \ (tty->term->flags & TERM_DECSLRM) - -#define tty_pane_full_width(tty, ctx) \ - ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) +#define tty_full_width(tty, ctx) \ + ((ctx)->xoff == 0 && (ctx)->sx >= (tty)->sx) #define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */) #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8) @@ -897,9 +896,7 @@ tty_update_client_offset(struct client *c) static int tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - - return (ctx->orlower - ctx->orupper >= screen_size_y(wp->screen) / 2); + return (ctx->orlower - ctx->orupper >= ctx->sy / 2); } /* @@ -933,7 +930,6 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) { struct client *c = tty->client; struct window_pane *wp = ctx->wp; - struct screen *s = wp->screen; u_int i; /* @@ -947,7 +943,7 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) } if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { - for (i = ctx->ocy; i < screen_size_y(s); i++) + for (i = ctx->ocy; i < ctx->sy; i++) tty_draw_pane(tty, ctx, i); } else { for (i = ctx->orupper; i <= ctx->orlower; i++) @@ -970,8 +966,8 @@ tty_is_visible(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, else lines = 0; - if (xoff + nx <= ctx->ox || xoff >= ctx->ox + ctx->sx || - yoff + ny <= ctx->oy || yoff >= lines + ctx->oy + ctx->sy) + if (xoff + nx <= ctx->wox || xoff >= ctx->wox + ctx->wsx || + yoff + ny <= ctx->woy || yoff >= lines + ctx->woy + ctx->wsy) return (0); return (1); } @@ -986,28 +982,28 @@ tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, if (!tty_is_visible(tty, ctx, px, py, nx, 1)) return (0); - *ry = ctx->yoff + py - ctx->oy; + *ry = ctx->yoff + py - ctx->woy; - if (xoff >= ctx->ox && xoff + nx <= ctx->ox + ctx->sx) { + if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) { /* All visible. */ *i = 0; - *x = ctx->xoff + px - ctx->ox; + *x = ctx->xoff + px - ctx->wox; *rx = nx; - } else if (xoff < ctx->ox && xoff + nx > ctx->ox + ctx->sx) { + } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) { /* Both left and right not visible. */ - *i = ctx->ox; + *i = ctx->wox; *x = 0; - *rx = ctx->sx; - } else if (xoff < ctx->ox) { + *rx = ctx->wsx; + } else if (xoff < ctx->wox) { /* Left not visible. */ - *i = ctx->ox - (ctx->xoff + px); + *i = ctx->wox - (ctx->xoff + px); *x = 0; *rx = nx - *i; } else { /* Right not visible. */ *i = 0; - *x = (ctx->xoff + px) - ctx->ox; - *rx = ctx->sx - *x; + *x = (ctx->xoff + px) - ctx->wox; + *rx = ctx->wsx - *x; } if (*rx > nx) fatalx("%s: x too big, %u > %u", __func__, *rx, nx); @@ -1083,50 +1079,50 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, if (!tty_is_visible(tty, ctx, px, py, nx, ny)) return (0); - if (xoff >= ctx->ox && xoff + nx <= ctx->ox + ctx->sx) { + if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) { /* All visible. */ *i = 0; - *x = ctx->xoff + px - ctx->ox; + *x = ctx->xoff + px - ctx->wox; *rx = nx; - } else if (xoff < ctx->ox && xoff + nx > ctx->ox + ctx->sx) { + } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) { /* Both left and right not visible. */ - *i = ctx->ox; + *i = ctx->wox; *x = 0; - *rx = ctx->sx; - } else if (xoff < ctx->ox) { + *rx = ctx->wsx; + } else if (xoff < ctx->wox) { /* Left not visible. */ - *i = ctx->ox - (ctx->xoff + px); + *i = ctx->wox - (ctx->xoff + px); *x = 0; *rx = nx - *i; } else { /* Right not visible. */ *i = 0; - *x = (ctx->xoff + px) - ctx->ox; - *rx = ctx->sx - *x; + *x = (ctx->xoff + px) - ctx->wox; + *rx = ctx->wsx - *x; } if (*rx > nx) fatalx("%s: x too big, %u > %u", __func__, *rx, nx); - if (yoff >= ctx->oy && yoff + ny <= ctx->oy + ctx->sy) { + if (yoff >= ctx->woy && yoff + ny <= ctx->woy + ctx->wsy) { /* All visible. */ *j = 0; - *y = ctx->yoff + py - ctx->oy; + *y = ctx->yoff + py - ctx->woy; *ry = ny; - } else if (yoff < ctx->oy && yoff + ny > ctx->oy + ctx->sy) { + } else if (yoff < ctx->woy && yoff + ny > ctx->woy + ctx->wsy) { /* Both top and bottom not visible. */ - *j = ctx->oy; + *j = ctx->woy; *y = 0; - *ry = ctx->sy; - } else if (yoff < ctx->oy) { + *ry = ctx->wsy; + } else if (yoff < ctx->woy) { /* Top not visible. */ - *j = ctx->oy - (ctx->yoff + py); + *j = ctx->woy - (ctx->yoff + py); *y = 0; *ry = ny - *j; } else { /* Bottom not visible. */ *j = 0; - *y = (ctx->yoff + py) - ctx->oy; - *ry = ctx->sy - *y; + *y = (ctx->yoff + py) - ctx->woy; + *ry = ctx->wsy - *y; } if (*ry > ny) fatalx("%s: y too big, %u > %u", __func__, *ry, ny); @@ -1222,7 +1218,7 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; - u_int nx = screen_size_x(s), i, x, rx, ry; + u_int nx = ctx->sx, i, x, rx, ry; log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger); @@ -1495,8 +1491,8 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), break; } - ctx->bigger = tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, - &ctx->sx, &ctx->sy); + ctx->bigger = tty_window_offset(&c->tty, &ctx->wox, &ctx->woy, + &ctx->wsx, &ctx->wsy); ctx->xoff = wp->xoff; ctx->yoff = wp->yoff; @@ -1514,7 +1510,7 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; if (ctx->bigger || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || tty_fake_bce(tty, wp, ctx->bg) || (!tty_term_has(tty->term, TTYC_ICH) && !tty_term_has(tty->term, TTYC_ICH1))) { @@ -1535,7 +1531,7 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; if (ctx->bigger || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || tty_fake_bce(tty, wp, ctx->bg) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { @@ -1573,7 +1569,7 @@ void tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1) || @@ -1597,7 +1593,7 @@ void tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1) || @@ -1621,23 +1617,20 @@ void tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - u_int nx; tty_default_attributes(tty, wp, ctx->bg); - nx = screen_size_x(wp->screen); - tty_clear_pane_line(tty, ctx, ctx->ocy, 0, nx, ctx->bg); + tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg); } void tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - u_int nx; + u_int nx = ctx->sx - ctx->ocx; tty_default_attributes(tty, wp, ctx->bg); - nx = screen_size_x(wp->screen) - ctx->ocx; tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg); } @@ -1660,7 +1653,7 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) return; if (ctx->bigger || - (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || (!tty_term_has(tty->term, TTYC_RI) && @@ -1692,7 +1685,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) return; if (ctx->bigger || - (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || wp->sx == 1 || @@ -1731,7 +1724,7 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) u_int i; if (ctx->bigger || - (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || wp->sx == 1 || @@ -1768,7 +1761,7 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) u_int i; if (ctx->bigger || - (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || (!tty_term_has(tty->term, TTYC_RI) && @@ -1801,18 +1794,18 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, wp, ctx->bg); - tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); + tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); px = 0; - nx = screen_size_x(wp->screen); + nx = ctx->sx; py = ctx->ocy + 1; - ny = screen_size_y(wp->screen) - ctx->ocy - 1; + ny = ctx->sy - ctx->ocy - 1; tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); px = ctx->ocx; - nx = screen_size_x(wp->screen) - ctx->ocx; + nx = ctx->sx - ctx->ocx; py = ctx->ocy; tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg); @@ -1826,11 +1819,11 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, wp, ctx->bg); - tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); + tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); px = 0; - nx = screen_size_x(wp->screen); + nx = ctx->sx; py = 0; ny = ctx->ocy; @@ -1851,13 +1844,13 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, wp, ctx->bg); - tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); + tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); px = 0; - nx = screen_size_x(wp->screen); + nx = ctx->sx; py = 0; - ny = screen_size_y(wp->screen); + ny = ctx->sy; tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); } @@ -1866,7 +1859,6 @@ void tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - struct screen *s = wp->screen; u_int i, j; if (ctx->bigger) { @@ -1876,12 +1868,12 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) tty_attributes(tty, &grid_default_cell, wp); - tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); + tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); - for (j = 0; j < screen_size_y(s); j++) { + for (j = 0; j < ctx->sy; j++) { tty_cursor_pane(tty, ctx, 0, j); - for (i = 0; i < screen_size_x(s); i++) + for (i = 0; i < ctx->sx; i++) tty_putc(tty, 'E'); } } @@ -1892,9 +1884,9 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1)) return; - if (ctx->xoff + ctx->ocx - ctx->ox > tty->sx - 1 && + if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 && ctx->ocy == ctx->orlower && - tty_pane_full_width(tty, ctx)) + tty_full_width(tty, ctx)) tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); @@ -1912,10 +1904,10 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) return; if (ctx->bigger && - (ctx->xoff + ctx->ocx < ctx->ox || - ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) { + (ctx->xoff + ctx->ocx < ctx->wox || + ctx->xoff + ctx->ocx + ctx->num > ctx->wox + ctx->wsx)) { if (!ctx->wrapped || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || (tty->term->flags & TERM_NOXENL) || ctx->xoff + ctx->ocx != 0 || ctx->yoff + ctx->ocy != tty->cy + 1 || @@ -2052,8 +2044,8 @@ static void tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower) { - tty_region(tty, ctx->yoff + rupper - ctx->oy, - ctx->yoff + rlower - ctx->oy); + tty_region(tty, ctx->yoff + rupper - ctx->woy, + ctx->yoff + rlower - ctx->woy); } /* Set region at absolute position. */ @@ -2096,8 +2088,8 @@ tty_margin_off(struct tty *tty) static void tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) { - tty_margin(tty, ctx->xoff - ctx->ox, - ctx->xoff + ctx->wp->sx - 1 - ctx->ox); + tty_margin(tty, ctx->xoff - ctx->wox, + ctx->xoff + ctx->wp->sx - 1 - ctx->wox); } /* Set margin at absolute position. */ @@ -2130,7 +2122,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) { if (!ctx->wrapped || - !tty_pane_full_width(tty, ctx) || + !tty_full_width(tty, ctx) || (tty->term->flags & TERM_NOXENL) || ctx->xoff + cx != 0 || ctx->yoff + cy != tty->cy + 1 || @@ -2145,7 +2137,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, static void tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) { - tty_cursor(tty, ctx->xoff + cx - ctx->ox, ctx->yoff + cy - ctx->oy); + tty_cursor(tty, ctx->xoff + cx - ctx->wox, ctx->yoff + cy - ctx->woy); } /* Move cursor to absolute position. */ From 9605b080f6c942ff2e51a2ba538cccc91c91c161 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:34:08 +0000 Subject: [PATCH 0404/1006] Do not hoke into struct window_pane from the tty code and instead set everything up in tty_ctx. Provide a way to initialize the tty_ctx from a callback and use it to let popups draw directly through input_parse in the same way as panes do, rather than forcing a full redraw on every change. --- cmd-display-panes.c | 8 +- format-draw.c | 8 +- input.c | 37 ++--- menu.c | 16 +- mode-tree.c | 2 +- popup.c | 70 +++++++-- screen-redraw.c | 18 ++- screen-write.c | 167 +++++++++++++++++--- server-client.c | 19 +-- server-fn.c | 2 +- status.c | 6 +- tmux.h | 55 +++++-- tty.c | 370 +++++++++++++++++++------------------------- window-clock.c | 2 +- window-copy.c | 24 +-- window.c | 20 --- 16 files changed, 468 insertions(+), 356 deletions(-) diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 0f12b14b..a13a06ae 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -131,9 +131,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, gc.bg = active_colour; else gc.bg = colour; - gc.flags |= GRID_FLAG_NOPALETTE; - - tty_attributes(tty, &gc, wp); + tty_attributes(tty, &gc, &grid_default_cell, NULL); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; @@ -160,9 +158,7 @@ draw_text: gc.fg = active_colour; else gc.fg = colour; - gc.flags |= GRID_FLAG_NOPALETTE; - - tty_attributes(tty, &gc, wp); + tty_attributes(tty, &gc, &grid_default_cell, NULL); tty_puts(tty, buf); tty_cursor(tty, 0, 0); diff --git a/format-draw.c b/format-draw.c index 4a4fc6bc..3751082e 100644 --- a/format-draw.c +++ b/format-draw.c @@ -242,7 +242,7 @@ format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx, /* If there is no list left, pass off to the no list function. */ if (width_list == 0) { - screen_write_start(&ctx, NULL, left); + screen_write_start(&ctx, left); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_stop(&ctx); @@ -334,7 +334,7 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx, /* If there is no list left, pass off to the no list function. */ if (width_list == 0) { - screen_write_start(&ctx, NULL, centre); + screen_write_start(&ctx, centre); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_stop(&ctx); @@ -431,7 +431,7 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, /* If there is no list left, pass off to the no list function. */ if (width_list == 0) { - screen_write_start(&ctx, NULL, right); + screen_write_start(&ctx, right); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_stop(&ctx); @@ -536,7 +536,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, */ for (i = 0; i < TOTAL; i++) { screen_init(&s[i], size, 1, 0); - screen_write_start(&ctx[i], NULL, &s[i]); + screen_write_start(&ctx[i], &s[i]); screen_write_clearendofline(&ctx[i], current_default.bg); width[i] = 0; } diff --git a/input.c b/input.c index 8485a17a..03b81c69 100644 --- a/input.c +++ b/input.c @@ -842,9 +842,9 @@ input_reset(struct input_ctx *ictx, int clear) if (clear && wp != NULL) { if (TAILQ_EMPTY(&wp->modes)) - screen_write_start(sctx, wp, &wp->base); + screen_write_start_pane(sctx, wp, &wp->base); else - screen_write_start(sctx, NULL, &wp->base); + screen_write_start(sctx, &wp->base); screen_write_reset(sctx); screen_write_stop(sctx); } @@ -960,9 +960,9 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) /* NULL wp if there is a mode set as don't want to update the tty. */ if (TAILQ_EMPTY(&wp->modes)) - screen_write_start(sctx, wp, &wp->base); + screen_write_start_pane(sctx, wp, &wp->base); else - screen_write_start(sctx, NULL, &wp->base); + screen_write_start(sctx, &wp->base); log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, ictx->state->name, len, (int)len, buf); @@ -973,15 +973,15 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) /* Parse given input for screen. */ void -input_parse_screen(struct input_ctx *ictx, struct screen *s, u_char *buf, - size_t len) +input_parse_screen(struct input_ctx *ictx, struct screen *s, + screen_write_init_ctx_cb cb, void *arg, u_char *buf, size_t len) { struct screen_write_ctx *sctx = &ictx->ctx; if (len == 0) return; - screen_write_start(sctx, NULL, s); + screen_write_start_callback(sctx, s, cb, arg); input_parse(ictx, buf, len); screen_write_stop(sctx); } @@ -1632,7 +1632,6 @@ static void input_csi_dispatch_rm_private(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; - struct window_pane *wp = ictx->wp; struct grid_cell *gc = &ictx->cell.cell; u_int i; @@ -1677,16 +1676,10 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) break; case 47: case 1047: - if (wp != NULL) - window_pane_alternate_off(wp, gc, 0); - else - screen_alternate_off(sctx->s, gc, 0); + screen_write_alternateoff(sctx, gc, 0); break; case 1049: - if (wp != NULL) - window_pane_alternate_off(wp, gc, 1); - else - screen_alternate_off(sctx->s, gc, 1); + screen_write_alternateoff(sctx, gc, 1); break; case 2004: screen_write_mode_clear(sctx, MODE_BRACKETPASTE); @@ -1782,16 +1775,10 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) break; case 47: case 1047: - if (wp != NULL) - window_pane_alternate_on(wp, gc, 0); - else - screen_alternate_on(sctx->s, gc, 0); + screen_write_alternateon(sctx, gc, 0); break; case 1049: - if (wp != NULL) - window_pane_alternate_on(wp, gc, 1); - else - screen_alternate_on(sctx->s, gc, 1); + screen_write_alternateon(sctx, gc, 1); break; case 2004: screen_write_mode_set(sctx, MODE_BRACKETPASTE); @@ -2595,7 +2582,7 @@ input_osc_52(struct input_ctx *ictx, const char *p) return; } - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_setselection(&ctx, out, outlen); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); diff --git a/menu.c b/menu.c index e78999c6..07fc8fa8 100644 --- a/menu.c +++ b/menu.c @@ -130,14 +130,12 @@ menu_free(struct menu *menu) free(menu); } -static int +static struct screen * menu_mode_cb(struct client *c, __unused u_int *cx, __unused u_int *cy) { struct menu_data *md = c->overlay_data; - if (~md->flags & MENU_NOMOUSE) - return (MODE_MOUSE_ALL); - return (0); + return (&md->s); } static void @@ -153,13 +151,15 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) style_apply(&gc, c->session->curw->window->options, "mode-style", NULL); - screen_write_start(&ctx, NULL, s); + screen_write_start(&ctx, s); screen_write_clearscreen(&ctx, 8); screen_write_menu(&ctx, menu, md->choice, &gc); screen_write_stop(&ctx); - for (i = 0; i < screen_size_y(&md->s); i++) - tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i); + for (i = 0; i < screen_size_y(&md->s); i++) { + tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, + &grid_default_cell, NULL); + } } static void @@ -349,6 +349,8 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, if (fs != NULL) cmd_find_copy_state(&md->fs, fs); screen_init(&md->s, menu->width + 4, menu->count + 2, 0); + if (~md->flags & MENU_NOMOUSE) + md->s.mode |= MODE_MOUSE_ALL; md->px = px; md->py = py; diff --git a/mode-tree.c b/mode-tree.c index 645e2ae9..8d210d72 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -562,7 +562,7 @@ mode_tree_draw(struct mode_tree_data *mtd) w = mtd->width; h = mtd->height; - screen_write_start(&ctx, NULL, s); + screen_write_start(&ctx, s); screen_write_clearscreen(&ctx, 8); if (mtd->line_size > 10) diff --git a/popup.c b/popup.c index 160b5aa3..9937d586 100644 --- a/popup.c +++ b/popup.c @@ -57,6 +57,44 @@ struct popup_data { u_int lb; }; +static void +popup_redraw_cb(const struct tty_ctx *ttyctx) +{ + struct popup_data *pd = ttyctx->arg; + + pd->c->flags |= CLIENT_REDRAWOVERLAY; +} + +static int +popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) +{ + struct popup_data *pd = ttyctx->arg; + + if (pd->c->flags & CLIENT_REDRAWOVERLAY) + return (-1); + + ttyctx->bigger = 0; + ttyctx->wox = 0; + ttyctx->woy = 0; + ttyctx->wsx = c->tty.sx; + ttyctx->wsy = c->tty.sy; + + ttyctx->xoff = ttyctx->rxoff = pd->px + 1; + ttyctx->yoff = ttyctx->ryoff = pd->py + 1; + + return (1); +} + +static void +popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) +{ + struct popup_data *pd = ctx->arg; + + ttyctx->redraw_cb = popup_redraw_cb; + ttyctx->set_client_cb = popup_set_client_cb; + ttyctx->arg = pd; +} + static void popup_write_screen(struct client *c, struct popup_data *pd) { @@ -72,7 +110,7 @@ popup_write_screen(struct client *c, struct popup_data *pd) else format_defaults(ft, c, NULL, NULL, NULL); - screen_write_start(&ctx, NULL, &pd->s); + screen_write_start(&ctx, &pd->s); screen_write_clearscreen(&ctx, 8); y = 0; @@ -98,7 +136,7 @@ popup_write_screen(struct client *c, struct popup_data *pd) screen_write_stop(&ctx); } -static int +static struct screen * popup_mode_cb(struct client *c, u_int *cx, u_int *cy) { struct popup_data *pd = c->overlay_data; @@ -107,7 +145,7 @@ popup_mode_cb(struct client *c, u_int *cx, u_int *cy) return (0); *cx = pd->px + 1 + pd->s.cx; *cy = pd->py + 1 + pd->s.cy; - return (pd->s.mode); + return (&pd->s); } static int @@ -132,7 +170,7 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) u_int i, px = pd->px, py = pd->py; screen_init(&s, pd->sx, pd->sy, 0); - screen_write_start(&ctx, NULL, &s); + screen_write_start(&ctx, &s); screen_write_clearscreen(&ctx, 8); screen_write_box(&ctx, pd->sx, pd->sy); screen_write_cursormove(&ctx, 1, 1, 0); @@ -140,8 +178,10 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) screen_write_stop(&ctx); c->overlay_check = NULL; - for (i = 0; i < pd->sy; i++) - tty_draw_line(tty, NULL, &s, 0, i, pd->sx, px, py + i); + for (i = 0; i < pd->sy; i++){ + tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, + &grid_default_cell, NULL); + } c->overlay_check = popup_check_cb; } @@ -327,15 +367,23 @@ popup_job_update_cb(struct job *job) { struct popup_data *pd = job_get_data(job); struct evbuffer *evb = job_get_event(job)->input; + struct client *c = pd->c; struct screen *s = &pd->s; void *data = EVBUFFER_DATA(evb); size_t size = EVBUFFER_LENGTH(evb); - if (size != 0) { - input_parse_screen(pd->ictx, s, data, size); - evbuffer_drain(evb, size); - pd->c->flags |= CLIENT_REDRAWOVERLAY; - } + if (size == 0) + return; + + c->overlay_check = NULL; + c->tty.flags &= ~TTY_FREEZE; + + input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size); + + c->tty.flags |= TTY_FREEZE; + c->overlay_check = popup_check_cb; + + evbuffer_drain(evb, size); } static void diff --git a/screen-redraw.c b/screen-redraw.c index 19ed6305..0f83479c 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -346,7 +346,7 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, screen_init(&wp->status_screen, width, 1, 0); wp->status_screen.mode = 0; - screen_write_start(&ctx, NULL, &wp->status_screen); + screen_write_start(&ctx, &wp->status_screen); gc.attr |= GRID_ATTR_CHARSET; for (i = 0; i < width; i++) @@ -423,7 +423,8 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) if (ctx->statustop) yoff += ctx->statuslines; - tty_draw_line(tty, NULL, s, i, 0, width, x, yoff - ctx->oy); + tty_draw_line(tty, s, i, 0, width, x, yoff - ctx->oy, + &grid_default_cell, NULL); } tty_cursor(tty, 0, 0); } @@ -615,7 +616,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) } } - tty_attributes(tty, gc, NULL); + tty_attributes(tty, gc, &grid_default_cell, NULL); if (ctx->statustop) tty_cursor(tty, i, ctx->statuslines + j); else @@ -676,8 +677,10 @@ screen_redraw_draw_status(struct screen_redraw_ctx *ctx) y = 0; else y = c->tty.sy - ctx->statuslines; - for (i = 0; i < ctx->statuslines; i++) - tty_draw_line(tty, NULL, s, 0, i, UINT_MAX, 0, y + i); + for (i = 0; i < ctx->statuslines; i++) { + tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i, + &grid_default_cell, NULL); + } } /* Draw one pane. */ @@ -688,6 +691,7 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) struct window *w = c->session->curw->window; struct tty *tty = &c->tty; struct screen *s; + struct grid_cell defaults; u_int i, j, top, x, y, width; log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id); @@ -731,6 +735,8 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u", __func__, c->name, wp->id, i, j, x, y, width); - tty_draw_line(tty, wp, s, i, j, width, x, y); + tty_default_colours(&defaults, wp); + tty_draw_line(tty, s, i, j, width, x, y, &defaults, + wp->palette); } } diff --git a/screen-write.c b/screen-write.c index 32424b39..909b8531 100644 --- a/screen-write.c +++ b/screen-write.c @@ -27,8 +27,8 @@ static void screen_write_collect_clear(struct screen_write_ctx *, u_int, u_int); static int screen_write_collect_clear_end(struct screen_write_ctx *, u_int, u_int, u_int); -static int screen_write_collect_clear_start(struct screen_write_ctx *, u_int, - u_int, u_int); +static int screen_write_collect_clear_start(struct screen_write_ctx *, + u_int, u_int, u_int); static void screen_write_collect_scroll(struct screen_write_ctx *); static void screen_write_collect_flush(struct screen_write_ctx *, int, const char *); @@ -101,6 +101,50 @@ screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy) evtimer_add(&w->offset_timer, &tv); } +/* Do a full redraw. */ +static void +screen_write_redraw_cb(const struct tty_ctx *ttyctx) +{ + struct window_pane *wp = ttyctx->arg; + + wp->flags |= PANE_REDRAW; +} + +/* Update context for client. */ +static int +screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c) +{ + struct window_pane *wp = ttyctx->arg; + + if (c->session->curw->window != wp->window) + return (0); + if (wp->layout_cell == NULL) + return (0); + + if (wp->flags & (PANE_REDRAW|PANE_DROP)) + return (-1); + if (c->flags & CLIENT_REDRAWPANES) { + /* + * Redraw is already deferred to redraw another pane - redraw + * this one also when that happens. + */ + log_debug("adding %%%u to deferred redraw", wp->id); + wp->flags |= PANE_REDRAW; + return (-1); + } + + ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, + &ttyctx->wsx, &ttyctx->wsy); + + ttyctx->xoff = ttyctx->rxoff = wp->xoff; + ttyctx->yoff = ttyctx->ryoff = wp->yoff; + + if (status_at_line(c) == 0) + ttyctx->yoff += status_line_size(c); + + return (1); +} + /* Set up context for TTY command. */ static void screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, @@ -110,17 +154,35 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, memset(ttyctx, 0, sizeof *ttyctx); - ttyctx->wp = ctx->wp; + if (ctx->wp != NULL) { + tty_default_colours(&ttyctx->defaults, ctx->wp); + ttyctx->palette = ctx->wp->palette; + } else { + memcpy(&ttyctx->defaults, &grid_default_cell, + sizeof ttyctx->defaults); + ttyctx->palette = NULL; + } + ttyctx->s = s; ttyctx->sx = screen_size_x(s); ttyctx->sy = screen_size_y(s); ttyctx->ocx = s->cx; ttyctx->ocy = s->cy; - ttyctx->orlower = s->rlower; ttyctx->orupper = s->rupper; + if (ctx->init_ctx_cb != NULL) + ctx->init_ctx_cb(ctx, ttyctx); + else { + ttyctx->redraw_cb = screen_write_redraw_cb; + if (ctx->wp == NULL) + ttyctx->set_client_cb = NULL; + else + ttyctx->set_client_cb = screen_write_set_client_cb; + ttyctx->arg = ctx->wp; + } + if (ctx->wp != NULL && !ctx->sync && (sync || ctx->wp != ctx->wp->window->active)) { @@ -151,18 +213,13 @@ screen_write_free_list(struct screen *s) free(s->write_list); } -/* Initialize writing with a window. */ -void -screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, - struct screen *s) +/* Set up for writing. */ +static void +screen_write_init(struct screen_write_ctx *ctx, struct screen *s) { memset(ctx, 0, sizeof *ctx); - ctx->wp = wp; - if (wp != NULL && s == NULL) - ctx->s = wp->screen; - else - ctx->s = s; + ctx->s = s; if (ctx->s->write_list == NULL) screen_write_make_list(ctx->s); @@ -170,17 +227,51 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, ctx->scrolled = 0; ctx->bg = 8; +} + +/* Initialize writing with a pane. */ +void +screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp, + struct screen *s) +{ + if (s == NULL) + s = wp->screen; + screen_write_init(ctx, s); + ctx->wp = wp; if (log_get_level() != 0) { - if (wp != NULL) { - log_debug("%s: size %ux%u, pane %%%u (at %u,%u)", - __func__, screen_size_x(ctx->s), - screen_size_y(ctx->s), wp->id, wp->xoff, wp->yoff); - } else { - log_debug("%s: size %ux%u, no pane", - __func__, screen_size_x(ctx->s), - screen_size_y(ctx->s)); - } + log_debug("%s: size %ux%u, pane %%%u (at %u,%u)", + __func__, screen_size_x(ctx->s), screen_size_y(ctx->s), + wp->id, wp->xoff, wp->yoff); + } +} + +/* Initialize writing with a callback. */ +void +screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s, + screen_write_init_ctx_cb cb, void *arg) +{ + screen_write_init(ctx, s); + + ctx->init_ctx_cb = cb; + ctx->arg = arg; + + if (log_get_level() != 0) { + log_debug("%s: size %ux%u, with callback", __func__, + screen_size_x(ctx->s), screen_size_y(ctx->s)); + } +} + + +/* Initialize writing. */ +void +screen_write_start(struct screen_write_ctx *ctx, struct screen *s) +{ + screen_write_init(ctx, s); + + if (log_get_level() != 0) { + log_debug("%s: size %ux%u, no pane", __func__, + screen_size_x(ctx->s), screen_size_y(ctx->s)); } } @@ -1799,3 +1890,35 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) tty_write(tty_cmd_rawstring, &ttyctx); } + +/* Turn alternate screen on. */ +void +screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, + int cursor) +{ + struct tty_ctx ttyctx; + struct window_pane *wp = ctx->wp; + + if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) + return; + screen_alternate_on(ctx->s, gc, cursor); + + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.redraw_cb(&ttyctx); +} + +/* Turn alternate screen off. */ +void +screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc, + int cursor) +{ + struct tty_ctx ttyctx; + struct window_pane *wp = ctx->wp; + + if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) + return; + screen_alternate_off(ctx->s, gc, cursor); + + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.redraw_cb(&ttyctx); +} diff --git a/server-client.c b/server-client.c index b21dac1d..a45e9812 100644 --- a/server-client.c +++ b/server-client.c @@ -1542,9 +1542,9 @@ server_client_reset_state(struct client *c) struct tty *tty = &c->tty; struct window *w = c->session->curw->window; struct window_pane *wp = w->active, *loop; - struct screen *s; + struct screen *s = NULL; struct options *oo = c->session->options; - int mode, cursor, flags; + int mode = 0, cursor, flags; u_int cx = 0, cy = 0, ox, oy, sx, sy; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) @@ -1556,17 +1556,14 @@ server_client_reset_state(struct client *c) /* Get mode from overlay if any, else from screen. */ if (c->overlay_draw != NULL) { - s = NULL; - if (c->overlay_mode == NULL) - mode = 0; - else - mode = c->overlay_mode(c, &cx, &cy); - } else { + if (c->overlay_mode != NULL) + s = c->overlay_mode(c, &cx, &cy); + } else s = wp->screen; + if (s != NULL) mode = s->mode; - if (c->prompt_string != NULL || c->message_string != NULL) - mode &= ~MODE_CURSOR; - } + 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/server-fn.c b/server-fn.c index da1371ae..fde1d8e8 100644 --- a/server-fn.c +++ b/server-fn.c @@ -318,7 +318,7 @@ server_destroy_pane(struct window_pane *wp, int notify) if (notify) notify_pane("pane-died", wp); - screen_write_start(&ctx, wp, &wp->base); + screen_write_start_pane(&ctx, wp, &wp->base); screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1); screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1, 0); screen_write_linefeed(&ctx, 1, 8); diff --git a/status.c b/status.c index a4844294..b5442550 100644 --- a/status.c +++ b/status.c @@ -372,7 +372,7 @@ status_redraw(struct client *c) screen_resize(&sl->screen, width, lines, 0); changed = force = 1; } - screen_write_start(&ctx, NULL, &sl->screen); + screen_write_start(&ctx, &sl->screen); /* Write the status lines. */ o = options_get(s->options, "status-format"); @@ -509,7 +509,7 @@ status_message_redraw(struct client *c) style_apply(&gc, s->options, "message-style", ft); format_free(ft); - screen_write_start(&ctx, NULL, sl->active); + screen_write_start(&ctx, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); screen_write_cursormove(&ctx, 0, lines - 1, 0); for (offset = 0; offset < c->tty.sx; offset++) @@ -664,7 +664,7 @@ status_prompt_redraw(struct client *c) if (start > c->tty.sx) start = c->tty.sx; - screen_write_start(&ctx, NULL, sl->active); + screen_write_start(&ctx, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); screen_write_cursormove(&ctx, 0, lines - 1, 0); for (offset = 0; offset < c->tty.sx; offset++) diff --git a/tmux.h b/tmux.h index e9a5db8a..53417cd7 100644 --- a/tmux.h +++ b/tmux.h @@ -55,7 +55,11 @@ struct mouse_event; struct options; struct options_array_item; struct options_entry; +struct screen_write_collect_item; +struct screen_write_collect_line; +struct screen_write_ctx; struct session; +struct tty_ctx; struct tmuxpeer; struct tmuxproc; struct winlink; @@ -786,13 +790,16 @@ struct screen { }; /* Screen write context. */ -struct screen_write_collect_item; -struct screen_write_collect_line; +typedef void (*screen_write_init_ctx_cb)(struct screen_write_ctx *, + struct tty_ctx *); struct screen_write_ctx { struct window_pane *wp; struct screen *s; int sync; + screen_write_init_ctx_cb init_ctx_cb; + void *arg; + struct screen_write_collect_item *item; u_int scrolled; u_int bg; @@ -1252,8 +1259,6 @@ struct tty { struct termios tio; struct grid_cell cell; - - int last_wp; struct grid_cell last_cell; #define TTY_NOCURSOR 0x1 @@ -1285,8 +1290,14 @@ struct tty { }; /* TTY command context. */ +typedef void (*tty_ctx_redraw_cb)(const struct tty_ctx *); +typedef int (*tty_ctx_set_client_cb)(struct tty_ctx *, struct client *); struct tty_ctx { - struct window_pane *wp; + struct screen *s; + + tty_ctx_redraw_cb redraw_cb; + tty_ctx_set_client_cb set_client_cb; + void *arg; const struct grid_cell *cell; int wrapped; @@ -1308,12 +1319,18 @@ struct tty_ctx { /* Target region (usually pane) offset and size. */ u_int xoff; u_int yoff; + u_int rxoff; + u_int ryoff; u_int sx; u_int sy; /* The background colour used for clearing (erasing). */ u_int bg; + /* The default colours and palette. */ + struct grid_cell defaults; + int *palette; + /* Containing region (usually window) offset and size. */ int bigger; u_int wox; @@ -1492,7 +1509,7 @@ RB_HEAD(client_files, client_file); typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); typedef int (*overlay_check_cb)(struct client *, u_int, u_int); -typedef int (*overlay_mode_cb)(struct client *, u_int *, u_int *); +typedef struct screen *(*overlay_mode_cb)(struct client *, u_int *, u_int *); typedef void (*overlay_draw_cb)(struct client *, struct screen_redraw_ctx *); typedef int (*overlay_key_cb)(struct client *, struct key_event *); typedef void (*overlay_free_cb)(struct client *); @@ -1969,7 +1986,7 @@ void tty_update_window_offset(struct window *); void tty_update_client_offset(struct client *); void tty_raw(struct tty *, const char *); void tty_attributes(struct tty *, const struct grid_cell *, - struct window_pane *); + const struct grid_cell *, int *); void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); @@ -1992,8 +2009,8 @@ void tty_send_requests(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); -void tty_draw_line(struct tty *, struct window_pane *, struct screen *, - u_int, u_int, u_int, u_int, u_int); +void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int, + u_int, u_int, const struct grid_cell *, int *); void tty_sync_start(struct tty *); void tty_sync_end(struct tty *); int tty_open(struct tty *, char **); @@ -2024,6 +2041,7 @@ void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); +void tty_default_colours(struct grid_cell *, struct window_pane *); /* tty-term.c */ extern struct tty_terms tty_terms; @@ -2342,8 +2360,8 @@ void input_reset(struct input_ctx *, int); struct evbuffer *input_pending(struct input_ctx *); void input_parse_pane(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); -void input_parse_screen(struct input_ctx *, struct screen *, u_char *, - size_t); +void input_parse_screen(struct input_ctx *, struct screen *, + screen_write_init_ctx_cb, void *, u_char *, size_t); /* input-key.c */ int input_key_pane(struct window_pane *, key_code, struct mouse_event *); @@ -2426,8 +2444,11 @@ char *grid_view_string_cells(struct grid *, u_int, u_int, u_int); /* screen-write.c */ void screen_write_make_list(struct screen *); void screen_write_free_list(struct screen *); -void screen_write_start(struct screen_write_ctx *, struct window_pane *, - struct screen *); +void screen_write_start_pane(struct screen_write_ctx *, + struct window_pane *, struct screen *); +void screen_write_start(struct screen_write_ctx *, struct screen *); +void screen_write_start_callback(struct screen_write_ctx *, struct screen *, + screen_write_init_ctx_cb, void *); void screen_write_stop(struct screen_write_ctx *); void screen_write_reset(struct screen_write_ctx *); size_t printflike(1, 2) screen_write_strlen(const char *, ...); @@ -2481,6 +2502,10 @@ void screen_write_collect_add(struct screen_write_ctx *, void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *); void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); +void screen_write_alternateon(struct screen_write_ctx *, + struct grid_cell *, int); +void screen_write_alternateoff(struct screen_write_ctx *, + struct grid_cell *, int); /* screen-redraw.c */ void screen_redraw_screen(struct client *); @@ -2569,10 +2594,6 @@ struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); int window_pane_destroy_ready(struct window_pane *); void window_pane_resize(struct window_pane *, u_int, u_int); -void window_pane_alternate_on(struct window_pane *, - struct grid_cell *, int); -void window_pane_alternate_off(struct window_pane *, - struct grid_cell *, int); void window_pane_set_palette(struct window_pane *, u_int, int); void window_pane_unset_palette(struct window_pane *, u_int); void window_pane_reset_palette(struct window_pane *); diff --git a/tty.c b/tty.c index aaf4d639..388f6f5f 100644 --- a/tty.c +++ b/tty.c @@ -34,7 +34,7 @@ static int tty_log_fd = -1; -static int tty_client_ready(struct client *, struct window_pane *); +static int tty_client_ready(struct client *); static void tty_set_italics(struct tty *); static int tty_try_colour(struct tty *, int, const char *); @@ -45,12 +45,9 @@ static void tty_cursor_pane_unless_wrap(struct tty *, const struct tty_ctx *, u_int, u_int); static void tty_invalidate(struct tty *); static void tty_colours(struct tty *, const struct grid_cell *); -static void tty_check_fg(struct tty *, struct window_pane *, - struct grid_cell *); -static void tty_check_bg(struct tty *, struct window_pane *, - struct grid_cell *); -static void tty_check_us(struct tty *, struct window_pane *, - struct grid_cell *); +static void tty_check_fg(struct tty *, int *, struct grid_cell *); +static void tty_check_bg(struct tty *, int *, struct grid_cell *); +static void tty_check_us(struct tty *, int *, struct grid_cell *); static void tty_colours_fg(struct tty *, const struct grid_cell *); static void tty_colours_bg(struct tty *, const struct grid_cell *); static void tty_colours_us(struct tty *, const struct grid_cell *); @@ -61,17 +58,17 @@ static void tty_region(struct tty *, u_int, u_int); static void tty_margin_pane(struct tty *, const struct tty_ctx *); static void tty_margin(struct tty *, u_int, u_int); static int tty_large_region(struct tty *, const struct tty_ctx *); -static int tty_fake_bce(const struct tty *, struct window_pane *, u_int); +static int tty_fake_bce(const struct tty *, const struct grid_cell *, + u_int); static void tty_redraw_region(struct tty *, const struct tty_ctx *); static void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, u_int); static void tty_repeat_space(struct tty *, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); static void tty_cell(struct tty *, const struct grid_cell *, - struct window_pane *); -static void tty_default_colours(struct grid_cell *, struct window_pane *); -static void tty_default_attributes(struct tty *, struct window_pane *, - u_int); + const struct grid_cell *, int *); +static void tty_default_attributes(struct tty *, const struct grid_cell *, + int *, u_int); #define tty_use_margin(tty) \ (tty->term->flags & TERM_DECSLRM) @@ -888,6 +885,27 @@ tty_update_client_offset(struct client *c) c->flags |= (CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS); } +/* Get a palette entry. */ +static int +tty_get_palette(int *palette, int c) +{ + int new; + + if (palette == NULL) + return (-1); + + new = -1; + if (c < 8) + new = palette[c]; + else if (c >= 90 && c <= 97) + new = palette[8 + c - 90]; + else if (c & COLOUR_FLAG_256) + new = palette[c & ~COLOUR_FLAG_256]; + if (new == 0) + return (-1); + return (new); +} + /* * Is the region large enough to be worth redrawing once later rather than * probably several times now? Currently yes if it is more than 50% of the @@ -904,18 +922,11 @@ tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) * emulated. */ static int -tty_fake_bce(const struct tty *tty, struct window_pane *wp, u_int bg) +tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg) { - struct grid_cell gc; - if (tty_term_flag(tty->term, TTYC_BCE)) return (0); - - memcpy(&gc, &grid_default_cell, sizeof gc); - if (wp != NULL) - tty_default_colours(&gc, wp); - - if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc.bg)) + if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc->bg)) return (1); return (0); } @@ -929,16 +940,15 @@ static void tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) { struct client *c = tty->client; - struct window_pane *wp = ctx->wp; u_int i; /* - * If region is large, schedule a window redraw. In most cases this is - * likely to be followed by some more scrolling. + * If region is large, schedule a redraw. In most cases this is likely + * to be followed by some more scrolling. */ if (tty_large_region(tty, ctx)) { - log_debug("%s: %s, large redraw of %%%u", __func__, c->name, wp->id); - wp->flags |= PANE_REDRAW; + log_debug("%s: %s large redraw", __func__, c->name); + ctx->redraw_cb(ctx); return; } @@ -977,8 +987,7 @@ static int tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry) { - struct window_pane *wp = ctx->wp; - u_int xoff = wp->xoff + px; + u_int xoff = ctx->rxoff + px; if (!tty_is_visible(tty, ctx, px, py, nx, 1)) return (0); @@ -1013,8 +1022,8 @@ tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, /* Clear a line. */ static void -tty_clear_line(struct tty *tty, struct window_pane *wp, u_int py, u_int px, - u_int nx, u_int bg) +tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, + u_int px, u_int nx, u_int bg) { struct client *c = tty->client; @@ -1025,7 +1034,7 @@ tty_clear_line(struct tty *tty, struct window_pane *wp, u_int py, u_int px, return; /* If genuine BCE is available, can try escape sequences. */ - if (!tty_fake_bce(tty, wp, bg)) { + if (!tty_fake_bce(tty, defaults, bg)) { /* Off the end of the line, use EL if available. */ if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) { tty_cursor(tty, px, py); @@ -1064,7 +1073,7 @@ tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py, log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry)) - tty_clear_line(tty, ctx->wp, ry, x, rx, bg); + tty_clear_line(tty, &ctx->defaults, ry, x, rx, bg); } /* Clamp area position to visible part of pane. */ @@ -1073,8 +1082,7 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, u_int nx, u_int ny, u_int *i, u_int *j, u_int *x, u_int *y, u_int *rx, u_int *ry) { - struct window_pane *wp = ctx->wp; - u_int xoff = wp->xoff + px, yoff = wp->yoff + py; + u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py; if (!tty_is_visible(tty, ctx, px, py, nx, ny)) return (0); @@ -1132,8 +1140,8 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, /* Clear an area, adjusting to visible part of pane. */ static void -tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, - u_int px, u_int nx, u_int bg) +tty_clear_area(struct tty *tty, const struct grid_cell *defaults, u_int py, + u_int ny, u_int px, u_int nx, u_int bg) { struct client *c = tty->client; u_int yy; @@ -1146,7 +1154,7 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, return; /* If genuine BCE is available, can try escape sequences. */ - if (!tty_fake_bce(tty, wp, bg)) { + if (!tty_fake_bce(tty, defaults, bg)) { /* Use ED if clearing off the bottom of the terminal. */ if (px == 0 && px + nx >= tty->sx && @@ -1199,7 +1207,7 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, /* Couldn't use an escape sequence, loop over the lines. */ for (yy = py; yy < py + ny; yy++) - tty_clear_line(tty, wp, yy, px, nx, bg); + tty_clear_line(tty, defaults, yy, px, nx, bg); } /* Clear an area in a pane. */ @@ -1210,24 +1218,26 @@ tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py, u_int i, j, x, y, rx, ry; if (tty_clamp_area(tty, ctx, px, py, nx, ny, &i, &j, &x, &y, &rx, &ry)) - tty_clear_area(tty, ctx->wp, y, ry, x, rx, bg); + tty_clear_area(tty, &ctx->defaults, y, ry, x, rx, bg); } static void tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) { - struct window_pane *wp = ctx->wp; - struct screen *s = wp->screen; - u_int nx = ctx->sx, i, x, rx, ry; + struct screen *s = ctx->s; + u_int nx = ctx->sx, i, x, rx, ry; log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger); if (!ctx->bigger) { - tty_draw_line(tty, wp, s, 0, py, nx, ctx->xoff, ctx->yoff + py); + tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py, + &ctx->defaults, ctx->palette); return; } - if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) - tty_draw_line(tty, wp, s, i, py, rx, x, ry); + if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) { + tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->defaults, + ctx->palette); + } } static const struct grid_cell * @@ -1265,8 +1275,8 @@ tty_check_overlay(struct tty *tty, u_int px, u_int py) } void -tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, - u_int px, u_int py, u_int nx, u_int atx, u_int aty) +tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, + u_int atx, u_int aty, const struct grid_cell *defaults, int *palette) { struct grid *gd = s->grid; struct grid_cell gc, last; @@ -1314,8 +1324,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, gl = NULL; else gl = grid_get_line(gd, gd->hsize + py - 1); - if (wp == NULL || - gl == NULL || + if (gl == NULL || (~gl->flags & GRID_LINE_WRAPPED) || atx != 0 || tty->cx < tty->sx || @@ -1324,8 +1333,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, atx == 0 && px + sx != nx && tty_term_has(tty->term, TTYC_EL1) && - !tty_fake_bce(tty, wp, 8)) { - tty_default_attributes(tty, wp, 8); + !tty_fake_bce(tty, defaults, 8)) { + tty_default_attributes(tty, defaults, palette, 8); tty_cursor(tty, nx - 1, aty); tty_putcode(tty, TTYC_EL1); cleared = 1; @@ -1352,11 +1361,11 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, gcp->us != last.us || ux + width + gcp->data.width > nx || (sizeof buf) - len < gcp->data.size)) { - tty_attributes(tty, &last, wp); + tty_attributes(tty, &last, defaults, palette); if (last.flags & GRID_FLAG_CLEARED) { log_debug("%s: %zu cleared", __func__, len); - tty_clear_line(tty, wp, aty, atx + ux, width, - last.bg); + tty_clear_line(tty, defaults, aty, atx + ux, + width, last.bg); } else { if (!wrapped || atx != 0 || ux != 0) tty_cursor(tty, atx + ux, aty); @@ -1376,7 +1385,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, if (!tty_check_overlay(tty, atx + ux, aty)) ux += gcp->data.width; else if (ux + gcp->data.width > nx) { - tty_attributes(tty, &last, wp); + tty_attributes(tty, &last, defaults, palette); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.width; j++) { if (ux + j > nx) @@ -1385,7 +1394,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, ux++; } } else if (gcp->attr & GRID_ATTR_CHARSET) { - tty_attributes(tty, &last, wp); + tty_attributes(tty, &last, defaults, palette); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.size; j++) tty_putc(tty, gcp->data.data[j]); @@ -1397,10 +1406,11 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, } } if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) { - tty_attributes(tty, &last, wp); + tty_attributes(tty, &last, defaults, palette); if (last.flags & GRID_FLAG_CLEARED) { log_debug("%s: %zu cleared (end)", __func__, len); - tty_clear_line(tty, wp, aty, atx + ux, width, last.bg); + tty_clear_line(tty, defaults, aty, atx + ux, width, + last.bg); } else { if (!wrapped || atx != 0 || ux != 0) tty_cursor(tty, atx + ux, aty); @@ -1412,8 +1422,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, if (!cleared && ux < nx) { log_debug("%s: %u to end of line (%zu cleared)", __func__, nx - ux, len); - tty_default_attributes(tty, wp, 8); - tty_clear_line(tty, wp, aty, atx + ux, nx - ux, 8); + tty_default_attributes(tty, defaults, palette, 8); + tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8); } tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; @@ -1451,7 +1461,7 @@ tty_sync_end(struct tty *tty) } static int -tty_client_ready(struct client *c, struct window_pane *wp) +tty_client_ready(struct client *c) { if (c->session == NULL || c->tty.term == NULL) return (0); @@ -1459,10 +1469,6 @@ tty_client_ready(struct client *c, struct window_pane *wp) return (0); if (c->tty.flags & TTY_FREEZE) return (0); - if (c->session->curw->window != wp->window) - return (0); - if (wp->layout_cell == NULL) - return (0); return (1); } @@ -1470,36 +1476,19 @@ void tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - struct client *c; + struct client *c; + int state; - if (wp == NULL) + if (ctx->set_client_cb == NULL) return; - if (wp->flags & (PANE_REDRAW|PANE_DROP)) - return; - TAILQ_FOREACH(c, &clients, entry) { - if (!tty_client_ready(c, wp)) + if (!tty_client_ready(c)) continue; - if (c->flags & CLIENT_REDRAWPANES) { - /* - * Redraw is already deferred to redraw another pane - - * redraw this one also when that happens. - */ - log_debug("adding %%%u to deferred redraw", wp->id); - wp->flags |= PANE_REDRAW; + state = ctx->set_client_cb(ctx, c); + if (state == -1) break; - } - - ctx->bigger = tty_window_offset(&c->tty, &ctx->wox, &ctx->woy, - &ctx->wsx, &ctx->wsy); - - ctx->xoff = wp->xoff; - ctx->yoff = wp->yoff; - - if (status_at_line(c) == 0) - ctx->yoff += status_line_size(c); - + if (state == 0) + continue; cmdfn(&c->tty, ctx); } } @@ -1507,18 +1496,16 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), void tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (ctx->bigger || !tty_full_width(tty, ctx) || - tty_fake_bce(tty, wp, ctx->bg) || + tty_fake_bce(tty, &ctx->defaults, ctx->bg) || (!tty_term_has(tty->term, TTYC_ICH) && !tty_term_has(tty->term, TTYC_ICH1))) { tty_draw_pane(tty, ctx, ctx->ocy); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -1528,18 +1515,16 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (ctx->bigger || !tty_full_width(tty, ctx) || - tty_fake_bce(tty, wp, ctx->bg) || + tty_fake_bce(tty, &ctx->defaults, ctx->bg) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { tty_draw_pane(tty, ctx, ctx->ocy); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -1554,12 +1539,12 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, ctx->wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_term_has(tty->term, TTYC_ECH) && - !tty_fake_bce(tty, ctx->wp, 8)) + !tty_fake_bce(tty, &ctx->defaults, 8)) tty_putcode1(tty, TTYC_ECH, ctx->num); else tty_repeat_space(tty, ctx->num); @@ -1570,16 +1555,16 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger || !tty_full_width(tty, ctx) || - tty_fake_bce(tty, ctx->wp, ctx->bg) || + tty_fake_bce(tty, &ctx->defaults, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1) || - ctx->wp->sx == 1 || - ctx->wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, ctx->wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); @@ -1594,16 +1579,16 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger || !tty_full_width(tty, ctx) || - tty_fake_bce(tty, ctx->wp, ctx->bg) || + tty_fake_bce(tty, &ctx->defaults, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1) || - ctx->wp->sx == 1 || - ctx->wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, ctx->wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); @@ -1616,9 +1601,7 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg); } @@ -1626,10 +1609,9 @@ tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int nx = ctx->sx - ctx->ocx; + u_int nx = ctx->sx - ctx->ocx; - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg); } @@ -1637,9 +1619,7 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg); } @@ -1647,24 +1627,22 @@ tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (ctx->ocy != ctx->orupper) return; if (ctx->bigger || (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || - tty_fake_bce(tty, wp, 8) || + tty_fake_bce(tty, &ctx->defaults, 8) || !tty_term_has(tty->term, TTYC_CSR) || (!tty_term_has(tty->term, TTYC_RI) && !tty_term_has(tty->term, TTYC_RIN)) || - ctx->wp->sx == 1 || - ctx->wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1679,22 +1657,20 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (ctx->ocy != ctx->orlower) return; if (ctx->bigger || (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || - tty_fake_bce(tty, wp, 8) || + tty_fake_bce(tty, &ctx->defaults, 8) || !tty_term_has(tty->term, TTYC_CSR) || - wp->sx == 1 || - wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1720,20 +1696,19 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int i; + u_int i; if (ctx->bigger || (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || - tty_fake_bce(tty, wp, 8) || + tty_fake_bce(tty, &ctx->defaults, 8) || !tty_term_has(tty->term, TTYC_CSR) || - wp->sx == 1 || - wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1757,22 +1732,21 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int i; + u_int i; if (ctx->bigger || (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || - tty_fake_bce(tty, wp, 8) || + tty_fake_bce(tty, &ctx->defaults, 8) || !tty_term_has(tty->term, TTYC_CSR) || (!tty_term_has(tty->term, TTYC_RI) && !tty_term_has(tty->term, TTYC_RIN)) || - wp->sx == 1 || - wp->sy == 1) { + ctx->sx == 1 || + ctx->sy == 1) { tty_redraw_region(tty, ctx); return; } - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1789,10 +1763,9 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int px, py, nx, ny; + u_int px, py, nx, ny; - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1814,10 +1787,9 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int px, py, nx, ny; + u_int px, py, nx, ny; - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1839,10 +1811,9 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int px, py, nx, ny; + u_int px, py, nx, ny; - tty_default_attributes(tty, wp, ctx->bg); + tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1858,15 +1829,14 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - u_int i, j; + u_int i, j; if (ctx->bigger) { - wp->flags |= PANE_REDRAW; + ctx->redraw_cb(ctx); return; } - tty_attributes(tty, &grid_default_cell, wp); + tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1892,14 +1862,12 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); - tty_cell(tty, ctx->cell, ctx->wp); + tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette); } void tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1)) return; @@ -1915,14 +1883,14 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) tty->cy == tty->rlower) tty_draw_pane(tty, ctx, ctx->ocy); else - wp->flags |= PANE_REDRAW; + ctx->redraw_cb(ctx); return; } tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); - tty_attributes(tty, ctx->cell, ctx->wp); + tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette); tty_putn(tty, ctx->ptr, ctx->num, ctx->num); } @@ -1958,7 +1926,8 @@ tty_cmd_syncstart(struct tty *tty, __unused const struct tty_ctx *ctx) } static void -tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) +tty_cell(struct tty *tty, const struct grid_cell *gc, + const struct grid_cell *defaults, int *palette) { const struct grid_cell *gcp; @@ -1973,7 +1942,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) return; /* Set the attributes. */ - tty_attributes(tty, gc, wp); + tty_attributes(tty, gc, defaults, palette); /* Get the cell and if ASCII write with putc to do ACS translation. */ gcp = tty_check_codeset(tty, gc); @@ -1999,21 +1968,16 @@ tty_reset(struct tty *tty) tty_putcode(tty, TTYC_SGR0); memcpy(gc, &grid_default_cell, sizeof *gc); } - memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); - tty->last_wp = -1; } static void tty_invalidate(struct tty *tty) { memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); - memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); - tty->last_wp = -1; tty->cx = tty->cy = UINT_MAX; - tty->rupper = tty->rleft = UINT_MAX; tty->rlower = tty->rright = UINT_MAX; @@ -2089,7 +2053,7 @@ static void tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) { tty_margin(tty, ctx->xoff - ctx->wox, - ctx->xoff + ctx->wp->sx - 1 - ctx->wox); + ctx->xoff + ctx->sx - 1 - ctx->wox); } /* Set margin at absolute position. */ @@ -2282,27 +2246,24 @@ out: void tty_attributes(struct tty *tty, const struct grid_cell *gc, - struct window_pane *wp) + const struct grid_cell *defaults, int *palette) { struct grid_cell *tc = &tty->cell, gc2; int changed; - /* Ignore cell if it is the same as the last one. */ - if (wp != NULL && - (int)wp->id == tty->last_wp && - ~(wp->flags & PANE_STYLECHANGED) && - gc->attr == tty->last_cell.attr && - gc->fg == tty->last_cell.fg && - gc->bg == tty->last_cell.bg && - gc->us == tty->last_cell.us) - return; - tty->last_wp = (wp != NULL ? (int)wp->id : -1); - memcpy(&tty->last_cell, gc, sizeof tty->last_cell); - /* Copy cell and update default colours. */ memcpy(&gc2, gc, sizeof gc2); - if (wp != NULL) - tty_default_colours(&gc2, wp); + if (gc2.fg == 8) + gc2.fg = defaults->fg; + if (gc2.bg == 8) + gc2.bg = defaults->bg; + + /* Ignore cell if it is the same as the last one. */ + if (gc2.attr == tty->last_cell.attr && + gc2.fg == tty->last_cell.fg && + gc2.bg == tty->last_cell.bg && + gc2.us == tty->last_cell.us) + return; /* * If no setab, try to use the reverse attribute as a best-effort for a @@ -2320,9 +2281,9 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, } /* Fix up the colours if necessary. */ - tty_check_fg(tty, wp, &gc2); - tty_check_bg(tty, wp, &gc2); - tty_check_us(tty, wp, &gc2); + tty_check_fg(tty, palette, &gc2); + tty_check_bg(tty, palette, &gc2); + tty_check_us(tty, palette, &gc2); /* * If any bits are being cleared or the underline colour is now default, @@ -2377,6 +2338,8 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, tty_putcode(tty, TTYC_SMOL); if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) tty_putcode(tty, TTYC_SMACS); + + memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell); } static void @@ -2441,7 +2404,7 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) } static void -tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) +tty_check_fg(struct tty *tty, int *palette, struct grid_cell *gc) { u_char r, g, b; u_int colours; @@ -2456,7 +2419,7 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) c = gc->fg; if (c < 8 && gc->attr & GRID_ATTR_BRIGHT) c += 90; - if ((c = window_pane_get_palette(wp, c)) != -1) + if ((c = tty_get_palette(palette, c)) != -1) gc->fg = c; } @@ -2497,7 +2460,7 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) } static void -tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) +tty_check_bg(struct tty *tty, int *palette, struct grid_cell *gc) { u_char r, g, b; u_int colours; @@ -2505,7 +2468,7 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Perform substitution if this pane has a palette. */ if (~gc->flags & GRID_FLAG_NOPALETTE) { - if ((c = window_pane_get_palette(wp, gc->bg)) != -1) + if ((c = tty_get_palette(palette, gc->bg)) != -1) gc->bg = c; } @@ -2548,14 +2511,13 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) } static void -tty_check_us(__unused struct tty *tty, struct window_pane *wp, - struct grid_cell *gc) +tty_check_us(__unused struct tty *tty, int *palette, struct grid_cell *gc) { int c; /* Perform substitution if this pane has a palette. */ if (~gc->flags & GRID_FLAG_NOPALETTE) { - if ((c = window_pane_get_palette(wp, gc->us)) != -1) + if ((c = tty_get_palette(palette, gc->us)) != -1) gc->us = c; } @@ -2686,11 +2648,12 @@ tty_window_default_style(struct grid_cell *gc, struct window_pane *wp) gc->bg = wp->bg; } -static void +void tty_default_colours(struct grid_cell *gc, struct window_pane *wp) { struct options *oo = wp->options; - int c; + + memcpy(gc, &grid_default_cell, sizeof *gc); if (wp->flags & PANE_STYLECHANGED) { wp->flags &= ~PANE_STYLECHANGED; @@ -2707,12 +2670,6 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) gc->fg = wp->cached_active_gc.fg; else gc->fg = wp->cached_gc.fg; - - if (gc->fg != 8) { - c = window_pane_get_palette(wp, gc->fg); - if (c != -1) - gc->fg = c; - } } if (gc->bg == 8) { @@ -2720,21 +2677,16 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) gc->bg = wp->cached_active_gc.bg; else gc->bg = wp->cached_gc.bg; - - if (gc->bg != 8) { - c = window_pane_get_palette(wp, gc->bg); - if (c != -1) - gc->bg = c; - } } } static void -tty_default_attributes(struct tty *tty, struct window_pane *wp, u_int bg) +tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, + int *palette, u_int bg) { - static struct grid_cell gc; + struct grid_cell gc; memcpy(&gc, &grid_default_cell, sizeof gc); gc.bg = bg; - tty_attributes(tty, &gc, wp); + tty_attributes(tty, &gc, defaults, palette); } diff --git a/window-clock.c b/window-clock.c index 45d4d47b..8cef3f9a 100644 --- a/window-clock.c +++ b/window-clock.c @@ -219,7 +219,7 @@ window_clock_draw_screen(struct window_mode_entry *wme) colour = options_get_number(wp->window->options, "clock-mode-colour"); style = options_get_number(wp->window->options, "clock-mode-style"); - screen_write_start(&ctx, NULL, s); + screen_write_start(&ctx, s); t = time(NULL); tm = localtime(&t); diff --git a/window-copy.c b/window-copy.c index f5a07a10..9f84ade9 100644 --- a/window-copy.c +++ b/window-copy.c @@ -406,7 +406,7 @@ window_copy_init(struct window_mode_entry *wme, data->screen.cx = data->cx; data->screen.cy = data->cy; - screen_write_start(&ctx, NULL, &data->screen); + screen_write_start(&ctx, &data->screen); for (i = 0; i < screen_size_y(&data->screen); i++) window_copy_write_line(wme, &ctx, i); screen_write_cursormove(&ctx, data->cx, data->cy, 0); @@ -473,7 +473,7 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); - screen_write_start(&back_ctx, NULL, backing); + screen_write_start(&back_ctx, backing); if (data->backing_written) { /* * On the second or later line, do a CRLF before writing @@ -489,7 +489,7 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) data->oy += screen_hsize(data->backing) - old_hsize; - screen_write_start(&ctx, wp, &data->screen); + screen_write_start_pane(&ctx, wp, &data->screen); /* * If the history has changed, draw the top line. @@ -713,7 +713,7 @@ window_copy_size_changed(struct window_mode_entry *wme) window_copy_clear_selection(wme); window_copy_clear_marks(wme); - screen_write_start(&ctx, NULL, s); + screen_write_start(&ctx, s); window_copy_write_lines(wme, &ctx, 0, screen_size_y(s)); screen_write_stop(&ctx); @@ -2822,7 +2822,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) fy = screen_hsize(data->backing) - data->oy + data->cy; screen_init(&ss, screen_write_strlen("%s", str), 1, 0); - screen_write_start(&ctx, NULL, &ss); + screen_write_start(&ctx, &ss); screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); screen_write_stop(&ctx); @@ -2867,7 +2867,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, if (ssp == NULL) { width = screen_write_strlen("%s", data->searchstr); screen_init(&ss, width, 1, 0); - screen_write_start(&ctx, NULL, &ss); + screen_write_start(&ctx, &ss); screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); screen_write_stop(&ctx); @@ -3207,7 +3207,7 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny) struct screen_write_ctx ctx; u_int i; - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); for (i = py; i < py + ny; i++) window_copy_write_line(wme, &ctx, i); screen_write_cursormove(&ctx, data->cx, data->cy, 0); @@ -3326,7 +3326,7 @@ window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) if (data->cx == screen_size_x(s)) window_copy_redraw_lines(wme, data->cy, 1); else { - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_cursormove(&ctx, data->cx, data->cy, 0); screen_write_stop(&ctx); } @@ -3579,7 +3579,7 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, struct screen_write_ctx ctx; if (options_get_number(global_options, "set-clipboard") != 0) { - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); @@ -3636,7 +3636,7 @@ window_copy_append_selection(struct window_mode_entry *wme) return; if (options_get_number(global_options, "set-clipboard") != 0) { - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); @@ -4399,7 +4399,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 0, 0); - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); screen_write_deleteline(&ctx, ny, 8); window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny); @@ -4435,7 +4435,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) window_copy_search_marks(wme, NULL, data->searchregex); window_copy_update_selection(wme, 0, 0); - screen_write_start(&ctx, wp, NULL); + screen_write_start_pane(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); screen_write_insertline(&ctx, ny, 8); window_copy_write_lines(wme, &ctx, 0, ny); diff --git a/window.c b/window.c index 7fea9d80..b28a2257 100644 --- a/window.c +++ b/window.c @@ -995,26 +995,6 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wp->flags |= (PANE_RESIZE|PANE_RESIZED); } -void -window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, - int cursor) -{ - if (!options_get_number(wp->options, "alternate-screen")) - return; - screen_alternate_on(&wp->base, gc, cursor); - wp->flags |= PANE_REDRAW; -} - -void -window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, - int cursor) -{ - if (!options_get_number(wp->options, "alternate-screen")) - return; - screen_alternate_off(&wp->base, gc, cursor); - wp->flags |= PANE_REDRAW; -} - void window_pane_set_palette(struct window_pane *wp, u_int n, int colour) { From 2df75aa11727538451d7402b5ad1579d7493ce4c Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:35:19 +0000 Subject: [PATCH 0405/1006] Use VIS_CSTYLE for paste buffers also. --- paste.c | 2 +- window-buffer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/paste.c b/paste.c index e98b5771..46f218d2 100644 --- a/paste.c +++ b/paste.c @@ -312,7 +312,7 @@ paste_make_sample(struct paste_buffer *pb) { char *buf; size_t len, used; - const int flags = VIS_OCTAL|VIS_TAB|VIS_NL; + const int flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; const size_t width = 200; len = pb->size; diff --git a/window-buffer.c b/window-buffer.c index 53dfeca4..74be73a4 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -233,7 +233,7 @@ window_buffer_draw(__unused void *modedata, void *itemdata, while (end != pdata + psize && *end != '\n') end++; buf = xreallocarray(buf, 4, end - start + 1); - utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_TAB); + utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_CSTYLE|VIS_TAB); if (*buf != '\0') { screen_write_cursormove(ctx, cx, cy + i, 0); screen_write_nputs(ctx, sx, &grid_default_cell, "%s", From 3fb4d4df43b9e2bb5661d3a820cdc26a38f09d9e Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:36:57 +0000 Subject: [PATCH 0406/1006] Do not need to work out status line offset, we already have it. --- tty.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tty.c b/tty.c index 388f6f5f..c8efeac7 100644 --- a/tty.c +++ b/tty.c @@ -963,21 +963,16 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) /* Is this position visible in the pane? */ static int -tty_is_visible(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, - u_int nx, u_int ny) +tty_is_visible(__unused struct tty *tty, const struct tty_ctx *ctx, u_int px, + u_int py, u_int nx, u_int ny) { - u_int xoff = ctx->xoff + px, yoff = ctx->yoff + py, lines; + u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py; if (!ctx->bigger) return (1); - if (status_at_line(tty->client) == 0) - lines = status_line_size(tty->client); - else - lines = 0; - if (xoff + nx <= ctx->wox || xoff >= ctx->wox + ctx->wsx || - yoff + ny <= ctx->woy || yoff >= lines + ctx->woy + ctx->wsy) + yoff + ny <= ctx->woy || yoff >= ctx->woy + ctx->wsy) return (0); return (1); } From d056144aa173c7b20d2a78e7d0a5134569e135db Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:38:14 +0000 Subject: [PATCH 0407/1006] Try to search the entire history first for up to 200 ms so a search count can be shown. If it takes too long, search the visible text only. --- menu.c | 1 + window-copy.c | 131 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 91 insertions(+), 41 deletions(-) diff --git a/menu.c b/menu.c index 07fc8fa8..62010a58 100644 --- a/menu.c +++ b/menu.c @@ -351,6 +351,7 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, screen_init(&md->s, menu->width + 4, menu->count + 2, 0); if (~md->flags & MENU_NOMOUSE) md->s.mode |= MODE_MOUSE_ALL; + md->s.mode &= ~MODE_CURSOR; md->px = px; md->py = py; diff --git a/window-copy.c b/window-copy.c index 9f84ade9..38e87b67 100644 --- a/window-copy.c +++ b/window-copy.c @@ -258,7 +258,8 @@ struct window_copy_mode_data { int searchregex; char *searchstr; u_char *searchmark; - u_int searchcount; + int searchcount; + int searchmore; int searchthis; int searchx; int searchy; @@ -266,7 +267,8 @@ struct window_copy_mode_data { u_char searchgen; int timeout; /* search has timed out */ -#define WINDOW_COPY_SEARCH_TIMEOUT 10 +#define WINDOW_COPY_SEARCH_TIMEOUT 10000 +#define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200 int jumptype; char jumpchar; @@ -2847,6 +2849,15 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) return (found); } +static uint64_t +window_copy_get_time(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return ((tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000ULL)); +} + static int window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, int regex) @@ -2856,13 +2867,13 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, struct screen_write_ctx ctx; struct grid *gd = s->grid; const struct grid_line *gl; - int found, cis, which = -1; + int found, cis, which = -1, stopped = 0; int cflags = REG_EXTENDED; u_int px, py, i, b, nfound = 0, width; - u_int ssize = 1; + u_int ssize = 1, start, end; char *sbuf; regex_t reg; - time_t tstart, t; + uint64_t stop = 0, tstart, t; if (ssp == NULL) { width = screen_write_strlen("%s", data->searchstr); @@ -2877,10 +2888,6 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, cis = window_copy_is_lowercase(data->searchstr); - free(data->searchmark); - data->searchmark = xcalloc(gd->hsize + gd->sy, gd->sx); - data->searchgen = 1; - if (regex) { sbuf = xmalloc(ssize); sbuf[0] = '\0'; @@ -2893,13 +2900,18 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, return (0); } } - time(&tstart); - for (py = gd->hsize - data->oy; py > 0; py--) { - gl = grid_peek_line(gd, py - 1); - if (~gl->flags & GRID_LINE_WRAPPED) - break; - } - for (; py < gd->hsize - data->oy + gd->sy; py++) { + tstart = window_copy_get_time(); + + start = 0; + end = gd->hsize + gd->sy; + stop = window_copy_get_time() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; + +again: + free(data->searchmark); + data->searchmark = xcalloc(gd->hsize + gd->sy, gd->sx); + data->searchgen = 1; + + for (py = start; py < end; py++) { px = 0; for (;;) { if (regex) { @@ -2930,29 +2942,60 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, px++; } - time(&t); + t = window_copy_get_time(); if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { data->timeout = 1; break; } + if (stop != 0 && t > stop) { + stopped = 1; + break; + } } + if (data->timeout) { + window_copy_clear_marks(wme); + goto out; + } + + if (stopped && stop != 0) { + /* Try again but just the visible context. */ + for (start = gd->hsize - data->oy; start > 0; start--) { + gl = grid_peek_line(gd, start - 1); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + } + end = gd->hsize - data->oy + gd->sy; + stop = 0; + goto again; + } + + if (stopped) { + data->searchthis = -1; + if (nfound > 1000) + data->searchcount = 1000; + else if (nfound > 100) + data->searchcount = 100; + else if (nfound > 10) + data->searchcount = 10; + else + data->searchcount = -1; + data->searchmore = 1; + } else { + if (which != -1) + data->searchthis = 1 + nfound - which; + else + data->searchthis = -1; + data->searchcount = nfound; + data->searchmore = 0; + } + +out: + if (ssp == &ss) + screen_free(&ss); if (regex) { free(sbuf); regfree(®); } - if (data->timeout) { - window_copy_clear_marks(wme); - return (1); - } - - if (which != -1) - data->searchthis = 1 + nfound - which; - else - data->searchthis = -1; - data->searchcount = nfound; - - if (ssp == &ss) - screen_free(&ss); return (1); } @@ -3057,25 +3100,28 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, const struct grid_cell *cgc) { struct window_copy_mode_data *data = wme->data; - u_int mark, at, start, end, cy; + u_int mark, start, end, cy, cursor, current; u_int sx = screen_size_x(data->backing); if (data->searchmark == NULL) return; - mark = data->searchmark[(fy * sx) + fx]; + + current = (fy * sx) + fx; + + mark = data->searchmark[current]; if (mark == 0) return; cy = screen_hsize(data->backing) - data->oy + data->cy; - at = (cy * sx) + data->cx; - if (data->searchmark[at] == mark) { - window_copy_match_start_end(data, at, &start, &end); - if (at >= start && at <= end) { + cursor = (cy * sx) + data->cx; + if (data->searchmark[cursor] == mark) { + window_copy_match_start_end(data, cursor, &start, &end); + if (current >= start && current <= end) { gc->attr = cgc->attr; gc->fg = cgc->fg; gc->bg = cgc->bg; + return; } - return; } gc->attr = mgc->attr; @@ -3133,13 +3179,16 @@ window_copy_write_line(struct window_mode_entry *wme, "[%u/%u]", data->oy, hsize); } } else { - if (data->searchthis == -1) { + if (data->searchcount == -1) { size = xsnprintf(hdr, sizeof hdr, - "(%u results) [%d/%u]", data->searchcount, - data->oy, hsize); + "[%u/%u]", data->oy, hsize); + } else if (data->searchthis == -1) { + size = xsnprintf(hdr, sizeof hdr, + "(%d%s results) [%u/%u]", data->searchcount, + data->searchmore ? "+" : "", data->oy, hsize); } else { size = xsnprintf(hdr, sizeof hdr, - "(%u/%u results) [%d/%u]", data->searchthis, + "(%d/%d results) [%u/%u]", data->searchthis, data->searchcount, data->oy, hsize); } } From beb214bcb3e2a76e153d0570aed72454b9748a9b Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:40:04 +0000 Subject: [PATCH 0408/1006] Add formats for after hook command arguments. --- arguments.c | 54 +++++++++++++++++++++++++++++++++++++---------------- cmd-queue.c | 39 +++++++++++++++++++++++++++++++++++++- tmux.h | 2 ++ 3 files changed, 78 insertions(+), 17 deletions(-) diff --git a/arguments.c b/arguments.c index 547cec00..712e61c5 100644 --- a/arguments.c +++ b/arguments.c @@ -56,11 +56,11 @@ args_cmp(struct args_entry *a1, struct args_entry *a2) /* Find a flag in the arguments tree. */ static struct args_entry * -args_find(struct args *args, u_char ch) +args_find(struct args *args, u_char flag) { struct args_entry entry; - entry.flag = ch; + entry.flag = flag; return (RB_FIND(args_tree, &args->tree, &entry)); } @@ -249,11 +249,11 @@ args_escape(const char *s) /* Return if an argument is present. */ int -args_has(struct args *args, u_char ch) +args_has(struct args *args, u_char flag) { struct args_entry *entry; - entry = args_find(args, ch); + entry = args_find(args, flag); if (entry == NULL) return (0); return (entry->count); @@ -261,15 +261,15 @@ args_has(struct args *args, u_char ch) /* Set argument value in the arguments tree. */ void -args_set(struct args *args, u_char ch, const char *s) +args_set(struct args *args, u_char flag, const char *s) { struct args_entry *entry; struct args_value *value; - entry = args_find(args, ch); + entry = args_find(args, flag); if (entry == NULL) { entry = xcalloc(1, sizeof *entry); - entry->flag = ch; + entry->flag = flag; entry->count = 1; TAILQ_INIT(&entry->values); RB_INSERT(args_tree, &args->tree, entry); @@ -285,22 +285,44 @@ args_set(struct args *args, u_char ch, const char *s) /* Get argument value. Will be NULL if it isn't present. */ const char * -args_get(struct args *args, u_char ch) +args_get(struct args *args, u_char flag) { struct args_entry *entry; - if ((entry = args_find(args, ch)) == NULL) + if ((entry = args_find(args, flag)) == NULL) + return (NULL); + if (TAILQ_EMPTY(&entry->values)) return (NULL); return (TAILQ_LAST(&entry->values, args_values)->value); } +/* Get first argument. */ +u_char +args_first(struct args *args, struct args_entry **entry) +{ + *entry = RB_MIN(args_tree, &args->tree); + if (*entry == NULL) + return (0); + return ((*entry)->flag); +} + +/* Get next argument. */ +u_char +args_next(struct args_entry **entry) +{ + *entry = RB_NEXT(args_tree, &args->tree, *entry); + if (*entry == NULL) + return (0); + return ((*entry)->flag); +} + /* Get first value in argument. */ const char * -args_first_value(struct args *args, u_char ch, struct args_value **value) +args_first_value(struct args *args, u_char flag, struct args_value **value) { struct args_entry *entry; - if ((entry = args_find(args, ch)) == NULL) + if ((entry = args_find(args, flag)) == NULL) return (NULL); *value = TAILQ_FIRST(&entry->values); @@ -323,15 +345,15 @@ args_next_value(struct args_value **value) /* Convert an argument value to a number. */ long long -args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, - char **cause) +args_strtonum(struct args *args, u_char flag, long long minval, + long long maxval, char **cause) { const char *errstr; long long ll; struct args_entry *entry; struct args_value *value; - if ((entry = args_find(args, ch)) == NULL) { + if ((entry = args_find(args, flag)) == NULL) { *cause = xstrdup("missing"); return (0); } @@ -349,13 +371,13 @@ args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, /* Convert an argument to a number which may be a percentage. */ long long -args_percentage(struct args *args, u_char ch, long long minval, +args_percentage(struct args *args, u_char flag, long long minval, long long maxval, long long curval, char **cause) { const char *value; struct args_entry *entry; - if ((entry = args_find(args, ch)) == NULL) { + if ((entry = args_find(args, flag)) == NULL) { *cause = xstrdup("missing"); return (0); } diff --git a/cmd-queue.c b/cmd-queue.c index 59f86c64..620a3e93 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -341,9 +341,15 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, struct cmd_find_state *current, const char *fmt, ...) { struct cmdq_state *state = item->state; + struct cmd *cmd = item->cmd; + struct args *args = cmd_get_args(cmd); + struct args_entry *entryp; + struct args_value *valuep; struct options *oo; va_list ap; - char *name; + char *name, tmp[32], flag, *arguments; + int i; + const char *value; struct cmdq_item *new_item; struct cmdq_state *new_state; struct options_entry *o; @@ -375,6 +381,37 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS); cmdq_add_format(new_state, "hook", "%s", name); + arguments = args_print(args); + cmdq_add_format(new_state, "hook_arguments", "%s", arguments); + free(arguments); + + for (i = 0; i < args->argc; i++) { + xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i); + cmdq_add_format(new_state, tmp, "%s", args->argv[i]); + } + flag = args_first(args, &entryp); + while (flag != 0) { + value = args_get(args, flag); + if (value == NULL) { + xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag); + cmdq_add_format(new_state, tmp, "1"); + } else { + xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag); + cmdq_add_format(new_state, tmp, "%s", value); + } + + i = 0; + value = args_first_value(args, flag, &valuep); + while (value != NULL) { + xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i); + cmdq_add_format(new_state, tmp, "%s", value); + i++; + value = args_next_value(&valuep); + } + + flag = args_next(&entryp); + } + a = options_array_first(o); while (a != NULL) { cmdlist = options_array_item_value(a)->cmdlist; diff --git a/tmux.h b/tmux.h index 53417cd7..811b5fb6 100644 --- a/tmux.h +++ b/tmux.h @@ -2088,6 +2088,8 @@ char *args_print(struct args *); char *args_escape(const char *); int args_has(struct args *, u_char); const char *args_get(struct args *, u_char); +u_char args_first(struct args *, struct args_entry **); +u_char args_next(struct args_entry **); const char *args_first_value(struct args *, u_char, struct args_value **); const char *args_next_value(struct args_value **); long long args_strtonum(struct args *, u_char, long long, long long, From 4e053685df9f4d1c398148712ab0529a7e9d32e7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:40:44 +0000 Subject: [PATCH 0409/1006] Export TERM_PROGRAM and TERM_PROGRAM_VERSION like various other terminals. --- environ.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/environ.c b/environ.c index 0ce717df..940109b0 100644 --- a/environ.c +++ b/environ.c @@ -252,6 +252,8 @@ environ_for_session(struct session *s, int no_TERM) if (!no_TERM) { value = options_get_string(global_options, "default-terminal"); environ_set(env, "TERM", 0, "%s", value); + environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux"); + environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion()); } if (s != NULL) From 469eda7e44fe6d502c976ebc34bbd97e6c6ed3e5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:41:54 +0000 Subject: [PATCH 0410/1006] Only redraw popup on the client it belongs to. --- popup.c | 4 +++- tmux.1 | 2 +- tty-keys.c | 5 +---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/popup.c b/popup.c index 9937d586..4b47df2b 100644 --- a/popup.c +++ b/popup.c @@ -70,8 +70,10 @@ popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) { struct popup_data *pd = ttyctx->arg; + if (c != pd->c) + return (0); if (pd->c->flags & CLIENT_REDRAWOVERLAY) - return (-1); + return (0); ttyctx->bigger = 0; ttyctx->wox = 0; diff --git a/tmux.1 b/tmux.1 index 5b7bb3db..eafa3751 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4553,7 +4553,7 @@ The following variables are available, where appropriate: .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" .It Li "pane_mode" Ta "" Ta "Name of pane mode, if any" -.It Li "pane_path" Ta "#T" Ta "Path of pane (can be set by application)" +.It Li "pane_path" Ta "" Ta "Path of pane (can be set by application)" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" .It Li "pane_right" Ta "" Ta "Right of pane" diff --git a/tty-keys.c b/tty-keys.c index f5a3418f..dc064a17 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1064,10 +1064,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, /* Add terminal features. */ switch (p[0]) { case 41: /* VT420 */ - tty_add_features(&c->term_features, - "margins," - "rectfill", - ","); + tty_add_features(&c->term_features, "margins,rectfill", ","); break; case 'M': /* mintty */ tty_default_features(&c->term_features, "mintty", 0); From cf9baddd6f844e8a26f1e7c59ba1c9eb3358571f Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:45:29 +0000 Subject: [PATCH 0411/1006] Change the existing client flags for control mode to apply for any client, use the same mechanism for the read-only flag and add an ignore-size flag. refresh-client -F has become -f (-F stays for backwards compatibility) and attach-session and switch-client now have -f flags also. A new format "client_flags" lists the flags and is shown by list-clients by default. This separates the read-only flag from "ignore size" behaviour (new ignore-size) flag - both behaviours are useful in different circumstances. attach -r and switchc -r remain and set or toggle both flags together. --- cmd-attach-session.c | 15 +++++++----- cmd-list-clients.c | 8 +++--- cmd-new-session.c | 8 +++--- cmd-refresh-client.c | 25 ++++++------------- cmd-switch-client.c | 10 +++++--- format.c | 2 +- resize.c | 8 +++--- server-client.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ tmux.1 | 46 +++++++++++++++++++++++++++-------- tmux.h | 6 +++-- 10 files changed, 136 insertions(+), 50 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 8c30c767..38d9c024 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -37,8 +37,9 @@ const struct cmd_entry cmd_attach_session_entry = { .name = "attach-session", .alias = "attach", - .args = { "c:dErt:x", 0, 0 }, - .usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE, + .args = { "c:dEf:rt:x", 0, 0 }, + .usage = "[-dErx] [-c working-directory] [-f flags] " + CMD_TARGET_SESSION_USAGE, /* -t is special */ @@ -48,7 +49,7 @@ const struct cmd_entry cmd_attach_session_entry = { enum cmd_retval cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, - int xflag, int rflag, const char *cflag, int Eflag) + int xflag, int rflag, const char *cflag, int Eflag, const char *fflag) { struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state target; @@ -101,6 +102,10 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, free((void *)s->cwd); s->cwd = format_single(item, cflag, c, s, wl, wp); } + if (fflag) + server_client_set_flags(c, fflag); + if (rflag) + c->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE); c->last_session = c->session; if (c->session != NULL) { @@ -135,8 +140,6 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, free(cause); return (CMD_RETURN_ERROR); } - if (rflag) - c->flags |= CLIENT_READONLY; if (dflag || xflag) { if (xflag) @@ -182,5 +185,5 @@ cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item) return (cmd_attach_session(item, args_get(args, 't'), args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'), - args_get(args, 'c'), args_has(args, 'E'))); + args_get(args, 'c'), args_has(args, 'E'), args_get(args, 'f'))); } diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 75118c8e..d450f017 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -28,10 +28,10 @@ * List all clients. */ -#define LIST_CLIENTS_TEMPLATE \ - "#{client_name}: #{session_name} " \ - "[#{client_width}x#{client_height} #{client_termname}]" \ - "#{?client_utf8, (utf8),} #{?client_readonly, (ro),}" +#define LIST_CLIENTS_TEMPLATE \ + "#{client_name}: #{session_name} " \ + "[#{client_width}x#{client_height} #{client_termname}] " \ + "#{?client_flags,(,}#{client_flags}#{?client_flags,),}" static enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmdq_item *); diff --git a/cmd-new-session.c b/cmd-new-session.c index 9815e1e1..a9a0376b 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -39,9 +39,9 @@ const struct cmd_entry cmd_new_session_entry = { .name = "new-session", .alias = "new", - .args = { "Ac:dDe:EF:n:Ps:t:x:Xy:", 0, -1 }, + .args = { "Ac:dDe:EF:f:n:Ps:t:x:Xy:", 0, -1 }, .usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] " - "[-n window-name] [-s session-name] " + "[-f flags] [-n window-name] [-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -112,7 +112,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (as != NULL) { retval = cmd_attach_session(item, as->name, args_has(args, 'D'), args_has(args, 'X'), 0, NULL, - args_has(args, 'E')); + args_has(args, 'E'), args_get(args, 'f')); free(newname); return (retval); } @@ -306,6 +306,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) * taking this session and needs to get MSG_READY and stay around. */ if (!detached) { + if (args_has(args, 'f')) + server_client_set_flags(c, args_get(args, 'f')); if (!already_attached) { if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 5514ff73..c53a6a78 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "cC:DF:lLRSt:U", 0, 1 }, - .usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE + .args = { "cC:Df:F:lLRSt:U", 0, 1 }, + .usage = "[-cDlLRSU] [-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, @@ -50,7 +50,6 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) struct tty *tty = &tc->tty; struct window *w; const char *size, *errstr; - char *copy, *next, *s; u_int x, y, adjust; if (args_has(args, 'c') || @@ -108,7 +107,12 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'C') || args_has(args, 'F')) { + if (args_has(args, 'F')) /* -F is an alias for -f */ + server_client_set_flags(tc, args_get(args, 'F')); + if (args_has(args, 'f')) + server_client_set_flags(tc, args_get(args, 'f')); + + if (args_has(args, 'C')) { if (args_has(args, 'C')) { if (!(tc->flags & CLIENT_CONTROL)) { cmdq_error(item, "not a control client"); @@ -129,19 +133,6 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) tc->flags |= CLIENT_SIZECHANGED; recalculate_sizes(); } - if (args_has(args, 'F')) { - if (!(tc->flags & CLIENT_CONTROL)) { - cmdq_error(item, "not a control client"); - return (CMD_RETURN_ERROR); - } - s = copy = xstrdup(args_get(args, 'F')); - while ((next = strsep(&s, ",")) != NULL) { - /* Unknown flags are ignored. */ - if (strcmp(next, "no-output") == 0) - tc->flags |= CLIENT_CONTROL_NOOUTPUT; - } - free(copy); - } return (CMD_RETURN_NORMAL); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 82510ce6..d062b946 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_switch_client_entry = { .name = "switch-client", .alias = "switchc", - .args = { "lc:Enpt:rT:Z", 0, 0 }, + .args = { "lc:EFnpt:rT:Z", 0, 0 }, .usage = "[-ElnprZ] [-c target-client] [-t target-session] " "[-T key-table]", @@ -74,8 +74,12 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) wl = target.wl; wp = target.wp; - if (args_has(args, 'r')) - tc->flags ^= CLIENT_READONLY; + if (args_has(args, 'r')) { + if (tc->flags & CLIENT_READONLY) + tc->flags &= ~(CLIENT_READONLY|CLIENT_IGNORESIZE); + else + tc->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE); + } tablename = args_get(args, 'T'); if (tablename != NULL) { diff --git a/format.c b/format.c index a2b2822f..3b9af626 100644 --- a/format.c +++ b/format.c @@ -2678,11 +2678,11 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_utf8", "%d", 1); else format_add(ft, "client_utf8", "%d", 0); - if (c->flags & CLIENT_READONLY) format_add(ft, "client_readonly", "%d", 1); else format_add(ft, "client_readonly", "%d", 0); + format_add(ft, "client_flags", "%s", server_client_get_flags(c)); s = c->session; if (s != NULL) diff --git a/resize.c b/resize.c index 96d733f0..15d146d8 100644 --- a/resize.c +++ b/resize.c @@ -72,17 +72,17 @@ ignore_client_size(struct client *c) return (1); if (c->flags & CLIENT_NOSIZEFLAGS) return (1); - if (c->flags & CLIENT_READONLY) { + if (c->flags & CLIENT_IGNORESIZE) { /* - * Ignore readonly clients if there are any attached clients - * that aren't readonly. + * Ignore flagged clients if there are any attached clients + * that aren't flagged. */ TAILQ_FOREACH (loop, &clients, entry) { if (loop->session == NULL) continue; if (loop->flags & CLIENT_NOSIZEFLAGS) continue; - if (~loop->flags & CLIENT_READONLY) + if (~loop->flags & CLIENT_IGNORESIZE) return (1); } } diff --git a/server-client.c b/server-client.c index a45e9812..86b7a1b8 100644 --- a/server-client.c +++ b/server-client.c @@ -2253,3 +2253,61 @@ server_client_get_cwd(struct client *c, struct session *s) return (home); return ("/"); } + +/* Set client flags. */ +void +server_client_set_flags(struct client *c, const char *flags) +{ + char *s, *copy, *next; + int flag, not; + + s = copy = xstrdup (flags); + while ((next = strsep(&s, ",")) != NULL) { + not = (*next == '!'); + if (not) + next++; + + if (strcmp(next, "no-output") == 0) + flag = CLIENT_CONTROL_NOOUTPUT; + else if (strcmp(next, "read-only") == 0) + flag = CLIENT_READONLY; + else if (strcmp(next, "ignore-size") == 0) + flag = CLIENT_IGNORESIZE; + else + continue; + + log_debug("client %s set flag %s", c->name, next); + if (not) + c->flags &= ~flag; + else + c->flags |= flag; + } + free(copy); + +} + +/*Get client flags. This is only flags useful to show to users. */ +const char * +server_client_get_flags(struct client *c) +{ + static char s[256]; + + *s = '\0'; + if (c->flags & CLIENT_ATTACHED) + strlcat(s, "attached,", sizeof s); + if (c->flags & CLIENT_CONTROL) + strlcat(s, "control-mode,", sizeof s); + if (c->flags & CLIENT_IGNORESIZE) + strlcat(s, "ignore-size,", sizeof s); + if (c->flags & CLIENT_CONTROL_NOOUTPUT) + strlcat(s, "no-output,", sizeof s); + if (c->flags & CLIENT_READONLY) + strlcat(s, "read-only,", sizeof s); + if (c->flags & CLIENT_SUSPENDED) + strlcat(s, "suspended,", sizeof s); + if (c->flags & CLIENT_UTF8) + strlcat(s, "UTF-8,", sizeof s); + if (*s != '\0') + s[strlen(s) - 1] = '\0'; + return (s); +} diff --git a/tmux.1 b/tmux.1 index eafa3751..97d57d5b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -947,6 +947,7 @@ The following commands are available to manage clients and sessions: .It Xo Ic attach-session .Op Fl dErx .Op Fl c Ar working-directory +.Op Fl f Ar flags .Op Fl t Ar target-session .Xc .D1 (alias: Ic attach ) @@ -964,12 +965,30 @@ is given, send .Dv SIGHUP to the parent process of the client as well as detaching the client, typically causing it to exit. +.Fl f +sets a comma-separated list of client flags. +The flags are: +.Bl -tag -width Ds +.It read-only +the client is read-only +.It ignore-size +the client does not affect the size of other clients +.It no-output +the client does not receive pane output in control mode +.El +.Pp +A leading +.Ql ! +turns a flag off if the client is already attached. .Fl r -signifies the client is read-only (only keys bound to the +is an alias for +.Fl f +.Ar read-only,ignore-size . +When a client is read-only, only keys bound to the .Ic detach-client or .Ic switch-client -commands have any effect) +commands have any effect. .Pp If no server is started, .Ic attach-session @@ -1095,6 +1114,7 @@ Lock all clients attached to .Op Fl AdDEPX .Op Fl c Ar start-directory .Op Fl e Ar environment +.Op Fl f Ar flags .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name @@ -1132,6 +1152,9 @@ or is given, the .Ic default-size option is set for the session. +.Fl f +sets a comma-separated list of client flags (see +.Ic attach-session ) . .Pp If run from a terminal, any .Xr termios 4 @@ -1209,7 +1232,7 @@ specified multiple times. .It Xo Ic refresh-client .Op Fl cDlLRSU .Op Fl C Ar XxY -.Op Fl F Ar flags +.Op Fl f Ar flags .Op Fl t Ar target-client .Op Ar adjustment .Xc @@ -1252,12 +1275,10 @@ window, changing the current window in the attached session will reset it. .Pp .Fl C -sets the width and height of a control client and -.Fl F -sets a comma-separated list of flags. -Currently the only flag available is -.Ql no-output -to disable receiving pane output. +sets the width and height of a control client. +.Fl f +sets a comma-separated list of client flags, see +.Ic attach-session . .Pp .Fl l requests the clipboard from the client using the @@ -1377,7 +1398,11 @@ or is used, the client is moved to the last, next or previous session respectively. .Fl r -toggles whether a client is read-only (see the +toggles the client +.Ic read-only +and +.Ic ignore-size +flags (see the .Ic attach-session command). .Pp @@ -4480,6 +4505,7 @@ The following variables are available, where appropriate: .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_created" Ta "" Ta "Time client created" .It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind" +.It Li "client_flags" Ta "" Ta "List of client flags" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_key_table" Ta "" Ta "Current key table" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" diff --git a/tmux.h b/tmux.h index 811b5fb6..7a687c09 100644 --- a/tmux.h +++ b/tmux.h @@ -1568,7 +1568,7 @@ struct client { #define CLIENT_CONTROLCONTROL 0x4000 #define CLIENT_FOCUSED 0x8000 #define CLIENT_UTF8 0x10000 -/* 0x20000 unused */ +#define CLIENT_IGNORESIZE 0x20000 #define CLIENT_IDENTIFIED 0x40000 #define CLIENT_STATUSFORCE 0x80000 #define CLIENT_DOUBLECLICK 0x100000 @@ -2163,7 +2163,7 @@ char *cmd_template_replace(const char *, const char *, int); /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int, - int, const char *, int); + int, const char *, int, const char *); /* cmd-parse.c */ void cmd_parse_empty(struct cmd_parse_input *); @@ -2302,6 +2302,8 @@ void server_client_push_stderr(struct client *); void printflike(2, 3) server_client_add_message(struct client *, const char *, ...); const char *server_client_get_cwd(struct client *, struct session *); +void server_client_set_flags(struct client *, const char *); +const char *server_client_get_flags(struct client *); /* server-fn.c */ void server_redraw_client(struct client *); From 4de0bd4c5c9eec6dd71cac87b91e736944896c22 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:46:01 +0000 Subject: [PATCH 0412/1006] Add M-+ and M-- to expand and collapse all items in tree mode. --- mode-tree.c | 12 +++++++++++- tmux.1 | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mode-tree.c b/mode-tree.c index 8d210d72..c08c802d 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -887,7 +887,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, struct mouse_event *m, u_int *xp, u_int *yp) { struct mode_tree_line *line; - struct mode_tree_item *current, *parent; + struct mode_tree_item *current, *parent, *mti; u_int i, x, y; int choice; key_code tmp; @@ -1058,6 +1058,16 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_build(mtd); } break; + case '-'|KEYC_ESCAPE: + TAILQ_FOREACH(mti, &mtd->children, entry) + mti->expanded = 0; + mode_tree_build(mtd); + break; + case '+'|KEYC_ESCAPE: + TAILQ_FOREACH(mti, &mtd->children, entry) + mti->expanded = 1; + mode_tree_build(mtd); + break; case '?': case '/': case '\023': /* C-s */ diff --git a/tmux.1 b/tmux.1 index 97d57d5b..d115329d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1903,6 +1903,10 @@ The following keys may be used in tree mode: .It Li "Enter" Ta "Choose selected item" .It Li "Up" Ta "Select previous item" .It Li "Down" Ta "Select next item" +.It Li "+" Ta "Expand selected item" +.It Li "-" Ta "Collapse selected item" +.It Li "M-+" Ta "Expand all items" +.It Li "M--" Ta "Collapse all items" .It Li "x" Ta "Kill selected item" .It Li "X" Ta "Kill tagged items" .It Li "<" Ta "Scroll list of previews left" From 367b4e4e0f1d78589398cddb609b33be870af30c Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:47:22 +0000 Subject: [PATCH 0413/1006] Change message log to be per server rather than per client and include every command that is run. --- cmd-queue.c | 25 +++++++++++++++++++++++ cmd-show-messages.c | 18 +++++++++++------ format.c | 4 +--- options-table.c | 2 +- server-client.c | 49 +-------------------------------------------- server.c | 35 ++++++++++++++++++++++++++++++++ status.c | 2 +- tmux.1 | 9 ++------- tmux.h | 18 +++++++++-------- 9 files changed, 88 insertions(+), 74 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 620a3e93..26e2f2f9 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -534,6 +534,28 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs, return (CMD_RETURN_NORMAL); } +/* Add message with command. */ +static void +cmdq_add_message(struct cmdq_item *item) +{ + struct client *c = item->client; + struct cmdq_state *state = item->state; + const char *name, *key; + char *tmp; + + tmp = cmd_print(item->cmd); + if (c != NULL) { + name = c->name; + if (c->session != NULL && state->event.key != KEYC_NONE) { + key = key_string_lookup_key(state->event.key); + server_add_message("%s key %s: %s", name, key, tmp); + } else + server_add_message("%s command: %s", name, tmp); + } else + server_add_message("command: %s", tmp); + free(tmp); +} + /* Fire command on command queue. */ static enum cmd_retval cmdq_fire_command(struct cmdq_item *item) @@ -549,6 +571,8 @@ cmdq_fire_command(struct cmdq_item *item) int flags, quiet = 0; char *tmp; + if (cfg_finished) + cmdq_add_message(item); if (log_get_level() > 1) { tmp = cmd_print(cmd); log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp); @@ -819,6 +843,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) cmd_get_source(cmd, &file, &line); cfg_add_cause("%s:%u: %s", file, line, msg); } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + server_add_message("%s message: %s", c->name, msg); if (~c->flags & CLIENT_UTF8) { tmp = msg; msg = utf8_sanitize(tmp); diff --git a/cmd-show-messages.c b/cmd-show-messages.c index d734ca65..2fa68f2a 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -72,10 +72,10 @@ static enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - struct client *tc = cmdq_get_target_client(item); struct message_entry *msg; - char *tim; + char *s; int done, blank; + struct format_tree *ft; done = blank = 0; if (args_has(args, 'T')) { @@ -89,11 +89,17 @@ cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) if (done) return (CMD_RETURN_NORMAL); - TAILQ_FOREACH(msg, &tc->message_log, entry) { - tim = ctime(&msg->msg_time); - *strchr(tim, '\n') = '\0'; - cmdq_print(item, "%s %s", tim, msg->msg); + ft = format_create_from_target(item); + TAILQ_FOREACH_REVERSE(msg, &message_log, message_list, entry) { + format_add(ft, "message_text", "%s", msg->msg); + format_add(ft, "message_number", "%u", msg->msg_num); + format_add_tv(ft, "message_time", &msg->msg_time); + + s = format_expand(ft, SHOW_MESSAGES_TEMPLATE); + cmdq_print(item, "%s", s); + free(s); } + format_free(ft); return (CMD_RETURN_NORMAL); } diff --git a/format.c b/format.c index 3b9af626..9f01588d 100644 --- a/format.c +++ b/format.c @@ -46,8 +46,6 @@ static void format_job_timer(int, short, void *); static char *format_find(struct format_tree *, const char *, int); static void format_add_cb(struct format_tree *, const char *, format_cb); -static void format_add_tv(struct format_tree *, const char *, - struct timeval *); static int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); static void format_defaults_session(struct format_tree *, @@ -1260,7 +1258,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) } /* Add a key and time. */ -static void +void format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) { struct format_entry *fe, *fe_now; diff --git a/options-table.c b/options-table.c index 5ca63a78..320a380f 100644 --- a/options-table.c +++ b/options-table.c @@ -253,7 +253,7 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, - .default_num = 100 + .default_num = 1000 }, { .name = "set-clipboard", diff --git a/server-client.c b/server-client.c index 86b7a1b8..0bd00fc4 100644 --- a/server-client.c +++ b/server-client.c @@ -213,23 +213,12 @@ server_client_create(int fd) c->queue = cmdq_new(); c->tty.fd = -1; - c->title = NULL; - - c->session = NULL; - c->last_session = NULL; c->tty.sx = 80; c->tty.sy = 24; status_init(c); - c->message_string = NULL; - TAILQ_INIT(&c->message_log); - - c->prompt_string = NULL; - c->prompt_buffer = NULL; - c->prompt_index = 0; - RB_INIT(&c->files); c->flags |= CLIENT_FOCUSED; @@ -272,7 +261,6 @@ server_client_open(struct client *c, char **cause) void server_client_lost(struct client *c) { - struct message_entry *msg, *msg1; struct client_file *cf; c->flags |= CLIENT_DEAD; @@ -313,11 +301,6 @@ server_client_lost(struct client *c) free(c->message_string); if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); - TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { - free(msg->msg); - TAILQ_REMOVE(&c->message_log, msg, entry); - free(msg); - } free(c->prompt_saved); free(c->prompt_string); @@ -1128,6 +1111,7 @@ server_client_key_callback(struct cmdq_item *item, void *data) c->tty.mouse_drag_update(c, m); goto out; } + event->key = key; } /* Find affected pane. */ @@ -2204,37 +2188,6 @@ server_client_dispatch_read_done(struct client *c, struct imsg *imsg) file_fire_done(cf); } -/* Add to client message log. */ -void -server_client_add_message(struct client *c, const char *fmt, ...) -{ - struct message_entry *msg, *msg1; - char *s; - va_list ap; - u_int limit; - - va_start(ap, fmt); - xvasprintf(&s, fmt, ap); - va_end(ap); - - log_debug("message %s (client %p)", s, c); - - msg = xcalloc(1, sizeof *msg); - msg->msg_time = time(NULL); - msg->msg_num = c->message_next++; - msg->msg = s; - TAILQ_INSERT_TAIL(&c->message_log, msg, entry); - - limit = options_get_number(global_options, "message-limit"); - TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { - if (msg->msg_num + limit >= c->message_next) - break; - free(msg->msg); - TAILQ_REMOVE(&c->message_log, msg, entry); - free(msg); - } -} - /* Get client working directory. */ const char * server_client_get_cwd(struct client *c, struct session *s) diff --git a/server.c b/server.c index be655b6a..a4216b87 100644 --- a/server.c +++ b/server.c @@ -51,6 +51,9 @@ static struct event server_ev_accept; struct cmd_find_state marked_pane; +static u_int message_next; +struct message_list message_log; + static int server_loop(void); static void server_send_exit(void); static void server_accept(int, short, void *); @@ -195,6 +198,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, TAILQ_INIT(&clients); RB_INIT(&sessions); key_bindings_init(); + TAILQ_INIT(&message_log); gettimeofday(&start_time, NULL); @@ -483,3 +487,34 @@ server_child_stopped(pid_t pid, int status) } job_check_died(pid, status); } + +/* Add to message log. */ +void +server_add_message(const char *fmt, ...) +{ + struct message_entry *msg, *msg1; + char *s; + va_list ap; + u_int limit; + + va_start(ap, fmt); + xvasprintf(&s, fmt, ap); + va_end(ap); + + log_debug("message: %s", s); + + msg = xcalloc(1, sizeof *msg); + gettimeofday(&msg->msg_time, NULL); + msg->msg_num = message_next++; + msg->msg = s; + TAILQ_INSERT_TAIL(&message_log, msg, entry); + + limit = options_get_number(global_options, "message-limit"); + TAILQ_FOREACH_SAFE(msg, &message_log, entry, msg1) { + if (msg->msg_num + limit >= message_next) + break; + free(msg->msg); + TAILQ_REMOVE(&message_log, msg, entry); + free(msg); + } +} diff --git a/status.c b/status.c index b5442550..375cad4d 100644 --- a/status.c +++ b/status.c @@ -436,7 +436,7 @@ status_message_set(struct client *c, const char *fmt, ...) xvasprintf(&c->message_string, fmt, ap); va_end(ap); - server_client_add_message(c, "%s", c->message_string); + server_add_message("%s message: %s", c->name, c->message_string); delay = options_get_number(c->session->options, "display-time"); if (delay > 0) { diff --git a/tmux.1 b/tmux.1 index d115329d..bac95e88 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1311,15 +1311,10 @@ Rename the session to .Op Fl t Ar target-client .Xc .D1 (alias: Ic showmsgs ) -Show client messages or server information. -Any messages displayed on the status line are saved in a per-client message -log, up to a maximum of the limit set by the +Show server messages or information. +Messages are stored, up to a maximum of the limit set by the .Ar message-limit server option. -With -.Fl t , -display the log for -.Ar target-client . .Fl J and .Fl T diff --git a/tmux.h b/tmux.h index 7a687c09..3c074519 100644 --- a/tmux.h +++ b/tmux.h @@ -1341,11 +1341,13 @@ struct tty_ctx { /* Saved message entry. */ struct message_entry { - char *msg; - u_int msg_num; - time_t msg_time; - TAILQ_ENTRY(message_entry) entry; + char *msg; + u_int msg_num; + struct timeval msg_time; + + TAILQ_ENTRY(message_entry) entry; }; +TAILQ_HEAD(message_list, message_entry); /* Parsed arguments structures. */ struct args_entry; @@ -1603,8 +1605,6 @@ struct client { char *message_string; struct event message_timer; - u_int message_next; - TAILQ_HEAD(, message_entry) message_log; char *prompt_string; struct utf8_data *prompt_buffer; @@ -1846,6 +1846,8 @@ void format_free(struct format_tree *); void format_merge(struct format_tree *, struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); +void format_add_tv(struct format_tree *, const char *, + struct timeval *); void format_each(struct format_tree *, void (*)(const char *, const char *, void *), void *); char *format_expand_time(struct format_tree *, const char *); @@ -2269,6 +2271,7 @@ void file_push(struct client_file *); extern struct tmuxproc *server_proc; extern struct clients clients; extern struct cmd_find_state marked_pane; +extern struct message_list message_log; void server_set_marked(struct session *, struct winlink *, struct window_pane *); void server_clear_marked(void); @@ -2278,6 +2281,7 @@ int server_check_marked(void); int server_start(struct tmuxproc *, int, struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); +void printflike(1, 2) server_add_message(const char *, ...); /* server-client.c */ u_int server_client_how_many(void); @@ -2299,8 +2303,6 @@ void server_client_exec(struct client *, const char *); void server_client_loop(void); void server_client_push_stdout(struct client *); void server_client_push_stderr(struct client *); -void printflike(2, 3) server_client_add_message(struct client *, const char *, - ...); const char *server_client_get_cwd(struct client *, struct session *); void server_client_set_flags(struct client *, const char *); const char *server_client_get_flags(struct client *); From daa95810b53c25d10c90b85ef6fbf8ab32479e23 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:48:35 +0000 Subject: [PATCH 0414/1006] Allow a custom time format to be given to the t format modifier. --- format.c | 55 ++++++++++++++++++++++++++++++++++++++++++++----------- tmux.1 | 15 +++++++++++++++ 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/format.c b/format.c index 9f01588d..9cc8195c 100644 --- a/format.c +++ b/format.c @@ -44,7 +44,6 @@ typedef void (*format_cb)(struct format_tree *, struct format_entry *); static char *format_job_get(struct format_tree *, const char *); static void format_job_timer(int, short, void *); -static char *format_find(struct format_tree *, const char *, int); static void format_add_cb(struct format_tree *, const char *, format_cb); static int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); @@ -122,8 +121,8 @@ struct format_tree { struct cmdq_item *item; struct client *client; - u_int tag; int flags; + u_int tag; time_t time; u_int loop; @@ -1369,16 +1368,17 @@ format_pretty_time(time_t t) /* Find a format entry. */ static char * -format_find(struct format_tree *ft, const char *key, int modifiers) +format_find(struct format_tree *ft, const char *key, int modifiers, + const char *time_format) { struct format_entry *fe, fe_find; struct environ_entry *envent; - static char s[64]; struct options_entry *o; int idx; - char *found = NULL, *saved; + char *found = NULL, *saved, s[512]; const char *errstr; time_t t = 0; + struct tm tm; o = options_parse_get(global_options, key, &idx, 0); if (o == NULL && ft->wp != NULL) @@ -1438,8 +1438,13 @@ found: if (modifiers & FORMAT_PRETTY) found = format_pretty_time(t); else { - ctime_r(&t, s); - s[strcspn(s, "\n")] = '\0'; + if (time_format != NULL) { + localtime_r(&t, &tm); + strftime(s, sizeof s, time_format, &tm); + } else { + ctime_r(&t, s); + s[strcspn(s, "\n")] = '\0'; + } found = xstrdup(s); } return (found); @@ -1467,6 +1472,30 @@ found: return (found); } +/* Remove escaped characters from string. */ +static char * +format_strip(const char *s) +{ + char *out, *cp; + int brackets = 0; + + cp = out = xmalloc(strlen(s) + 1); + for (; *s != '\0'; s++) { + if (*s == '#' && s[1] == '{') + brackets++; + if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { + if (brackets != 0) + *cp++ = *s; + continue; + } + if (*s == '}') + brackets--; + *cp++ = *s; + } + *cp = '\0'; + return (out); +} + /* Skip until end. */ const char * format_skip(const char *s, const char *end) @@ -1476,7 +1505,7 @@ format_skip(const char *s, const char *end) for (; *s != '\0'; s++) { if (*s == '#' && s[1] == '{') brackets++; - if (*s == '#' && strchr(",#{}", s[1]) != NULL) { + if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { s++; continue; } @@ -1584,7 +1613,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) *count = 0; while (*cp != '\0' && *cp != ':') { - /* Skip and separator character. */ + /* Skip any separator character. */ if (*cp == ';') cp++; @@ -1975,6 +2004,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, { 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; @@ -2052,6 +2082,9 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, break; if (strchr(fm->argv[0], 'p') != NULL) modifiers |= FORMAT_PRETTY; + else if (fm->argc >= 2 && + strchr(fm->argv[0], 'f') != NULL) + time_format = format_strip(fm->argv[1]); break; case 'q': modifiers |= FORMAT_QUOTE; @@ -2178,7 +2211,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, condition = xstrndup(copy + 1, cp - (copy + 1)); format_log(ft, "condition is: %s", condition); - found = format_find(ft, condition, modifiers); + found = format_find(ft, condition, modifiers, time_format); if (found == NULL) { /* * If the condition not found, try to expand it. If @@ -2223,7 +2256,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, value = xstrdup(""); } else { /* Neither: look up directly. */ - value = format_find(ft, copy, modifiers); + value = format_find(ft, copy, modifiers, time_format); if (value == NULL) { format_log(ft, "format '%s' not found", copy); value = xstrdup(""); diff --git a/tmux.1 b/tmux.1 index bac95e88..852ae80f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4407,6 +4407,21 @@ Adding .Ql p ( .Ql `t/p` ) will use shorter but less accurate time format for times in the past. +A custom format may be given using an +.Ql f +suffix (note that +.Ql % +must be escaped as +.Ql %% +if the format is separately being passed through +.Xr strftime 3 , +for example in the +.Ic status-left +option): +.Ql #{t/f/%%H#:%%M:window_activity} , +see +.Xr strftime 3 . +.Pp The .Ql b:\& and From 6ea6d46d0a1b206ece54a05af4f3fe7a3d6fe4cc Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:49:20 +0000 Subject: [PATCH 0415/1006] Store and restore cursor position when copy mode is resized, from Anindya Mukherjee. --- grid.c | 2 +- screen.c | 52 ++++++++++++++++++++++++++------------------------ tmux.h | 3 +-- window-copy.c | 53 +++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 70 insertions(+), 40 deletions(-) diff --git a/grid.c b/grid.c index 81f3709c..2437e2ea 100644 --- a/grid.c +++ b/grid.c @@ -1285,7 +1285,7 @@ grid_reflow(struct grid *gd, u_int sx) /* * If the line is exactly right or the first character is wider - * than the targe width, just move it across unchanged. + * than the target width, just move it across unchanged. */ if (width == sx || first > sx) { grid_reflow_move(target, gl); diff --git a/screen.c b/screen.c index f416ac37..227934dd 100644 --- a/screen.c +++ b/screen.c @@ -49,7 +49,7 @@ struct screen_title_entry { TAILQ_HEAD(screen_titles, screen_title_entry); static void screen_resize_y(struct screen *, u_int, int, u_int *); -static void screen_reflow(struct screen *, u_int, u_int *, u_int *); +static void screen_reflow(struct screen *, u_int, u_int *, u_int *, int); /* Free titles stack. */ static void @@ -220,27 +220,19 @@ screen_pop_title(struct screen *s) } } -/* Resize screen and return cursor position. */ +/* Resize screen with options. */ void screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, - int eat_empty, u_int *cx, u_int *cy) + int eat_empty, int cursor) { - u_int tcx, tcy; + u_int cx = s->cx, cy = s->grid->hsize + s->cy; if (s->write_list != NULL) screen_write_free_list(s); - if (cx == NULL) - cx = &tcx; - *cx = s->cx; - - if (cy == NULL) - cy = т - *cy = s->grid->hsize + s->cy; - log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)", __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy, - *cx, *cy); + cx, cy); if (sx < 1) sx = 1; @@ -254,20 +246,21 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, reflow = 0; if (sy != screen_size_y(s)) - screen_resize_y(s, sy, eat_empty, cy); + screen_resize_y(s, sy, eat_empty, &cy); if (reflow) - screen_reflow(s, sx, cx, cy); + screen_reflow(s, sx, &cx, &cy, cursor); - if (*cy >= s->grid->hsize) { - s->cx = *cx; - s->cy = (*cy) - s->grid->hsize; + if (cy >= s->grid->hsize) { + s->cx = cx; + s->cy = cy - s->grid->hsize; } else { s->cx = 0; s->cy = 0; } + log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx, - s->cy, *cx, *cy); + s->cy, cx, cy); if (s->write_list != NULL) screen_write_make_list(s); @@ -277,7 +270,7 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, void screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) { - screen_resize_cursor(s, sx, sy, reflow, 1, NULL, NULL); + screen_resize_cursor(s, sx, sy, reflow, 1, 1); } static void @@ -525,17 +518,26 @@ screen_select_cell(struct screen *s, struct grid_cell *dst, /* Reflow wrapped lines. */ static void -screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy) +screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor) { u_int wx, wy; - grid_wrap_position(s->grid, *cx, *cy, &wx, &wy); - log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx, wy); + if (cursor) { + grid_wrap_position(s->grid, *cx, *cy, &wx, &wy); + log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx, + wy); + } grid_reflow(s->grid, new_x); - grid_unwrap_position(s->grid, cx, cy, wx, wy); - log_debug("%s: new cursor is %u,%u", __func__, *cx,* cy); + if (cursor) { + grid_unwrap_position(s->grid, cx, cy, wx, wy); + log_debug("%s: new cursor is %u,%u", __func__, *cx, *cy); + } + else { + *cx = 0; + *cy = s->grid->hsize; + } } /* diff --git a/tmux.h b/tmux.h index 3c074519..bd746fe7 100644 --- a/tmux.h +++ b/tmux.h @@ -2529,8 +2529,7 @@ void screen_set_path(struct screen *, const char *); void screen_push_title(struct screen *); void screen_pop_title(struct screen *); void screen_resize(struct screen *, u_int, u_int, int); -void screen_resize_cursor(struct screen *, u_int, u_int, int, int, u_int *, - u_int *); +void screen_resize_cursor(struct screen *, u_int, u_int, int, int, int); void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int, u_int, int, struct grid_cell *); void screen_clear_selection(struct screen *); diff --git a/window-copy.c b/window-copy.c index 38e87b67..f500a65e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -306,8 +306,9 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, u_int *cy, int trim) { struct screen *dst; - u_int sy; const struct grid_line *gl; + u_int sy, wx, wy; + int reflow; dst = xcalloc(1, sizeof *dst); @@ -324,6 +325,12 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, screen_size_x(src), sy, screen_size_x(hint), screen_hsize(src) + screen_size_y(src)); screen_init(dst, screen_size_x(src), sy, screen_hlimit(src)); + + /* + * Ensure history is on for the backing grid so lines are not deleted + * during resizing. + */ + dst->grid->flags |= GRID_HISTORY; grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy); dst->grid->sy = sy - screen_hsize(src); @@ -337,8 +344,19 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, dst->cy = src->cy; } + if (cx != NULL && cy != NULL) { + *cx = dst->cx; + *cy = screen_hsize(dst) + dst->cy; + reflow = (screen_size_x(hint) != screen_size_x(dst)); + } + else + reflow = 0; + if (reflow) + grid_wrap_position(dst->grid, *cx, *cy, &wx, &wy); screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1, - 0, cx, cy); + 0, 0); + if (reflow) + grid_unwrap_position(dst->grid, cx, cy, wx, wy); return (dst); } @@ -392,13 +410,12 @@ window_copy_init(struct window_mode_entry *wme, data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy, wme->swp != wme->wp); + data->cx = cx; if (cy < screen_hsize(data->backing)) { - data->cx = cx; data->cy = 0; data->oy = screen_hsize(data->backing) - cy; } else { - data->cx = data->backing->cx; - data->cy = data->backing->cy; + data->cy = cy - screen_hsize(data->backing); data->oy = 0; } @@ -731,16 +748,28 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; + struct grid *gd = data->backing->grid; + u_int cx, cy, wx, wy; + int reflow; screen_resize(s, sx, sy, 0); - screen_resize_cursor(data->backing, sx, sy, 1, 0, NULL, NULL); + cx = data->cx; + cy = gd->hsize + data->cy - data->oy; + reflow = (gd->sx != sx); + if (reflow) + grid_wrap_position(gd, cx, cy, &wx, &wy); + screen_resize_cursor(data->backing, sx, sy, 1, 0, 0); + if (reflow) + grid_unwrap_position(gd, &cx, &cy, wx, wy); - if (data->cy > sy - 1) - data->cy = sy - 1; - if (data->cx > sx) - data->cx = sx; - if (data->oy > screen_hsize(data->backing)) - data->oy = screen_hsize(data->backing); + data->cx = cx; + if (cy < gd->hsize) { + data->cy = 0; + data->oy = gd->hsize - cy; + } else { + data->cy = cy - gd->hsize; + data->oy = 0; + } window_copy_size_changed(wme); window_copy_redraw_screen(wme); From 472d77fd0f4af8431267473df3cf109030760fa1 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 15:54:20 +0000 Subject: [PATCH 0416/1006] Support embedded styles in the display-message message, GitHub issue 2206. --- alerts.c | 8 +++++--- cmd-display-message.c | 2 +- cmd-list-keys.c | 2 +- cmd-queue.c | 2 +- mode-tree.c | 2 +- status.c | 8 ++++++-- tmux.h | 4 +++- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/alerts.c b/alerts.c index 6fe88a09..9675934a 100644 --- a/alerts.c +++ b/alerts.c @@ -316,8 +316,10 @@ 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, "%s in current window", type); - else - status_message_set(c, "%s in window %d", type, wl->idx); + status_message_set(c, 1, "%s in current window", type); + else { + status_message_set(c, 1, "%s in window %d", type, + wl->idx); + } } } diff --git a/cmd-display-message.c b/cmd-display-message.c index 4e69f03a..634f0a93 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -117,7 +117,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, "%s", msg); + status_message_set(tc, 0, "%s", msg); free(msg); format_free(ft); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 1141bbb5..4e7ba39c 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -112,7 +112,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, "%s%s%s", prefix, tmp, note); + status_message_set(tc, 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 26e2f2f9..a40053a6 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -856,7 +856,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) c->retval = 1; } else { *msg = toupper((u_char) *msg); - status_message_set(c, "%s", msg); + status_message_set(c, 1, "%s", msg); } free(msg); diff --git a/mode-tree.c b/mode-tree.c index c08c802d..b3b3f335 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1110,7 +1110,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, "%s", error); + status_message_set(c, 1, "%s", error); } free(error); } diff --git a/status.c b/status.c index 375cad4d..b5fa0824 100644 --- a/status.c +++ b/status.c @@ -423,7 +423,7 @@ status_redraw(struct client *c) /* Set a status line message. */ void -status_message_set(struct client *c, const char *fmt, ...) +status_message_set(struct client *c, int ignore_styles, const char *fmt, ...) { struct timeval tv; va_list ap; @@ -433,6 +433,7 @@ status_message_set(struct client *c, const char *fmt, ...) status_push_screen(c); va_start(ap, fmt); + c->message_ignore_styles = ignore_styles; xvasprintf(&c->message_string, fmt, ap); va_end(ap); @@ -515,7 +516,10 @@ status_message_redraw(struct client *c) for (offset = 0; offset < c->tty.sx; offset++) screen_write_putc(&ctx, &gc, ' '); screen_write_cursormove(&ctx, 0, lines - 1, 0); - screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); + if (c->message_ignore_styles) + screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); + else + format_draw(&ctx, &gc, c->tty.sx, c->message_string, NULL); screen_write_stop(&ctx); if (grid_compare(sl->active->grid, old_screen.grid) == 0) { diff --git a/tmux.h b/tmux.h index bd746fe7..385ba212 100644 --- a/tmux.h +++ b/tmux.h @@ -1603,6 +1603,7 @@ struct client { uint64_t redraw_panes; + int message_ignore_styles; char *message_string; struct event message_timer; @@ -2340,7 +2341,8 @@ 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(2, 3) status_message_set(struct client *, const char *, ...); +void printflike(3, 4) status_message_set(struct client *, int, const char *, + ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, const char *, const char *, From d67245c734c9c600ad6d186570a1230aa21b80c8 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:02:24 +0000 Subject: [PATCH 0417/1006] Add a customize mode where keys and options may be browsed and changed, includes adding a brief description of each option. Bound to "C" by default. --- Makefile | 1 + cmd-choose-tree.c | 17 +- cmd-list-keys.c | 8 +- cmd-run-shell.c | 3 +- cmd-set-option.c | 216 +------ cmd-show-messages.c | 4 + cmd-show-options.c | 8 +- cmd.c | 2 + format-draw.c | 2 +- format.c | 2 +- key-bindings.c | 60 +- mode-tree.c | 97 ++- options-table.c | 358 ++++++++--- options.c | 252 +++++++- screen-write.c | 92 ++- style.c | 7 +- tmux.1 | 47 ++ tmux.h | 33 +- window-buffer.c | 4 +- window-client.c | 2 +- window-customize.c | 1430 +++++++++++++++++++++++++++++++++++++++++++ window-tree.c | 2 +- 22 files changed, 2284 insertions(+), 363 deletions(-) create mode 100644 window-customize.c diff --git a/Makefile b/Makefile index 0f1c94c4..7fb664ad 100644 --- a/Makefile +++ b/Makefile @@ -120,6 +120,7 @@ SRCS= alerts.c \ window-client.c \ window-clock.c \ window-copy.c \ + window-customize.c \ window-tree.c \ window.c \ xmalloc.c \ diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 0ada8fd4..a58469ac 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -68,6 +68,19 @@ const struct cmd_entry cmd_choose_buffer_entry = { .exec = cmd_choose_tree_exec }; +const struct cmd_entry cmd_customize_mode_entry = { + .name = "customize-mode", + .alias = NULL, + + .args = { "F:f:Nt:Z", 0, 0 }, + .usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE, + + .target = { 't', CMD_FIND_PANE, 0 }, + + .flags = 0, + .exec = cmd_choose_tree_exec +}; + static enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) { @@ -84,7 +97,9 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) if (server_client_how_many() == 0) return (CMD_RETURN_NORMAL); mode = &window_client_mode; - } else + } else if (cmd_get_entry(self) == &cmd_customize_mode_entry) + mode = &window_customize_mode; + else mode = &window_tree_mode; window_pane_set_mode(wp, NULL, mode, target, args); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 4e7ba39c..60ef73af 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -68,7 +68,8 @@ cmd_list_keys_get_width(const char *tablename, key_code only) while (bd != NULL) { if ((only != KEYC_UNKNOWN && bd->key != only) || KEYC_IS_MOUSE(bd->key) || - bd->note == NULL) { + bd->note == NULL || + *bd->note == '\0') { bd = key_bindings_next(table, bd); continue; } @@ -99,14 +100,15 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, while (bd != NULL) { if ((only != KEYC_UNKNOWN && bd->key != only) || KEYC_IS_MOUSE(bd->key) || - (bd->note == NULL && !args_has(args, 'a'))) { + ((bd->note == NULL || *bd->note == '\0') && + !args_has(args, 'a'))) { bd = key_bindings_next(table, bd); continue; } found = 1; key = key_string_lookup_key(bd->key); - if (bd->note == NULL) + if (bd->note == NULL || *bd->note == '\0') note = cmd_list_print(bd->cmdlist, 1); else note = xstrdup(bd->note); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 82d4a1a2..3792aaae 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -190,7 +190,8 @@ cmd_run_shell_callback(struct job *job) retcode = WTERMSIG(status); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); retcode += 128; - } + } else + retcode = 0; if (msg != NULL) cmd_run_shell_print(job, msg); free(msg); diff --git a/cmd-set-option.c b/cmd-set-option.c index e04aa7ff..c6f83796 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -30,15 +30,6 @@ static enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmdq_item *); -static int cmd_set_option_set(struct cmd *, struct cmdq_item *, - struct options *, struct options_entry *, const char *); -static int cmd_set_option_flag(struct cmdq_item *, - const struct options_table_entry *, struct options *, - const char *); -static int cmd_set_option_choice(struct cmdq_item *, - const struct options_table_entry *, struct options *, - const char *); - const struct cmd_entry cmd_set_option_entry = { .name = "set-option", .alias = "set", @@ -84,10 +75,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); struct cmd_find_state *target = cmdq_get_target(item); - struct client *loop; - struct session *s = target->s; - struct window *w; - struct window_pane *wp; struct options *oo; struct options_entry *parent, *o; char *name, *argument, *value = NULL, *cause; @@ -138,7 +125,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) parent = options_get(oo, name); /* Check that array options and indexes match up. */ - if (idx != -1 && (*name == '@' || !options_isarray(parent))) { + if (idx != -1 && (*name == '@' || !options_is_array(parent))) { cmdq_error(item, "not an array: %s", argument); goto fail; } @@ -185,10 +172,15 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) goto fail; } options_set_string(oo, name, append, "%s", value); - } else if (idx == -1 && !options_isarray(parent)) { - error = cmd_set_option_set(self, item, oo, parent, value); - if (error != 0) + } else if (idx == -1 && !options_is_array(parent)) { + error = options_from_string(oo, options_table_entry(parent), + options_table_entry(parent)->name, value, + args_has(args, 'a'), &cause); + if (error != 0) { + cmdq_error(item, "%s", cause); + free(cause); goto fail; + } } else { if (value == NULL) { cmdq_error(item, "empty value"); @@ -212,51 +204,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) } } - /* Update timers and so on for various options. */ - if (strcmp(name, "automatic-rename") == 0) { - RB_FOREACH(w, windows, &windows) { - if (w->active == NULL) - continue; - if (options_get_number(w->options, "automatic-rename")) - w->active->flags |= PANE_CHANGED; - } - } - if (strcmp(name, "key-table") == 0) { - TAILQ_FOREACH(loop, &clients, entry) - server_client_set_key_table(loop, NULL); - } - if (strcmp(name, "user-keys") == 0) { - TAILQ_FOREACH(loop, &clients, entry) { - if (loop->tty.flags & TTY_OPENED) - tty_keys_build(&loop->tty); - } - } - if (strcmp(name, "status") == 0 || - strcmp(name, "status-interval") == 0) - status_timer_start_all(); - if (strcmp(name, "monitor-silence") == 0) - alerts_reset_all(); - if (strcmp(name, "window-style") == 0 || - strcmp(name, "window-active-style") == 0) { - RB_FOREACH(wp, window_pane_tree, &all_window_panes) - wp->flags |= PANE_STYLECHANGED; - } - if (strcmp(name, "pane-border-status") == 0) { - RB_FOREACH(w, windows, &windows) - layout_fix_panes(w); - } - RB_FOREACH(s, sessions, &sessions) - status_update_cache(s); - - /* - * Update sizes and redraw. May not always be necessary but do it - * anyway. - */ - recalculate_sizes(); - TAILQ_FOREACH(loop, &clients, entry) { - if (loop->session != NULL) - server_redraw_client(loop); - } + options_push_changes(name); out: free(argument); @@ -270,147 +218,3 @@ fail: free(name); return (CMD_RETURN_ERROR); } - -static int -cmd_set_option_check_string(const struct options_table_entry *oe, - const char *value, char **cause) -{ - struct style sy; - - if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) { - xasprintf(cause, "not a suitable shell: %s", value); - return (-1); - } - if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) { - xasprintf(cause, "value is invalid: %s", value); - return (-1); - } - if ((oe->flags & OPTIONS_TABLE_IS_STYLE) && - strstr(value, "#{") == NULL && - style_parse(&sy, &grid_default_cell, value) != 0) { - xasprintf(cause, "invalid style: %s", value); - return (-1); - } - return (0); -} - -static int -cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, - struct options_entry *parent, const char *value) -{ - const struct options_table_entry *oe; - struct args *args = cmd_get_args(self); - int append = args_has(args, 'a'); - long long number; - const char *errstr, *new; - char *old, *cause; - key_code key; - - oe = options_table_entry(parent); - if (value == NULL && - oe->type != OPTIONS_TABLE_FLAG && - oe->type != OPTIONS_TABLE_CHOICE) { - cmdq_error(item, "empty value"); - return (-1); - } - - switch (oe->type) { - case OPTIONS_TABLE_STRING: - old = xstrdup(options_get_string(oo, oe->name)); - options_set_string(oo, oe->name, append, "%s", value); - new = options_get_string(oo, oe->name); - if (cmd_set_option_check_string(oe, new, &cause) != 0) { - cmdq_error(item, "%s", cause); - free(cause); - - options_set_string(oo, oe->name, 0, "%s", old); - free(old); - return (-1); - } - free(old); - return (0); - case OPTIONS_TABLE_NUMBER: - number = strtonum(value, oe->minimum, oe->maximum, &errstr); - if (errstr != NULL) { - cmdq_error(item, "value is %s: %s", errstr, value); - return (-1); - } - options_set_number(oo, oe->name, number); - return (0); - case OPTIONS_TABLE_KEY: - key = key_string_lookup_string(value); - if (key == KEYC_UNKNOWN) { - cmdq_error(item, "bad key: %s", value); - return (-1); - } - options_set_number(oo, oe->name, key); - return (0); - case OPTIONS_TABLE_COLOUR: - if ((number = colour_fromstring(value)) == -1) { - cmdq_error(item, "bad colour: %s", value); - return (-1); - } - options_set_number(oo, oe->name, number); - return (0); - case OPTIONS_TABLE_FLAG: - return (cmd_set_option_flag(item, oe, oo, value)); - case OPTIONS_TABLE_CHOICE: - return (cmd_set_option_choice(item, oe, oo, value)); - case OPTIONS_TABLE_COMMAND: - break; - } - return (-1); -} - -static int -cmd_set_option_flag(struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - int flag; - - if (value == NULL || *value == '\0') - flag = !options_get_number(oo, oe->name); - else if (strcmp(value, "1") == 0 || - strcasecmp(value, "on") == 0 || - strcasecmp(value, "yes") == 0) - flag = 1; - else if (strcmp(value, "0") == 0 || - strcasecmp(value, "off") == 0 || - strcasecmp(value, "no") == 0) - flag = 0; - else { - cmdq_error(item, "bad value: %s", value); - return (-1); - } - options_set_number(oo, oe->name, flag); - return (0); -} - -static int -cmd_set_option_choice(struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - const char **cp; - int n, choice = -1; - - if (value == NULL) { - choice = options_get_number(oo, oe->name); - if (choice < 2) - choice = !choice; - } else { - n = 0; - for (cp = oe->choices; *cp != NULL; cp++) { - if (strcmp(*cp, value) == 0) - choice = n; - n++; - } - if (choice == -1) { - cmdq_error(item, "unknown value: %s", value); - return (-1); - } - } - options_set_number(oo, oe->name, choice); - return (0); -} diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 2fa68f2a..deb0487c 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -29,6 +30,9 @@ * Show client message log. */ +#define SHOW_MESSAGES_TEMPLATE \ + "#{t/p:message_time}: #{message_text}" + static enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmdq_item *); diff --git a/cmd-show-options.c b/cmd-show-options.c index e2aec930..8e70eaa9 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -151,7 +151,7 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, xasprintf(&tmp, "%s[%d]", name, idx); name = tmp; } else { - if (options_isarray(o)) { + if (options_is_array(o)) { a = options_array_first(o); if (a == NULL) { if (!args_has(args, 'v')) @@ -168,10 +168,10 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, } } - value = options_tostring(o, idx, 0); + value = options_to_string(o, idx, 0); if (args_has(args, 'v')) cmdq_print(item, "%s", value); - else if (options_isstring(o)) { + else if (options_is_string(o)) { escaped = args_escape(value); if (parent) cmdq_print(item, "%s* %s", name, escaped); @@ -229,7 +229,7 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, } else parent = 0; - if (!options_isarray(o)) + if (!options_is_array(o)) cmd_show_options_print(self, item, o, -1, parent); else if ((a = options_array_first(o)) == NULL) { if (!args_has(args, 'v')) { diff --git a/cmd.c b/cmd.c index bc807cbe..8a977023 100644 --- a/cmd.c +++ b/cmd.c @@ -40,6 +40,7 @@ extern const struct cmd_entry cmd_clock_mode_entry; extern const struct cmd_entry cmd_command_prompt_entry; extern const struct cmd_entry cmd_confirm_before_entry; extern const struct cmd_entry cmd_copy_mode_entry; +extern const struct cmd_entry cmd_customize_mode_entry; extern const struct cmd_entry cmd_delete_buffer_entry; extern const struct cmd_entry cmd_detach_client_entry; extern const struct cmd_entry cmd_display_menu_entry; @@ -130,6 +131,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_command_prompt_entry, &cmd_confirm_before_entry, &cmd_copy_mode_entry, + &cmd_customize_mode_entry, &cmd_delete_buffer_entry, &cmd_detach_client_entry, &cmd_display_menu_entry, diff --git a/format-draw.c b/format-draw.c index 3751082e..bd32b2a8 100644 --- a/format-draw.c +++ b/format-draw.c @@ -547,7 +547,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, */ cp = expanded; while (*cp != '\0') { - if (cp[0] != '#' || cp[1] != '[') { + if (cp[0] != '#' || cp[1] != '[' || sy.ignore) { /* See if this is a UTF-8 character. */ if ((more = utf8_open(ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) diff --git a/format.c b/format.c index 9cc8195c..c7f7b90a 100644 --- a/format.c +++ b/format.c @@ -1392,7 +1392,7 @@ format_find(struct format_tree *ft, const char *key, int modifiers, if (o == NULL) o = options_parse_get(global_s_options, key, &idx, 0); if (o != NULL) { - found = options_tostring(o, idx, 1); + found = options_to_string(o, idx, 1); goto found; } diff --git a/key-bindings.c b/key-bindings.c index 85bfb788..ecd2f7dc 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -89,9 +89,8 @@ key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) } static void -key_bindings_free(struct key_table *table, struct key_binding *bd) +key_bindings_free(struct key_binding *bd) { - RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free((void *)bd->note); free(bd); @@ -110,6 +109,7 @@ key_bindings_get_table(const char *name, int create) table = xmalloc(sizeof *table); table->name = xstrdup(name); RB_INIT(&table->key_bindings); + RB_INIT(&table->default_key_bindings); table->references = 1; /* one reference in key_tables */ RB_INSERT(key_tables, &key_tables, table); @@ -138,8 +138,14 @@ key_bindings_unref_table(struct key_table *table) if (--table->references != 0) return; - RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) - key_bindings_free(table, bd); + RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) { + RB_REMOVE(key_bindings, &table->key_bindings, bd); + key_bindings_free(bd); + } + RB_FOREACH_SAFE(bd, key_bindings, &table->default_key_bindings, bd1) { + RB_REMOVE(key_bindings, &table->default_key_bindings, bd); + key_bindings_free(bd); + } free((void *)table->name); free(table); @@ -154,6 +160,15 @@ key_bindings_get(struct key_table *table, key_code key) return (RB_FIND(key_bindings, &table->key_bindings, &bd)); } +struct key_binding * +key_bindings_get_default(struct key_table *table, key_code key) +{ + struct key_binding bd; + + bd.key = key; + return (RB_FIND(key_bindings, &table->default_key_bindings, &bd)); +} + struct key_binding * key_bindings_first(struct key_table *table) { @@ -176,8 +191,10 @@ 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_XTERM); - if (bd != NULL) - key_bindings_free(table, bd); + if (bd != NULL) { + RB_REMOVE(key_bindings, &table->key_bindings, bd); + key_bindings_free(bd); + } bd = xcalloc(1, sizeof *bd); bd->key = key; @@ -203,9 +220,12 @@ key_bindings_remove(const char *name, key_code key) bd = key_bindings_get(table, key & ~KEYC_XTERM); if (bd == NULL) return; - key_bindings_free(table, bd); - if (RB_EMPTY(&table->key_bindings)) { + RB_REMOVE(key_bindings, &table->key_bindings, bd); + key_bindings_free(bd); + + if (RB_EMPTY(&table->key_bindings) && + RB_EMPTY(&table->default_key_bindings)) { RB_REMOVE(key_tables, &key_tables, table); key_bindings_unref_table(table); } @@ -228,6 +248,28 @@ key_bindings_remove_table(const char *name) } } +static enum cmd_retval +key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data) +{ + struct key_table *table; + struct key_binding *bd, *new_bd; + + RB_FOREACH(table, key_tables, &key_tables) { + RB_FOREACH(bd, key_bindings, &table->key_bindings) { + new_bd = xcalloc(1, sizeof *bd); + new_bd->key = bd->key; + if (bd->note != NULL) + new_bd->note = xstrdup(bd->note); + new_bd->flags = bd->flags; + new_bd->cmdlist = bd->cmdlist; + new_bd->cmdlist->references++; + RB_INSERT(key_bindings, &table->default_key_bindings, + new_bd); + } + } + return (CMD_RETURN_NORMAL); +} + void key_bindings_init(void) { @@ -278,6 +320,7 @@ key_bindings_init(void) "bind -N 'Toggle the marked pane' m select-pane -m", "bind -N 'Select the next window' n next-window", "bind -N 'Select the next pane' o select-pane -t:.+", + "bind -N 'Customize options' C customize-mode -Z", "bind -N 'Select the previous pane' p previous-window", "bind -N 'Display pane numbers' q display-panes", "bind -N 'Redraw the current client' r refresh-client", @@ -524,6 +567,7 @@ key_bindings_init(void) cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL)); cmd_list_free(pr->cmdlist); } + cmdq_append(NULL, cmdq_get_callback(key_bindings_init_done, NULL)); } static enum cmd_retval diff --git a/mode-tree.c b/mode-tree.c index b3b3f335..131830d6 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -45,6 +45,7 @@ struct mode_tree_data { mode_tree_draw_cb drawcb; mode_tree_search_cb searchcb; mode_tree_menu_cb menucb; + mode_tree_height_cb heightcb; struct mode_tree_list children; struct mode_tree_list saved; @@ -79,6 +80,7 @@ struct mode_tree_item { int expanded; int tagged; + int draw_as_parent; struct mode_tree_list children; TAILQ_ENTRY(mode_tree_item) entry; @@ -210,7 +212,7 @@ mode_tree_clear_tagged(struct mode_tree_list *mtl) } } -static void +void mode_tree_up(struct mode_tree_data *mtd, int wrap) { if (mtd->current == 0) { @@ -247,6 +249,12 @@ mode_tree_get_current(struct mode_tree_data *mtd) return (mtd->line_list[mtd->current].item->itemdata); } +const char * +mode_tree_get_current_name(struct mode_tree_data *mtd) +{ + return (mtd->line_list[mtd->current].item->name); +} + void mode_tree_expand_current(struct mode_tree_data *mtd) { @@ -256,6 +264,15 @@ mode_tree_expand_current(struct mode_tree_data *mtd) } } +void +mode_tree_collapse_current(struct mode_tree_data *mtd) +{ + if (mtd->line_list[mtd->current].item->expanded) { + mtd->line_list[mtd->current].item->expanded = 0; + mode_tree_build(mtd); + } +} + static int mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found) { @@ -343,7 +360,8 @@ mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb, struct mode_tree_data * mode_tree_start(struct window_pane *wp, struct args *args, mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb, - mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, void *modedata, + mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, + mode_tree_height_cb heightcb, void *modedata, const struct menu_item *menu, const char **sort_list, u_int sort_size, struct screen **s) { @@ -381,6 +399,7 @@ mode_tree_start(struct window_pane *wp, struct args *args, mtd->drawcb = drawcb; mtd->searchcb = searchcb; mtd->menucb = menucb; + mtd->heightcb = heightcb; TAILQ_INIT(&mtd->children); @@ -404,6 +423,27 @@ mode_tree_zoom(struct mode_tree_data *mtd, struct args *args) mtd->zoomed = -1; } +static void +mode_tree_set_height(struct mode_tree_data *mtd) +{ + struct screen *s = &mtd->screen; + u_int height; + + if (mtd->heightcb != NULL) { + height = mtd->heightcb(mtd, screen_size_y(s)); + if (height < screen_size_y(s)) + mtd->height = screen_size_y(s) - height; + } else { + mtd->height = (screen_size_y(s) / 3) * 2; + if (mtd->height > mtd->line_size) + mtd->height = screen_size_y(s) / 2; + } + if (mtd->height < 10) + mtd->height = screen_size_y(s); + if (screen_size_y(s) - mtd->height < 2) + mtd->height = screen_size_y(s); +} + void mode_tree_build(struct mode_tree_data *mtd) { @@ -434,15 +474,9 @@ mode_tree_build(struct mode_tree_data *mtd) mode_tree_set_current(mtd, tag); mtd->width = screen_size_x(s); - if (mtd->preview) { - mtd->height = (screen_size_y(s) / 3) * 2; - if (mtd->height > mtd->line_size) - mtd->height = screen_size_y(s) / 2; - if (mtd->height < 10) - mtd->height = screen_size_y(s); - if (screen_size_y(s) - mtd->height < 2) - mtd->height = screen_size_y(s); - } else + if (mtd->preview) + mode_tree_set_height(mtd); + else mtd->height = screen_size_y(s); mode_tree_check_selected(mtd); } @@ -494,7 +528,7 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, struct mode_tree_item *mti, *saved; log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag, - name, text); + name, (text == NULL ? "" : text)); mti = xcalloc(1, sizeof *mti); mti->parent = parent; @@ -502,7 +536,8 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, mti->tag = tag; mti->name = xstrdup(name); - mti->text = xstrdup(text); + if (text != NULL) + mti->text = xstrdup(text); saved = mode_tree_find_item(&mtd->saved, tag); if (saved != NULL) { @@ -524,6 +559,12 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, return (mti); } +void +mode_tree_draw_as_parent(struct mode_tree_item *mti) +{ + mti->draw_as_parent = 1; +} + void mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti) { @@ -621,8 +662,8 @@ mode_tree_draw(struct mode_tree_data *mtd) tag = "*"; else tag = ""; - xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name, - tag); + xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name, + tag, (mti->text != NULL) ? ": " : "" ); width = utf8_cstrwidth(text); if (width > w) width = w; @@ -636,11 +677,17 @@ mode_tree_draw(struct mode_tree_data *mtd) if (i != mtd->current) { screen_write_clearendofline(&ctx, 8); screen_write_nputs(&ctx, w, &gc0, "%s", text); - format_draw(&ctx, &gc0, w - width, mti->text, NULL); + if (mti->text != NULL) { + format_draw(&ctx, &gc0, w - width, mti->text, + NULL); + } } else { screen_write_clearendofline(&ctx, gc.bg); screen_write_nputs(&ctx, w, &gc, "%s", text); - format_draw(&ctx, &gc, w - width, mti->text, NULL); + if (mti->text != NULL) { + format_draw(&ctx, &gc, w - width, mti->text, + NULL); + } } free(text); @@ -658,13 +705,18 @@ mode_tree_draw(struct mode_tree_data *mtd) line = &mtd->line_list[mtd->current]; mti = line->item; + if (mti->draw_as_parent) + mti = mti->parent; screen_write_cursormove(&ctx, 0, h, 0); screen_write_box(&ctx, w, sy - h); - xasprintf(&text, " %s (sort: %s%s)", mti->name, - mtd->sort_list[mtd->sort_crit.field], - mtd->sort_crit.reversed ? ", reversed" : ""); + if (mtd->sort_list != NULL) { + xasprintf(&text, " %s (sort: %s%s)", mti->name, + mtd->sort_list[mtd->sort_crit.field], + mtd->sort_crit.reversed ? ", reversed" : ""); + } else + xasprintf(&text, " %s", mti->name); if (w - 2 >= strlen(text)) { screen_write_cursormove(&ctx, 1, h, 0); screen_write_puts(&ctx, &gc0, "%s", text); @@ -680,7 +732,8 @@ mode_tree_draw(struct mode_tree_data *mtd) else screen_write_puts(&ctx, &gc0, "active"); screen_write_puts(&ctx, &gc0, ") "); - } + } else + screen_write_puts(&ctx, &gc0, " "); } free(text); @@ -1027,7 +1080,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, break; case 'O': mtd->sort_crit.field++; - if (mtd->sort_crit.field == mtd->sort_size) + if (mtd->sort_crit.field >= mtd->sort_size) mtd->sort_crit.field = 0; mode_tree_build(mtd); break; diff --git a/options-table.c b/options-table.c index 320a380f..55eb5b9f 100644 --- a/options-table.c +++ b/options-table.c @@ -103,9 +103,9 @@ static const char *options_table_window_size_list[] = { "," \ "#[range=window|#{window_index} list=focus " \ "#{?#{!=:#{window-status-current-style},default}," \ - "#{window-status-current-style}," \ - "#{window-status-style}" \ - "}" \ + "#{window-status-current-style}," \ + "#{window-status-style}" \ + "}" \ "#{?#{&&:#{window_last_flag}," \ "#{!=:#{window-status-last-style},default}}, " \ "#{window-status-last-style}," \ @@ -175,6 +175,7 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_KEY, .scope = OPTIONS_TABLE_SERVER, .default_num = '\177', + .text = "The key to send for backspace." }, { .name = "buffer-limit", @@ -182,7 +183,9 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .minimum = 1, .maximum = INT_MAX, - .default_num = 50 + .default_num = 50, + .text = "The maximum number of automatic buffers. " + "When this is reached, the oldest buffer is deleted." }, { .name = "command-alias", @@ -195,25 +198,31 @@ const struct options_table_entry options_table[] = { "info=show-messages -JT," "choose-window=choose-tree -w," "choose-session=choose-tree -s", - .separator = "," + .separator = ",", + .text = "Array of command aliases. " + "Each entry is an alias and a command separated by '='." }, { .name = "copy-command", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "" + .default_str = "", + .text = "Shell command run when text is copied. " + "If empty, no command is run." }, { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "screen" + .default_str = "screen", + .text = "Default for the 'TERM' environment variable." }, { .name = "editor", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = _PATH_VI + .default_str = _PATH_VI, + .text = "Editor run to edit files." }, { .name = "escape-time", @@ -221,31 +230,38 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, - .default_num = 500 + .default_num = 500, + .text = "Time to wait before assuming a key is Escape." }, { .name = "exit-empty", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, - .default_num = 1 + .default_num = 1, + .text = "Whether the server should exit if there are no sessions." }, { .name = "exit-unattached", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, - .default_num = 0 + .default_num = 0, + .text = "Whether the server should exit if there are no attached " + "clients." }, { .name = "focus-events", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, - .default_num = 0 + .default_num = 0, + .text = "Whether to send focus events to applications." }, { .name = "history-file", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "" + .default_str = "", + .text = "Location of the command prompt history file. " + "Empty does not write a history file." }, { .name = "message-limit", @@ -253,14 +269,18 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, - .default_num = 1000 + .default_num = 1000, + .text = "Maximum number of server messages to keep." }, { .name = "set-clipboard", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SERVER, .choices = options_table_set_clipboard_list, - .default_num = 1 + .default_num = 1, + .text = "Whether to attempt to set the system clipboard ('on' or " + "'external') and whether to allow applications to create " + "paste buffers with an escape sequence ('on' only)." }, { .name = "terminal-overrides", @@ -268,7 +288,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "", - .separator = "," + .separator = ",", + .text = "List of terminal capabilities overrides." }, { .name = "terminal-features", @@ -276,8 +297,10 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "xterm*:clipboard:ccolour:cstyle:title," - "screen*:title", - .separator = "," + "screen*:title", + .separator = ",", + .text = "List of terminal features, used if they cannot be " + "automatically detected." }, { .name = "user-keys", @@ -285,7 +308,10 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "", - .separator = "," + .separator = ",", + .text = "User key assignments. " + "Each sequence in the list is translated into a key: " + "'User0', 'User1' and so on." }, /* Session options. */ @@ -293,7 +319,8 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, - .default_num = ALERT_OTHER + .default_num = ALERT_OTHER, + .text = "Action to take on an activity alert." }, { .name = "assume-paste-time", @@ -302,6 +329,9 @@ const struct options_table_entry options_table[] = { .minimum = 0, .maximum = INT_MAX, .default_num = 1, + .unit = "milliseconds", + .text = "Maximum time between input to assume it pasting rather " + "than typing." }, { .name = "base-index", @@ -309,57 +339,69 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, - .default_num = 0 + .default_num = 0, + .text = "Default index of the first window in each session." }, { .name = "bell-action", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, - .default_num = ALERT_ANY + .default_num = ALERT_ANY, + .text = "Action to take on a bell alert." }, { .name = "default-command", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "" + .default_str = "", + .text = "Default command to run in new panes. If empty, a shell is " + "started." }, { .name = "default-shell", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = _PATH_BSHELL + .default_str = _PATH_BSHELL, + .text = "Location of default shell." }, { .name = "default-size", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .pattern = "[0-9]*x[0-9]*", - .default_str = "80x24" + .default_str = "80x24", + .text = "Initial size of new sessions." }, { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, - .default_num = 0 + .default_num = 0, + .text = "Whether to destroy sessions when they have no attached " + "clients." }, { .name = "detach-on-destroy", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, - .default_num = 1 + .default_num = 1, + .text = "Whether to detach when a session is destroyed, or switch " + "the client to another session if any exist." }, { .name = "display-panes-active-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, - .default_num = 1 + .default_num = 1, + .text = "Colour of the active pane for 'display-panes'." }, { .name = "display-panes-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, - .default_num = 4 + .default_num = 4, + .text = "Colour of not active panes for 'display-panes'." }, { .name = "display-panes-time", @@ -367,7 +409,9 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, - .default_num = 1000 + .default_num = 1000, + .unit = "milliseconds", + .text = "Time for which 'display-panes' should show pane numbers." }, { .name = "display-time", @@ -375,7 +419,9 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, - .default_num = 750 + .default_num = 750, + .unit = "milliseconds", + .text = "Time for which status line messages should appear." }, { .name = "history-limit", @@ -383,13 +429,19 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, - .default_num = 2000 + .default_num = 2000, + .unit = "lines", + .text = "Maximum number of lines to keep in the history for each " + "pane. " + "If changed, the new value applies only to new panes." }, { .name = "key-table", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "root" + .default_str = "root", + .text = "Default key table. " + "Key presses are first looked up in this table." }, { .name = "lock-after-time", @@ -397,13 +449,16 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, - .default_num = 0 + .default_num = 0, + .unit = "seconds", + .text = "Time after which a client is locked if not used." }, { .name = "lock-command", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "lock -np" + .default_str = "lock -np", + .text = "Shell command to run to lock a client." }, { .name = "message-command-style", @@ -411,7 +466,9 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=black,fg=yellow", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the command prompt when in command mode, if " + "'mode-keys' is set to 'vi'." }, { .name = "message-style", @@ -419,31 +476,39 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=yellow,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the command prompt." }, { .name = "mouse", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, - .default_num = 0 + .default_num = 0, + .text = "Whether the mouse is recognised and mouse key bindings are " + "executed. " + "Applications inside panes can use the mouse even when 'off'." }, { .name = "prefix", .type = OPTIONS_TABLE_KEY, .scope = OPTIONS_TABLE_SESSION, .default_num = '\002', + .text = "The prefix key." }, { .name = "prefix2", .type = OPTIONS_TABLE_KEY, .scope = OPTIONS_TABLE_SESSION, .default_num = KEYC_NONE, + .text = "A second prefix key." }, { .name = "renumber-windows", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, - .default_num = 0 + .default_num = 0, + .text = "Whether windows are automatically renumbered rather than " + "leaving gaps." }, { .name = "repeat-time", @@ -451,45 +516,56 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, - .default_num = 500 + .default_num = 500, + .unit = "milliseconds", + .text = "Time to wait for a key binding to repeat, if it is bound " + "with the '-r' flag." }, { .name = "set-titles", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, - .default_num = 0 + .default_num = 0, + .text = "Whether to set the terminal title, if supported." }, { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "#S:#I:#W - \"#T\" #{session_alerts}" + .default_str = "#S:#I:#W - \"#T\" #{session_alerts}", + .text = "Format of the terminal title to set." }, { .name = "silence-action", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, - .default_num = ALERT_OTHER + .default_num = ALERT_OTHER, + .text = "Action to take on a silence alert." }, { .name = "status", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_list, - .default_num = 1 + .default_num = 1, + .text = "Number of lines in the status line." }, { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 8, + .text = "Background colour of the status line. This option is " + "deprecated, use 'status-style' instead." }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 8, + .text = "Foreground colour of the status line. This option is " + "deprecated, use 'status-style' instead." }, { .name = "status-format", @@ -497,6 +573,11 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .flags = OPTIONS_TABLE_IS_ARRAY, .default_arr = options_table_status_format_default, + .text = "Formats for the status lines. " + "Each array member is the format for one status line. " + "The default status line is made up of several components " + "which may be configured individually with other option such " + "as 'status-left'." }, { .name = "status-interval", @@ -504,27 +585,32 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, - .default_num = 15 + .default_num = 15, + .unit = "seconds", + .text = "Number of seconds between status line updates." }, { .name = "status-justify", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_justify_list, - .default_num = 0 + .default_num = 0, + .text = "Position of the window list in the status line." }, { .name = "status-keys", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_keys_list, - .default_num = MODEKEY_EMACS + .default_num = MODEKEY_EMACS, + .text = "Key set to use at the command prompt." }, { .name = "status-left", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "[#S] " + .default_str = "[#{session_name}] ", + .text = "Contents of the left side of the status line." }, { .name = "status-left-length", @@ -532,7 +618,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, - .default_num = 10 + .default_num = 10, + .text = "Maximum width of the left side of the status line." }, { .name = "status-left-style", @@ -540,22 +627,26 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the left side of the status line." }, { .name = "status-position", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_position_list, - .default_num = 1 + .default_num = 1, + .text = "Position of the status line." }, { .name = "status-right", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "#{?window_bigger," - "[#{window_offset_x}#,#{window_offset_y}] ,}" - "\"#{=21:pane_title}\" %H:%M %d-%b-%y" + "[#{window_offset_x}#,#{window_offset_y}] ,}" + "\"#{=21:pane_title}\" %H:%M %d-%b-%y", + .text = "Contents of the right side of the status line." + }, { .name = "status-right-length", @@ -563,7 +654,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, - .default_num = 40 + .default_num = 40, + .text = "Maximum width of the right side of the status line." }, { .name = "status-right-style", @@ -571,7 +663,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the right side of the status line." }, { .name = "status-style", @@ -579,7 +672,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=green,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the status line." }, { .name = "update-environment", @@ -587,79 +681,100 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK " - "SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY" + "SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY", + .text = "List of environment variables to update in the session " + "environment when a client is attached." }, { .name = "visual-activity", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_visual_bell_list, - .default_num = VISUAL_OFF + .default_num = VISUAL_OFF, + .text = "How activity alerts should be shown: a message ('on'), " + "a message and a bell ('both') or nothing ('off')." }, { .name = "visual-bell", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_visual_bell_list, - .default_num = VISUAL_OFF + .default_num = VISUAL_OFF, + .text = "How bell alerts should be shown: a message ('on'), " + "a message and a bell ('both') or nothing ('off')." }, { .name = "visual-silence", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_visual_bell_list, - .default_num = VISUAL_OFF + .default_num = VISUAL_OFF, + .text = "How silence alerts should be shown: a message ('on'), " + "a message and a bell ('both') or nothing ('off')." }, { .name = "word-separators", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = " " + .default_str = " ", + .text = "Characters considered to separate words." }, /* Window options. */ { .name = "aggressive-resize", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 0 + .default_num = 0, + .text = "When 'window-size' is 'smallest', whether the maximum size " + "of a window is the smallest attached session where it is " + "the current window ('on') or the smallest session it is " + "linked to ('off')." }, { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_num = 0 + .default_num = 0, + .text = "Whether applications are allowed to use the escape sequence " + "to rename windows." }, { .name = "alternate-screen", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_num = 1 + .default_num = 1, + .text = "Whether applications are allowed to use the alternate " + "screen." }, { .name = "automatic-rename", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 1 + .default_num = 1, + .text = "Whether windows are automatically renamed." }, { .name = "automatic-rename-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}" - "#{?pane_dead,[dead],}" + "#{?pane_dead,[dead],}", + .text = "Format used to automatically rename windows." }, { .name = "clock-mode-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 4 + .default_num = 4, + .text = "Colour of the clock in clock mode." }, { .name = "clock-mode-style", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_clock_mode_style_list, - .default_num = 1 + .default_num = 1, + .text = "Time format of the clock in clock mode." }, { .name = "copy-mode-match-style", @@ -667,7 +782,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "bg=cyan,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of search matches in copy mode." }, { .name = "copy-mode-current-match-style", @@ -675,26 +791,32 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "bg=magenta,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the current search match in copy mode." }, { .name = "main-pane-height", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "24" + .default_str = "24", + .text = "Height of the main pane in the 'main-horizontal' layout. " + "This may be a percentage, for example '10%'." }, { .name = "main-pane-width", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "80" + .default_str = "80", + .text = "Width of the main pane in the 'main-vertical' layout. " + "This may be a percentage, for example '10%'." }, { .name = "mode-keys", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_mode_keys_list, - .default_num = MODEKEY_EMACS + .default_num = MODEKEY_EMACS, + .text = "Key set used in copy mode." }, { .name = "mode-style", @@ -702,19 +824,22 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "bg=yellow,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of indicators and highlighting in modes." }, { .name = "monitor-activity", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 0 + .default_num = 0, + .text = "Whether an alert is triggered by activity." }, { .name = "monitor-bell", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 1 + .default_num = 1, + .text = "Whether an alert is triggered by a bell." }, { .name = "monitor-silence", @@ -722,19 +847,26 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, - .default_num = 0 + .default_num = 0, + .text = "Time after which an alert is triggered by silence. " + "Zero means no alert." + }, { .name = "other-pane-height", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "0" + .default_str = "0", + .text = "Height of the other panes in the 'main-horizontal' layout. " + "This may be a percentage, for example '10%'." }, { .name = "other-pane-width", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "0" + .default_str = "0", + .text = "Height of the other panes in the 'main-vertical' layout. " + "This may be a percentage, for example '10%'." }, { .name = "pane-active-border-style", @@ -742,7 +874,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the active pane border." }, { .name = "pane-base-index", @@ -750,21 +883,24 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = USHRT_MAX, - .default_num = 0 + .default_num = 0, + .text = "Index of the first pane in each window." }, { .name = "pane-border-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] " - "\"#{pane_title}\"" + "\"#{pane_title}\"", + .text = "Format of text in the pane status lines." }, { .name = "pane-border-status", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_pane_status_list, - .default_num = PANE_STATUS_OFF + .default_num = PANE_STATUS_OFF, + .text = "Position of the pane status lines." }, { .name = "pane-border-style", @@ -772,19 +908,23 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the pane status lines." }, { .name = "remain-on-exit", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_num = 0 + .default_num = 0, + .text = "Whether panes should remain ('on') or be automatically " + "killed ('off') when the program inside exits." }, { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 0 + .default_num = 0, + .text = "Whether typing should be sent to all panes simultaneously." }, { .name = "window-active-style", @@ -792,14 +932,20 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Default style of the active pane." }, { .name = "window-size", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_window_size_list, - .default_num = WINDOW_SIZE_LATEST + .default_num = WINDOW_SIZE_LATEST, + .text = "How window size is calculated. " + "'latest' uses the size of the most recently used client, " + "'largest' the largest client, 'smallest' the smallest " + "client and 'manual' a size set by the 'resize-window' " + "command." }, { .name = "window-style", @@ -807,7 +953,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Default style of panes that are not the active pane." }, { .name = "window-status-activity-style", @@ -815,7 +962,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of windows in the status line with an activity alert." }, { .name = "window-status-bell-style", @@ -823,13 +971,15 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of windows in the status line with a bell alert." }, { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "#I:#W#{?window_flags,#{window_flags}, }" + .default_str = "#I:#W#{?window_flags,#{window_flags}, }", + .text = "Format of the current window in the status line." }, { .name = "window-status-current-style", @@ -837,13 +987,16 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the current window in the status line." }, { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "#I:#W#{?window_flags,#{window_flags}, }" + .default_str = "#I:#W#{?window_flags,#{window_flags}, }", + .text = "Format of windows in the status line, except the current " + "window." }, { .name = "window-status-last-style", @@ -851,13 +1004,15 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of the last window in the status line." }, { .name = "window-status-separator", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = " " + .default_str = " ", + .text = "Separator between windows in the status line." }, { .name = "window-status-style", @@ -865,19 +1020,24 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .default_str = "default", .flags = OPTIONS_TABLE_IS_STYLE, - .separator = "," + .separator = ",", + .text = "Style of windows in the status line, except the current and " + "last windows." }, { .name = "wrap-search", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 1 + .default_num = 1, + .text = "Whether searching in copy mode should wrap at the top or " + "bottom." }, { .name = "xterm-keys", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, - .default_num = 1 + .default_num = 1, + .text = "Whether xterm-style function key sequences should be sent." }, /* Hook options. */ diff --git a/options.c b/options.c index 39a0d08f..58b7f7c1 100644 --- a/options.c +++ b/options.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -116,7 +117,7 @@ options_value_free(struct options_entry *o, union options_value *ov) } static char * -options_value_tostring(struct options_entry *o, union options_value *ov, +options_value_to_string(struct options_entry *o, union options_value *ov, int numeric) { char *s; @@ -175,6 +176,12 @@ options_free(struct options *oo) free(oo); } +struct options * +options_get_parent(struct options *oo) +{ + return (oo->parent); +} + void options_set_parent(struct options *oo, struct options *parent) { @@ -262,6 +269,35 @@ options_default(struct options *oo, const struct options_table_entry *oe) return (o); } +char * +options_default_to_string(const struct options_table_entry *oe) +{ + char *s; + + switch (oe->type) { + case OPTIONS_TABLE_STRING: + case OPTIONS_TABLE_COMMAND: + s = xstrdup(oe->default_str); + break; + case OPTIONS_TABLE_NUMBER: + xasprintf(&s, "%lld", oe->default_num); + break; + case OPTIONS_TABLE_KEY: + s = xstrdup(key_string_lookup_key(oe->default_num)); + break; + case OPTIONS_TABLE_COLOUR: + s = xstrdup(colour_tostring(oe->default_num)); + break; + case OPTIONS_TABLE_FLAG: + s = xstrdup(oe->default_num ? "on" : "off"); + break; + case OPTIONS_TABLE_CHOICE: + s = xstrdup(oe->choices[oe->default_num]); + break; + } + return (s); +} + static struct options_entry * options_add(struct options *oo, const char *name) { @@ -299,6 +335,12 @@ options_name(struct options_entry *o) return (o->name); } +struct options * +options_owner(struct options_entry *o) +{ + return (o->owner); +} + const struct options_table_entry * options_table_entry(struct options_entry *o) { @@ -492,19 +534,19 @@ options_array_item_value(struct options_array_item *a) } int -options_isarray(struct options_entry *o) +options_is_array(struct options_entry *o) { return (OPTIONS_IS_ARRAY(o)); } int -options_isstring(struct options_entry *o) +options_is_string(struct options_entry *o) { return (OPTIONS_IS_STRING(o)); } char * -options_tostring(struct options_entry *o, int idx, int numeric) +options_to_string(struct options_entry *o, int idx, int numeric) { struct options_array_item *a; @@ -514,9 +556,9 @@ options_tostring(struct options_entry *o, int idx, int numeric) a = options_array_item(o, idx); if (a == NULL) return (xstrdup("")); - return (options_value_tostring(o, &a->value, numeric)); + return (options_value_to_string(o, &a->value, numeric)); } - return (options_value_tostring(o, &o->value, numeric)); + return (options_value_to_string(o, &o->value, numeric)); } char * @@ -866,3 +908,201 @@ options_string_to_style(struct options *oo, const char *name, } return (&o->style); } + +static int +options_from_string_check(const struct options_table_entry *oe, + const char *value, char **cause) +{ + struct style sy; + + if (oe == NULL) + return (0); + if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) { + xasprintf(cause, "not a suitable shell: %s", value); + return (-1); + } + if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) { + xasprintf(cause, "value is invalid: %s", value); + return (-1); + } + if ((oe->flags & OPTIONS_TABLE_IS_STYLE) && + strstr(value, "#{") == NULL && + style_parse(&sy, &grid_default_cell, value) != 0) { + xasprintf(cause, "invalid style: %s", value); + return (-1); + } + return (0); +} + +static int +options_from_string_flag(struct options *oo, const char *name, + const char *value, char **cause) +{ + int flag; + + if (value == NULL || *value == '\0') + flag = !options_get_number(oo, name); + else if (strcmp(value, "1") == 0 || + strcasecmp(value, "on") == 0 || + strcasecmp(value, "yes") == 0) + flag = 1; + else if (strcmp(value, "0") == 0 || + strcasecmp(value, "off") == 0 || + strcasecmp(value, "no") == 0) + flag = 0; + else { + xasprintf(cause, "bad value: %s", value); + return (-1); + } + options_set_number(oo, name, flag); + return (0); +} + +static int +options_from_string_choice(const struct options_table_entry *oe, + struct options *oo, const char *name, const char *value, char **cause) +{ + const char **cp; + int n, choice = -1; + + if (value == NULL) { + choice = options_get_number(oo, name); + if (choice < 2) + choice = !choice; + } else { + n = 0; + for (cp = oe->choices; *cp != NULL; cp++) { + if (strcmp(*cp, value) == 0) + choice = n; + n++; + } + if (choice == -1) { + xasprintf(cause, "unknown value: %s", value); + return (-1); + } + } + options_set_number(oo, name, choice); + return (0); +} + +int +options_from_string(struct options *oo, const struct options_table_entry *oe, + const char *name, const char *value, int append, char **cause) +{ + enum options_table_type type; + long long number; + const char *errstr, *new; + char *old; + key_code key; + + if (oe != NULL) { + if (value == NULL && + oe->type != OPTIONS_TABLE_FLAG && + oe->type != OPTIONS_TABLE_CHOICE) { + xasprintf(cause, "empty value"); + return (-1); + } + type = oe->type; + } else { + if (*name != '@') { + xasprintf(cause, "bad option name"); + return (-1); + } + type = OPTIONS_TABLE_STRING; + } + + switch (type) { + case OPTIONS_TABLE_STRING: + old = xstrdup(options_get_string(oo, name)); + options_set_string(oo, name, append, "%s", value); + + new = options_get_string(oo, name); + if (options_from_string_check(oe, new, cause) != 0) { + options_set_string(oo, name, 0, "%s", old); + free(old); + return (-1); + } + free(old); + return (0); + case OPTIONS_TABLE_NUMBER: + number = strtonum(value, oe->minimum, oe->maximum, &errstr); + if (errstr != NULL) { + xasprintf(cause, "value is %s: %s", errstr, value); + return (-1); + } + options_set_number(oo, name, number); + return (0); + case OPTIONS_TABLE_KEY: + key = key_string_lookup_string(value); + if (key == KEYC_UNKNOWN) { + xasprintf(cause, "bad key: %s", value); + return (-1); + } + options_set_number(oo, name, key); + return (0); + case OPTIONS_TABLE_COLOUR: + if ((number = colour_fromstring(value)) == -1) { + xasprintf(cause, "bad colour: %s", value); + return (-1); + } + options_set_number(oo, name, number); + return (0); + case OPTIONS_TABLE_FLAG: + return (options_from_string_flag(oo, name, value, cause)); + case OPTIONS_TABLE_CHOICE: + return (options_from_string_choice(oe, oo, name, value, cause)); + case OPTIONS_TABLE_COMMAND: + break; + } + return (-1); +} + +void +options_push_changes(const char *name) +{ + struct client *loop; + struct session *s; + struct window *w; + struct window_pane *wp; + + if (strcmp(name, "automatic-rename") == 0) { + RB_FOREACH(w, windows, &windows) { + if (w->active == NULL) + continue; + if (options_get_number(w->options, "automatic-rename")) + w->active->flags |= PANE_CHANGED; + } + } + if (strcmp(name, "key-table") == 0) { + TAILQ_FOREACH(loop, &clients, entry) + server_client_set_key_table(loop, NULL); + } + if (strcmp(name, "user-keys") == 0) { + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->tty.flags & TTY_OPENED) + tty_keys_build(&loop->tty); + } + } + if (strcmp(name, "status") == 0 || + strcmp(name, "status-interval") == 0) + status_timer_start_all(); + if (strcmp(name, "monitor-silence") == 0) + alerts_reset_all(); + if (strcmp(name, "window-style") == 0 || + strcmp(name, "window-active-style") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) + wp->flags |= PANE_STYLECHANGED; + } + if (strcmp(name, "pane-border-status") == 0) { + RB_FOREACH(w, windows, &windows) + layout_fix_panes(w); + } + RB_FOREACH(s, sessions, &sessions) + status_update_cache(s); + + recalculate_sizes(); + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->session != NULL) + server_redraw_client(loop); + } +} diff --git a/screen-write.c b/screen-write.c index 909b8531..9571cbec 100644 --- a/screen-write.c +++ b/screen-write.c @@ -360,7 +360,97 @@ screen_write_strlen(const char *fmt, ...) return (size); } -/* Write simple string (no UTF-8 or maximum length). */ +/* Write string wrapped over lines. */ +int +screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width, + u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...) +{ + struct screen *s = ctx->s; + va_list ap; + char *tmp; + u_int cy = s->cy, i, end, next, idx = 0, at, left; + struct utf8_data *text; + struct grid_cell gc; + + memcpy(&gc, gcp, sizeof gc); + + va_start(ap, fmt); + xvasprintf(&tmp, fmt, ap); + va_end(ap); + + text = utf8_fromcstr(tmp); + free(tmp); + + left = (cx + width) - s->cx; + for (;;) { + /* Find the end of what can fit on the line. */ + at = 0; + for (end = idx; text[end].size != 0; end++) { + if (text[end].size == 1 && text[end].data[0] == '\n') + break; + if (at + text[end].width > left) + break; + at += text[end].width; + } + + /* + * If we're on a space, that's the end. If not, walk back to + * try and find one. + */ + if (text[end].size == 0) + next = end; + else if (text[end].size == 1 && text[end].data[0] == '\n') + next = end + 1; + else if (text[end].size == 1 && text[end].data[0] == ' ') + next = end + 1; + else { + for (i = end; i > idx; i--) { + if (text[i].size == 1 && text[i].data[0] == ' ') + break; + } + if (i != idx) { + next = i + 1; + end = i; + } else + next = end; + } + + /* Print the line. */ + for (i = idx; i < end; i++) { + utf8_copy(&gc.data, &text[i]); + screen_write_cell(ctx, &gc); + } + + /* If at the bottom, stop. */ + idx = next; + if (s->cy == cy + lines - 1 || text[idx].size == 0) + break; + + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + left = width; + } + + /* + * Fail if on the last line and there is more to come or at the end, or + * if the text was not entirely consumed. + */ + if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) || + text[idx].size != 0) { + free(text); + return (0); + } + free(text); + + /* + * If no more to come, move to the next line. Otherwise, leave on + * the same line (except if at the end). + */ + if (!more || s->cx == cx + width) + screen_write_cursormove(ctx, cx, s->cy + 1, 0); + return (1); +} + +/* Write simple string (no maximum length). */ void screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, const char *fmt, ...) diff --git a/style.c b/style.c index 3c615852..08614f9c 100644 --- a/style.c +++ b/style.c @@ -31,6 +31,7 @@ /* Default style. */ static struct style style_default = { { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 }, + 0, 8, STYLE_ALIGN_DEFAULT, @@ -78,7 +79,11 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->gc.bg = base->bg; sy->gc.attr = base->attr; sy->gc.flags = base->flags; - } else if (strcasecmp(tmp, "push-default") == 0) + } else if (strcasecmp(tmp, "ignore") == 0) + sy->ignore = 1; + else if (strcasecmp(tmp, "noignore") == 0) + sy->ignore = 0; + else if (strcasecmp(tmp, "push-default") == 0) sy->default_type = STYLE_DEFAULT_PUSH; else if (strcasecmp(tmp, "pop-default") == 0) sy->default_type = STYLE_DEFAULT_POP; diff --git a/tmux.1 b/tmux.1 index 852ae80f..45c004ff 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1952,6 +1952,53 @@ includes all sessions in any session groups in the tree rather than only the first. This command works only if at least one client is attached. .It Xo +.Ic customize-mode +.Op Fl NZ +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl t Ar target-pane +.Op Ar template +.Xc +Put a pane into customize mode, where options and key bindings may be browsed +and modified from a list. +Option values in the list are shown for the active pane in the current window. +.Fl Z +zooms the pane. +The following keys may be used in customize mode: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Set pane, window, session or global option value" +.It Li "Up" Ta "Select previous item" +.It Li "Down" Ta "Select next item" +.It Li "+" Ta "Expand selected item" +.It Li "-" Ta "Collapse selected item" +.It Li "M-+" Ta "Expand all items" +.It Li "M--" Ta "Collapse all items" +.It Li "s" Ta "Set option value or key attribute" +.It Li "S" Ta "Set global option value" +.It Li "w" Ta "Set window option value, if option is for pane and window" +.It Li "u" Ta "Unset an option (set to default value if global) or unbind a key" +.It Li "U" Ta "Unset tagged options and unbind tagged keys" +.It Li "C-s" Ta "Search by name" +.It Li "n" Ta "Repeat last search" +.It Li "t" Ta "Toggle if item is tagged" +.It Li "T" Ta "Tag no items" +.It Li "C-t" Ta "Tag all items" +.It Li "f" Ta "Enter a format to filter items" +.It Li "v" Ta "Toggle option information" +.It Li "q" Ta "Exit mode" +.El +.Pp +.Fl f +specifies an initial filter: the filter is a format - if it evaluates to zero, +the item in the list is not shown, otherwise it is shown. +If a filter would lead to an empty list, it is ignored. +.Fl F +specifies the format for each item in the tree. +.Fl N +starts without the option information. +This command works only if at least one client is attached. +.It Xo .Ic display-panes .Op Fl b .Op Fl d Ar duration diff --git a/tmux.h b/tmux.h index 385ba212..1abb6ca6 100644 --- a/tmux.h +++ b/tmux.h @@ -745,6 +745,7 @@ enum style_default_type { /* Style option. */ struct style { struct grid_cell gc; + int ignore; int fill; enum style_align align; @@ -1665,6 +1666,7 @@ RB_HEAD(key_bindings, key_binding); struct key_table { const char *name; struct key_bindings key_bindings; + struct key_bindings default_key_bindings; u_int references; @@ -1719,6 +1721,9 @@ struct options_table_entry { const char *separator; const char *pattern; + + const char *text; + const char *unit; }; /* Common command usages. */ @@ -1896,6 +1901,7 @@ void notify_pane(const char *, struct window_pane *); /* options.c */ struct options *options_create(struct options *); void options_free(struct options *); +struct options *options_get_parent(struct options *); void options_set_parent(struct options *, struct options *); struct options_entry *options_first(struct options *); struct options_entry *options_next(struct options_entry *); @@ -1903,7 +1909,9 @@ struct options_entry *options_empty(struct options *, const struct options_table_entry *); struct options_entry *options_default(struct options *, const struct options_table_entry *); +char *options_default_to_string(const struct options_table_entry *); const char *options_name(struct options_entry *); +struct options *options_owner(struct options_entry *); const struct options_table_entry *options_table_entry(struct options_entry *); struct options_entry *options_get_only(struct options *, const char *); struct options_entry *options_get(struct options *, const char *); @@ -1918,9 +1926,9 @@ struct options_array_item *options_array_first(struct options_entry *); struct options_array_item *options_array_next(struct options_array_item *); u_int options_array_item_index(struct options_array_item *); union options_value *options_array_item_value(struct options_array_item *); -int options_isarray(struct options_entry *); -int options_isstring(struct options_entry *); -char *options_tostring(struct options_entry *, int, int); +int options_is_array(struct options_entry *); +int options_is_string(struct options_entry *); +char *options_to_string(struct options_entry *, int, int); char *options_parse(const char *, int *); struct options_entry *options_parse_get(struct options *, const char *, int *, int); @@ -1940,6 +1948,10 @@ int options_scope_from_flags(struct args *, int, struct cmd_find_state *, struct options **, char **); struct style *options_string_to_style(struct options *, const char *, struct format_tree *); +int options_from_string(struct options *, + const struct options_table_entry *, const char *, + const char *, int, char **); +void options_push_changes(const char *); /* options-table.c */ extern const struct options_table_entry options_table[]; @@ -2231,6 +2243,7 @@ struct key_table *key_bindings_first_table(void); struct key_table *key_bindings_next_table(struct key_table *); void key_bindings_unref_table(struct key_table *); struct key_binding *key_bindings_get(struct key_table *, key_code); +struct key_binding *key_bindings_get_default(struct key_table *, key_code); struct key_binding *key_bindings_first(struct key_table *); struct key_binding *key_bindings_next(struct key_table *, struct key_binding *); void key_bindings_add(const char *, key_code, const char *, int, @@ -2460,6 +2473,8 @@ void screen_write_start_callback(struct screen_write_ctx *, struct screen *, void screen_write_stop(struct screen_write_ctx *); void screen_write_reset(struct screen_write_ctx *); size_t printflike(1, 2) screen_write_strlen(const char *, ...); +int printflike(7, 8) screen_write_text(struct screen_write_ctx *, u_int, u_int, + u_int, int, const struct grid_cell *, const char *, ...); void printflike(3, 4) screen_write_puts(struct screen_write_ctx *, const struct grid_cell *, const char *, ...); void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *, @@ -2678,19 +2693,23 @@ typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *, u_int, u_int); typedef int (*mode_tree_search_cb)(void *, void *, const char *); typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code); +typedef u_int (*mode_tree_height_cb)(void *, u_int); typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code); u_int mode_tree_count_tagged(struct mode_tree_data *); void *mode_tree_get_current(struct mode_tree_data *); +const char *mode_tree_get_current_name(struct mode_tree_data *); void mode_tree_expand_current(struct mode_tree_data *); +void mode_tree_collapse_current(struct mode_tree_data *); void mode_tree_expand(struct mode_tree_data *, uint64_t); int mode_tree_set_current(struct mode_tree_data *, uint64_t); void mode_tree_each_tagged(struct mode_tree_data *, mode_tree_each_cb, struct client *, key_code, int); +void mode_tree_up(struct mode_tree_data *, int); void mode_tree_down(struct mode_tree_data *, int); struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *, mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb, - mode_tree_menu_cb, void *, const struct menu_item *, const char **, - u_int, struct screen **); + mode_tree_menu_cb, mode_tree_height_cb, void *, + const struct menu_item *, const char **, u_int, struct screen **); void mode_tree_zoom(struct mode_tree_data *, struct args *); void mode_tree_build(struct mode_tree_data *); void mode_tree_free(struct mode_tree_data *); @@ -2698,6 +2717,7 @@ void mode_tree_resize(struct mode_tree_data *, u_int, u_int); struct mode_tree_item *mode_tree_add(struct mode_tree_data *, struct mode_tree_item *, void *, uint64_t, const char *, const char *, int); +void mode_tree_draw_as_parent(struct mode_tree_item *); void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *); void mode_tree_draw(struct mode_tree_data *); int mode_tree_key(struct mode_tree_data *, struct client *, key_code *, @@ -2728,6 +2748,9 @@ void window_copy_start_drag(struct client *, struct mouse_event *); char *window_copy_get_word(struct window_pane *, u_int, u_int); char *window_copy_get_line(struct window_pane *, u_int); +/* window-option.c */ +extern const struct window_mode window_customize_mode; + /* names.c */ void check_window_name(struct window *); char *default_window_name(struct window *); diff --git a/window-buffer.c b/window-buffer.c index 74be73a4..6156e6b4 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -298,8 +298,8 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->command = xstrdup(args->argv[0]); data->data = mode_tree_start(wp, args, window_buffer_build, - window_buffer_draw, window_buffer_search, window_buffer_menu, data, - window_buffer_menu_items, window_buffer_sort_list, + window_buffer_draw, window_buffer_search, window_buffer_menu, NULL, + data, window_buffer_menu_items, window_buffer_sort_list, nitems(window_buffer_sort_list), &s); mode_tree_zoom(data->data, args); diff --git a/window-client.c b/window-client.c index cd424dd7..159efa5a 100644 --- a/window-client.c +++ b/window-client.c @@ -271,7 +271,7 @@ window_client_init(struct window_mode_entry *wme, data->command = xstrdup(args->argv[0]); data->data = mode_tree_start(wp, args, window_client_build, - window_client_draw, NULL, window_client_menu, data, + window_client_draw, NULL, window_client_menu, NULL, data, window_client_menu_items, window_client_sort_list, nitems(window_client_sort_list), &s); mode_tree_zoom(data->data, args); diff --git a/window-customize.c b/window-customize.c new file mode 100644 index 00000000..2d82897f --- /dev/null +++ b/window-customize.c @@ -0,0 +1,1430 @@ +/* $OpenBSD$ */ + +/* + * 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 +#include + +#include "tmux.h" + +static struct screen *window_customize_init(struct window_mode_entry *, + struct cmd_find_state *, struct args *); +static void window_customize_free(struct window_mode_entry *); +static void window_customize_resize(struct window_mode_entry *, + u_int, u_int); +static void window_customize_key(struct window_mode_entry *, + struct client *, struct session *, + struct winlink *, key_code, struct mouse_event *); + +#define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \ + "#{?is_option," \ + "#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \ + "#[ignore]" \ + "#{option_value}#{?option_unit, #{option_unit},}" \ + "," \ + "#{key}" \ + "}" + +static const struct menu_item window_customize_menu_items[] = { + { "Select", '\r', NULL }, + { "Expand", KEYC_RIGHT, NULL }, + { "", KEYC_NONE, NULL }, + { "Tag", 't', NULL }, + { "Tag All", '\024', NULL }, + { "Tag None", 'T', NULL }, + { "", KEYC_NONE, NULL }, + { "Cancel", 'q', NULL }, + + { NULL, KEYC_NONE, NULL } +}; + +const struct window_mode window_customize_mode = { + .name = "options-mode", + .default_format = WINDOW_CUSTOMIZE_DEFAULT_FORMAT, + + .init = window_customize_init, + .free = window_customize_free, + .resize = window_customize_resize, + .key = window_customize_key, +}; + +enum window_customize_scope { + WINDOW_CUSTOMIZE_NONE, + WINDOW_CUSTOMIZE_KEY, + WINDOW_CUSTOMIZE_SERVER, + WINDOW_CUSTOMIZE_GLOBAL_SESSION, + WINDOW_CUSTOMIZE_SESSION, + WINDOW_CUSTOMIZE_GLOBAL_WINDOW, + WINDOW_CUSTOMIZE_WINDOW, + WINDOW_CUSTOMIZE_PANE +}; + +struct window_customize_itemdata { + struct window_customize_modedata *data; + enum window_customize_scope scope; + + char *table; + key_code key; + + struct options *oo; + char *name; + int idx; +}; + +struct window_customize_modedata { + struct window_pane *wp; + int dead; + int references; + + struct mode_tree_data *data; + char *format; + int hide_global; + + struct window_customize_itemdata **item_list; + u_int item_size; + + struct cmd_find_state fs; +}; + +static uint64_t +window_customize_get_tag(struct options_entry *o, int idx, + const struct options_table_entry *oe) +{ + uint64_t offset; + + if (oe == NULL) + return ((uint64_t)o); + offset = ((char *)oe - (char *)options_table) / sizeof *options_table; + return ((2ULL << 62)|(offset << 32)|((idx + 1) << 1)|1); +} + +static struct options * +window_customize_get_tree(enum window_customize_scope scope, + struct cmd_find_state *fs) +{ + switch (scope) { + case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_KEY: + return (NULL); + case WINDOW_CUSTOMIZE_SERVER: + return (global_options); + case WINDOW_CUSTOMIZE_GLOBAL_SESSION: + return (global_s_options); + case WINDOW_CUSTOMIZE_SESSION: + return (fs->s->options); + case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: + return (global_w_options); + case WINDOW_CUSTOMIZE_WINDOW: + return (fs->w->options); + case WINDOW_CUSTOMIZE_PANE: + return (fs->wp->options); + } + return (NULL); +} + +static int +window_customize_check_item(struct window_customize_modedata *data, + struct window_customize_itemdata *item, struct cmd_find_state *fsp) +{ + struct cmd_find_state fs; + + if (fsp == NULL) + fsp = &fs; + + if (cmd_find_valid_state(&data->fs)) + cmd_find_copy_state(fsp, &data->fs); + else + cmd_find_from_pane(fsp, data->wp, 0); + return (item->oo == window_customize_get_tree(item->scope, fsp)); +} + +static int +window_customize_get_key(struct window_customize_itemdata *item, + struct key_table **ktp, struct key_binding **bdp) +{ + struct key_table *kt; + struct key_binding *bd; + + kt = key_bindings_get_table(item->table, 0); + if (kt == NULL) + return (0); + bd = key_bindings_get(kt, item->key); + if (bd == NULL) + return (0); + + if (ktp != NULL) + *ktp = kt; + if (bdp != NULL) + *bdp = bd; + return (1); +} + +static char * +window_customize_scope_text(enum window_customize_scope scope, + struct cmd_find_state *fs) +{ + char *s; + u_int idx; + + switch (scope) { + case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_KEY: + case WINDOW_CUSTOMIZE_SERVER: + case WINDOW_CUSTOMIZE_GLOBAL_SESSION: + case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: + s = xstrdup(""); + break; + case WINDOW_CUSTOMIZE_PANE: + window_pane_index(fs->wp, &idx); + xasprintf(&s, "pane %u", idx); + break; + case WINDOW_CUSTOMIZE_SESSION: + xasprintf(&s, "session %s", fs->s->name); + break; + case WINDOW_CUSTOMIZE_WINDOW: + xasprintf(&s, "window %u", fs->wl->idx); + break; + } + return (s); +} + +static struct window_customize_itemdata * +window_customize_add_item(struct window_customize_modedata *data) +{ + struct window_customize_itemdata *item; + + data->item_list = xreallocarray(data->item_list, data->item_size + 1, + sizeof *data->item_list); + item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); + return (item); +} + +static void +window_customize_free_item(struct window_customize_itemdata *item) +{ + free(item->table); + free(item->name); + free(item); +} + +static void +window_customize_build_array(struct window_customize_modedata *data, + struct mode_tree_item *top, enum window_customize_scope scope, + struct options_entry *o, struct format_tree *ft) +{ + const struct options_table_entry *oe = options_table_entry(o); + struct options *oo = options_owner(o); + struct window_customize_itemdata *item; + struct options_array_item *ai; + char *name, *value, *text; + u_int idx; + uint64_t tag; + + ai = options_array_first(o); + while (ai != NULL) { + idx = options_array_item_index(ai); + + xasprintf(&name, "%s[%u]", options_name(o), idx); + format_add(ft, "option_name", "%s", name); + value = options_to_string(o, idx, 0); + format_add(ft, "option_value", "%s", value); + + item = window_customize_add_item(data); + item->scope = scope; + item->oo = oo; + item->name = xstrdup(options_name(o)); + item->idx = idx; + + text = format_expand(ft, data->format); + tag = window_customize_get_tag(o, idx, oe); + mode_tree_add(data->data, top, item, tag, name, text, -1); + free(text); + + free(name); + free(value); + + ai = options_array_next(ai); + } +} + +static void +window_customize_build_option(struct window_customize_modedata *data, + struct mode_tree_item *top, enum window_customize_scope scope, + struct options_entry *o, struct format_tree *ft, + const char *filter, struct cmd_find_state *fs) +{ + const struct options_table_entry *oe = options_table_entry(o); + struct options *oo = options_owner(o); + const char *name = options_name(o); + struct window_customize_itemdata *item; + char *text, *expanded, *value; + int global = 0, array = 0; + uint64_t tag; + + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK)) + return; + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) + array = 1; + + if (scope == WINDOW_CUSTOMIZE_SERVER || + scope == WINDOW_CUSTOMIZE_GLOBAL_SESSION || + scope == WINDOW_CUSTOMIZE_GLOBAL_WINDOW) + global = 1; + if (data->hide_global && global) + return; + + format_add(ft, "option_name", "%s", name); + format_add(ft, "option_is_global", "%d", global); + format_add(ft, "option_is_array", "%d", array); + + text = window_customize_scope_text(scope, fs); + format_add(ft, "option_scope", "%s", text); + free(text); + + if (oe != NULL && oe->unit != NULL) + format_add(ft, "option_unit", "%s", oe->unit); + else + format_add(ft, "option_unit", "%s", ""); + + if (!array) { + value = options_to_string(o, -1, 0); + format_add(ft, "option_value", "%s", value); + free(value); + } + + if (filter != NULL) { + expanded = format_expand(ft, filter); + if (!format_true(expanded)) { + free(expanded); + return; + } + free(expanded); + } + item = window_customize_add_item(data); + item->oo = oo; + item->scope = scope; + item->name = xstrdup(name); + item->idx = -1; + + if (array) + text = NULL; + else + text = format_expand(ft, data->format); + tag = window_customize_get_tag(o, -1, oe); + top = mode_tree_add(data->data, top, item, tag, name, text, 0); + free(text); + + if (array) + window_customize_build_array(data, top, scope, o, ft); +} + +static void +window_customize_find_user_options(struct options *oo, const char ***list, + u_int *size) +{ + struct options_entry *o; + const char *name; + u_int i; + + o = options_first(oo); + while (o != NULL) { + name = options_name(o); + if (*name != '@') { + o = options_next(o); + continue; + } + for (i = 0; i < *size; i++) { + if (strcmp((*list)[i], name) == 0) + break; + } + if (i != *size) { + o = options_next(o); + continue; + } + *list = xreallocarray(*list, (*size) + 1, sizeof **list); + (*list)[(*size)++] = name; + + o = options_next(o); + } +} + +static void +window_customize_build_options(struct window_customize_modedata *data, + const char *title, uint64_t tag, + enum window_customize_scope scope0, struct options *oo0, + enum window_customize_scope scope1, struct options *oo1, + enum window_customize_scope scope2, struct options *oo2, + struct format_tree *ft, const char *filter, struct cmd_find_state *fs) +{ + struct mode_tree_item *top; + struct options_entry *o, *loop; + const char **list = NULL, *name; + u_int size = 0, i; + enum window_customize_scope scope; + + top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0); + + /* + * We get the options from the first tree, but build it using the + * values from the other two. Any tree can have user options so we need + * to build a separate list of them. + */ + + window_customize_find_user_options(oo0, &list, &size); + if (oo1 != NULL) + window_customize_find_user_options(oo1, &list, &size); + if (oo2 != NULL) + window_customize_find_user_options(oo2, &list, &size); + + for (i = 0; i < size; i++) { + if (oo2 != NULL) + o = options_get(oo0, list[i]); + else if (oo1 != NULL) + o = options_get(oo1, list[i]); + else + o = options_get(oo2, list[i]); + if (options_owner(o) == oo2) + scope = scope2; + else if (options_owner(o) == oo1) + scope = scope1; + else + scope = scope0; + window_customize_build_option(data, top, scope, o, ft, filter, + fs); + } + free(list); + + loop = options_first(oo0); + while (loop != NULL) { + name = options_name(loop); + if (*name == '@') { + loop = options_next(loop); + continue; + } + if (oo2 != NULL) + o = options_get(oo2, name); + else if (oo1 != NULL) + o = options_get(oo1, name); + else + o = loop; + if (options_owner(o) == oo2) + scope = scope2; + else if (options_owner(o) == oo1) + scope = scope1; + else + scope = scope0; + window_customize_build_option(data, top, scope, o, ft, filter, + fs); + loop = options_next(loop); + } +} + +static void +window_customize_build_keys(struct window_customize_modedata *data, + struct key_table *kt, struct format_tree *ft, const char *filter, + struct cmd_find_state *fs, u_int number) +{ + struct mode_tree_item *top, *child, *mti; + struct window_customize_itemdata *item; + struct key_binding *bd; + char *title, *text, *tmp, *expanded; + const char *flag; + uint64_t tag; + + tag = (1ULL << 62)|((uint64_t)number << 54)|1; + + xasprintf(&title, "Key Table - %s", kt->name); + top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0); + free(title); + + ft = format_create_from_state(NULL, NULL, fs); + format_add(ft, "is_option", "0"); + format_add(ft, "is_key", "1"); + + bd = key_bindings_first(kt); + while (bd != NULL) { + format_add(ft, "key", "%s", key_string_lookup_key(bd->key)); + if (bd->note != NULL) + format_add(ft, "key_note", "%s", bd->note); + if (filter != NULL) { + expanded = format_expand(ft, filter); + if (!format_true(expanded)) { + free(expanded); + continue; + } + free(expanded); + } + + item = window_customize_add_item(data); + item->scope = WINDOW_CUSTOMIZE_KEY; + item->table = xstrdup(kt->name); + item->key = bd->key; + + expanded = format_expand(ft, data->format); + child = mode_tree_add(data->data, top, item, (uint64_t)bd, + expanded, NULL, 0); + free(expanded); + + tmp = cmd_list_print(bd->cmdlist, 0); + xasprintf(&text, "#[ignore]%s", tmp); + free(tmp); + mti = mode_tree_add(data->data, child, item, + tag|(bd->key << 3)|(0 << 1)|1, "Command", text, -1); + mode_tree_draw_as_parent(mti); + free(text); + + if (bd->note != NULL) + xasprintf(&text, "#[ignore]%s", bd->note); + else + text = xstrdup(""); + mti = mode_tree_add(data->data, child, item, + tag|(bd->key << 3)|(1 << 1)|1, "Note", text, -1); + mode_tree_draw_as_parent(mti); + free(text); + + if (bd->flags & KEY_BINDING_REPEAT) + flag = "on"; + else + flag = "off"; + mti = mode_tree_add(data->data, child, item, + tag|(bd->key << 3)|(2 << 1)|1, "Repeat", flag, -1); + mode_tree_draw_as_parent(mti); + + bd = key_bindings_next(kt, bd); + } + + format_free(ft); +} + +static void +window_customize_build(void *modedata, + __unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag, + const char *filter) +{ + struct window_customize_modedata *data = modedata; + struct cmd_find_state fs; + struct format_tree *ft; + u_int i; + struct key_table *kt; + + for (i = 0; i < data->item_size; i++) + window_customize_free_item(data->item_list[i]); + free(data->item_list); + data->item_list = NULL; + data->item_size = 0; + + if (cmd_find_valid_state(&data->fs)) + cmd_find_copy_state(&fs, &data->fs); + else + cmd_find_from_pane(&fs, data->wp, 0); + + ft = format_create_from_state(NULL, NULL, &fs); + format_add(ft, "is_option", "1"); + format_add(ft, "is_key", "0"); + + window_customize_build_options(data, "Server Options", + (3ULL << 62)|(OPTIONS_TABLE_SERVER << 1)|1, + WINDOW_CUSTOMIZE_SERVER, global_options, + WINDOW_CUSTOMIZE_NONE, NULL, + WINDOW_CUSTOMIZE_NONE, NULL, + ft, filter, &fs); + window_customize_build_options(data, "Session Options", + (3ULL << 62)|(OPTIONS_TABLE_SESSION << 1)|1, + WINDOW_CUSTOMIZE_GLOBAL_SESSION, global_s_options, + WINDOW_CUSTOMIZE_SESSION, fs.s->options, + WINDOW_CUSTOMIZE_NONE, NULL, + ft, filter, &fs); + window_customize_build_options(data, "Window & Pane Options", + (3ULL << 62)|(OPTIONS_TABLE_WINDOW << 1)|1, + WINDOW_CUSTOMIZE_GLOBAL_WINDOW, global_w_options, + WINDOW_CUSTOMIZE_WINDOW, fs.w->options, + WINDOW_CUSTOMIZE_PANE, fs.wp->options, + ft, filter, &fs); + + format_free(ft); + ft = format_create_from_state(NULL, NULL, &fs); + + i = 0; + kt = key_bindings_first_table(); + while (kt != NULL) { + if (!RB_EMPTY(&kt->key_bindings)) { + window_customize_build_keys(data, kt, ft, filter, &fs, + i); + if (++i == 256) + break; + } + kt = key_bindings_next_table(kt); + } + + format_free(ft); +} + +static void +window_customize_draw_key(__unused struct window_customize_modedata *data, + struct window_customize_itemdata *item, struct screen_write_ctx *ctx, + u_int sx, u_int sy) +{ + struct screen *s = ctx->s; + u_int cx = s->cx, cy = s->cy; + struct key_table *kt; + struct key_binding *bd, *default_bd; + const char *note, *period = ""; + char *cmd, *default_cmd; + + if (item == NULL || !window_customize_get_key(item, &kt, &bd)) + return; + + note = bd->note; + if (note == NULL) + note = "There is no note for this key."; + if (*note != '\0' && note[strlen (note) - 1] != '.') + period = "."; + if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s%s", + note, period)) + return; + screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ + if (s->cy >= cy + sy - 1) + return; + + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "This key is in the %s table.", kt->name)) + return; + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "This key %s repeat.", + (bd->flags & KEY_BINDING_REPEAT) ? "does" : "does not")) + return; + screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ + if (s->cy >= cy + sy - 1) + return; + + cmd = cmd_list_print(bd->cmdlist, 0); + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "Command: %s", cmd)) { + free(cmd); + return; + } + default_bd = key_bindings_get_default(kt, bd->key); + if (default_bd != NULL) { + default_cmd = cmd_list_print(default_bd->cmdlist, 0); + if (strcmp(cmd, default_cmd) != 0 && + !screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "The default is: %s", default_cmd)) { + free(default_cmd); + free(cmd); + return; + } + free(default_cmd); + } + free(cmd); +} + +static void +window_customize_draw_option(struct window_customize_modedata *data, + struct window_customize_itemdata *item, struct screen_write_ctx *ctx, + u_int sx, u_int sy) +{ + struct screen *s = ctx->s; + u_int cx = s->cx, cy = s->cy; + int idx; + struct options_entry *o, *parent; + struct options *go, *wo; + const struct options_table_entry *oe; + struct grid_cell gc; + const char **choice, *text, *name; + const char *space = "", *unit = ""; + char *value = NULL, *expanded; + char *default_value = NULL; + char choices[256] = ""; + struct cmd_find_state fs; + struct format_tree *ft; + + if (!window_customize_check_item(data, item, &fs)) + return; + name = item->name; + idx = item->idx; + + o = options_get(item->oo, name); + if (o == NULL) + return; + oe = options_table_entry(o); + + if (oe != NULL && oe->unit != NULL) { + space = " "; + unit = oe->unit; + } + ft = format_create_from_state(NULL, NULL, &fs); + + if (oe == NULL) + text = "This is a user option."; + else if (oe->text == NULL) + text = "This option doesn't have a description."; + else + text = oe->text; + if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s", + text)) + goto out; + screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ + if (s->cy >= cy + sy - 1) + goto out; + + if (oe == NULL) + text = "user"; + else if ((oe->scope & (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) == + (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) + text = "window and pane"; + else if (oe->scope & OPTIONS_TABLE_WINDOW) + text = "window"; + else if (oe->scope & OPTIONS_TABLE_SESSION) + text = "session"; + else + text = "server"; + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "This is a %s option.", text)) + goto out; + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { + if (idx != -1) { + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), + 0, &grid_default_cell, + "This is an array option, index %u.", idx)) + goto out; + } else { + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), + 0, &grid_default_cell, "This is an array option.")) + goto out; + } + if (idx == -1) + goto out; + } + screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ + if (s->cy >= cy + sy - 1) + goto out; + + value = options_to_string(o, idx, 0); + if (oe != NULL && idx == -1) { + default_value = options_default_to_string(oe); + if (strcmp(default_value, value) == 0) { + free(default_value); + default_value = NULL; + } + } + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "Option value: %s%s%s", value, space, unit)) + goto out; + if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) { + expanded = format_expand(ft, value); + if (strcmp(expanded, value) != 0) { + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), + 0, &grid_default_cell, "This expands to: %s", + expanded)) + goto out; + } + free(expanded); + } + if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) { + for (choice = oe->choices; *choice != NULL; choice++) { + strlcat(choices, *choice, sizeof choices); + strlcat(choices, ", ", sizeof choices); + } + choices[strlen(choices) - 2] = '\0'; + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "Available values are: %s", + choices)) + goto out; + } + if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) { + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1, + &grid_default_cell, "This is a colour option: ")) + goto out; + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.fg = options_get_number(item->oo, name); + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc, + "EXAMPLE")) + goto out; + } + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) { + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1, + &grid_default_cell, "This is a style option: ")) + goto out; + style_apply(&gc, item->oo, name, ft); + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc, + "EXAMPLE")) + goto out; + } + if (default_value != NULL) { + if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, + &grid_default_cell, "The default is: %s%s%s", default_value, + space, unit)) + goto out; + } + + screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ + if (s->cy > cy + sy - 1) + goto out; + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { + wo = NULL; + go = NULL; + } else { + switch (item->scope) { + case WINDOW_CUSTOMIZE_PANE: + wo = options_get_parent(item->oo); + go = options_get_parent(wo); + break; + case WINDOW_CUSTOMIZE_WINDOW: + case WINDOW_CUSTOMIZE_SESSION: + wo = NULL; + go = options_get_parent(item->oo); + break; + default: + wo = NULL; + go = NULL; + break; + } + } + if (wo != NULL && options_owner(o) != wo) { + parent = options_get_only(wo, name); + if (parent != NULL) { + value = options_to_string(parent, -1 , 0); + if (!screen_write_text(ctx, s->cx, sx, + sy - (s->cy - cy), 0, &grid_default_cell, + "Window value (from window %u): %s%s%s", fs.wl->idx, + value, space, unit)) + goto out; + } + } + if (go != NULL && options_owner(o) != go) { + parent = options_get_only(go, name); + if (parent != NULL) { + value = options_to_string(parent, -1 , 0); + if (!screen_write_text(ctx, s->cx, sx, + sy - (s->cy - cy), 0, &grid_default_cell, + "Global value: %s%s%s", value, space, unit)) + goto out; + } + } + +out: + free(value); + free(default_value); + format_free(ft); +} + +static void +window_customize_draw(void *modedata, void *itemdata, + struct screen_write_ctx *ctx, u_int sx, u_int sy) +{ + struct window_customize_modedata *data = modedata; + struct window_customize_itemdata *item = itemdata; + + if (item == NULL) + return; + + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_draw_key(data, item, ctx, sx, sy); + else + window_customize_draw_option(data, item, ctx, sx, sy); +} + +static void +window_customize_menu(void *modedata, struct client *c, key_code key) +{ + struct window_customize_modedata *data = modedata; + struct window_pane *wp = data->wp; + struct window_mode_entry *wme; + + wme = TAILQ_FIRST(&wp->modes); + if (wme == NULL || wme->data != modedata) + return; + window_customize_key(wme, c, NULL, NULL, key, NULL); +} + +static u_int +window_customize_height(__unused void *modedata, __unused u_int height) +{ + return (12); +} + +static struct screen * +window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs, + struct args *args) +{ + struct window_pane *wp = wme->wp; + struct window_customize_modedata *data; + struct screen *s; + + wme->data = data = xcalloc(1, sizeof *data); + data->wp = wp; + data->references = 1; + + memcpy(&data->fs, fs, sizeof data->fs); + + if (args == NULL || !args_has(args, 'F')) + data->format = xstrdup(WINDOW_CUSTOMIZE_DEFAULT_FORMAT); + else + data->format = xstrdup(args_get(args, 'F')); + + data->data = mode_tree_start(wp, args, window_customize_build, + window_customize_draw, NULL, window_customize_menu, + window_customize_height, data, window_customize_menu_items, NULL, 0, + &s); + mode_tree_zoom(data->data, args); + + mode_tree_build(data->data); + mode_tree_draw(data->data); + + return (s); +} + +static void +window_customize_destroy(struct window_customize_modedata *data) +{ + u_int i; + + if (--data->references != 0) + return; + + for (i = 0; i < data->item_size; i++) + window_customize_free_item(data->item_list[i]); + free(data->item_list); + + free(data->format); + + free(data); +} + +static void +window_customize_free(struct window_mode_entry *wme) +{ + struct window_customize_modedata *data = wme->data; + + if (data == NULL) + return; + + data->dead = 1; + mode_tree_free(data->data); + window_customize_destroy(data); +} + +static void +window_customize_resize(struct window_mode_entry *wme, u_int sx, u_int sy) +{ + struct window_customize_modedata *data = wme->data; + + mode_tree_resize(data->data, sx, sy); +} + +static void +window_customize_free_callback(void *modedata) +{ + window_customize_destroy(modedata); +} + +static void +window_customize_free_item_callback(void *itemdata) +{ + struct window_customize_itemdata *item = itemdata; + struct window_customize_modedata *data = item->data; + + window_customize_free_item(item); + window_customize_destroy(data); +} + +static int +window_customize_set_option_callback(struct client *c, void *itemdata, + const char *s, __unused int done) +{ + struct window_customize_itemdata *item = itemdata; + struct window_customize_modedata *data = item->data; + struct options_entry *o; + const struct options_table_entry *oe; + struct options *oo = item->oo; + const char *name = item->name; + char *cause; + int idx = item->idx; + + if (s == NULL || *s == '\0' || data->dead) + return (0); + if (item == NULL || !window_customize_check_item(data, item, NULL)) + return (0); + o = options_get(oo, name); + if (o == NULL) + return (0); + oe = options_table_entry(o); + + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { + if (idx == -1) { + for (idx = 0; idx < INT_MAX; idx++) { + if (options_array_get(o, idx) == NULL) + break; + } + } + if (options_array_set(o, idx, s, 0, &cause) != 0) + goto fail; + } else { + if (options_from_string(oo, oe, name, s, 0, &cause) != 0) + goto fail; + } + + options_push_changes(item->name); + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (0); + +fail: + *cause = toupper((u_char)*cause); + status_message_set(c, 1, "%s", cause); + free(cause); + return (0); +} + +static void +window_customize_set_option(struct client *c, + struct window_customize_modedata *data, + struct window_customize_itemdata *item, int global, int pane) +{ + struct options_entry *o; + const struct options_table_entry *oe; + struct options *oo; + struct window_customize_itemdata *new_item; + int flag, idx = item->idx; + enum window_customize_scope scope; + u_int choice; + const char *name = item->name, *space = ""; + char *prompt, *value, *text; + struct cmd_find_state fs; + + if (item == NULL || !window_customize_check_item(data, item, &fs)) + return; + o = options_get(item->oo, name); + if (o == NULL) + return; + + oe = options_table_entry(o); + if (~oe->scope & OPTIONS_TABLE_PANE) + pane = 0; + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { + scope = item->scope; + oo = item->oo; + } else { + if (global) { + switch (item->scope) { + case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_KEY: + case WINDOW_CUSTOMIZE_SERVER: + case WINDOW_CUSTOMIZE_GLOBAL_SESSION: + case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: + scope = item->scope; + break; + case WINDOW_CUSTOMIZE_SESSION: + scope = WINDOW_CUSTOMIZE_GLOBAL_SESSION; + break; + case WINDOW_CUSTOMIZE_WINDOW: + case WINDOW_CUSTOMIZE_PANE: + scope = WINDOW_CUSTOMIZE_GLOBAL_WINDOW; + break; + } + } else { + switch (item->scope) { + case WINDOW_CUSTOMIZE_NONE: + case WINDOW_CUSTOMIZE_KEY: + case WINDOW_CUSTOMIZE_SERVER: + case WINDOW_CUSTOMIZE_SESSION: + scope = item->scope; + break; + case WINDOW_CUSTOMIZE_WINDOW: + case WINDOW_CUSTOMIZE_PANE: + if (pane) + scope = WINDOW_CUSTOMIZE_PANE; + else + scope = WINDOW_CUSTOMIZE_WINDOW; + break; + case WINDOW_CUSTOMIZE_GLOBAL_SESSION: + scope = WINDOW_CUSTOMIZE_SESSION; + break; + case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: + if (pane) + scope = WINDOW_CUSTOMIZE_PANE; + else + scope = WINDOW_CUSTOMIZE_WINDOW; + break; + } + } + if (scope == item->scope) + oo = item->oo; + else + oo = window_customize_get_tree(scope, &fs); + } + + if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) { + flag = options_get_number(oo, name); + options_set_number(oo, name, !flag); + } else if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) { + choice = options_get_number(oo, name); + if (oe->choices[choice + 1] == NULL) + choice = 0; + else + choice++; + options_set_number(oo, name, choice); + } else { + text = window_customize_scope_text(scope, &fs); + if (*text != '\0') + space = ", for "; + else if (scope != WINDOW_CUSTOMIZE_SERVER) + space = ", global"; + if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { + if (idx == -1) { + xasprintf(&prompt, "(%s[+]%s%s) ", name, space, + text); + } else { + xasprintf(&prompt, "(%s[%d]%s%s) ", name, idx, + space, text); + } + } else + xasprintf(&prompt, "(%s%s%s) ", name, space, text); + free(text); + + value = options_to_string(o, idx, 0); + + new_item = xcalloc(1, sizeof *new_item); + new_item->data = data; + new_item->scope = scope; + new_item->oo = oo; + new_item->name = xstrdup(name); + new_item->idx = idx; + + data->references++; + status_prompt_set(c, prompt, value, + window_customize_set_option_callback, + window_customize_free_item_callback, new_item, + PROMPT_NOFORMAT); + + free(prompt); + free(value); + } +} + +static void +window_customize_unset_option(struct window_customize_modedata *data, + struct window_customize_itemdata *item) +{ + struct options_entry *o; + const struct options_table_entry *oe; + + if (item == NULL || !window_customize_check_item(data, item, NULL)) + return; + + o = options_get(item->oo, item->name); + if (o == NULL) + return; + if (item->idx != -1) { + if (item == mode_tree_get_current(data->data)) + mode_tree_up(data->data, 0); + options_array_set(o, item->idx, NULL, 0, NULL); + return; + } + oe = options_table_entry(o); + if (oe != NULL && + options_owner(o) != global_options && + options_owner(o) != global_s_options && + options_owner(o) != global_w_options) + options_remove(o); + else + options_default(options_owner(o), oe); +} + +static int +window_customize_set_command_callback(struct client *c, void *itemdata, + const char *s, __unused int done) +{ + struct window_customize_itemdata *item = itemdata; + struct window_customize_modedata *data = item->data; + struct key_binding *bd; + struct cmd_parse_result *pr; + char *error; + + if (s == NULL || *s == '\0' || data->dead) + return (0); + if (item == NULL || !window_customize_get_key(item, NULL, &bd)) + return (0); + + pr = cmd_parse_from_string(s, NULL); + switch (pr->status) { + case CMD_PARSE_EMPTY: + error = xstrdup("empty command"); + goto fail; + case CMD_PARSE_ERROR: + error = pr->error; + goto fail; + case CMD_PARSE_SUCCESS: + break; + } + cmd_list_free(bd->cmdlist); + bd->cmdlist = pr->cmdlist; + + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (0); + +fail: + *error = toupper((u_char)*error); + status_message_set(c, 1, "%s", error); + free(error); + return (0); +} + +static int +window_customize_set_note_callback(__unused struct client *c, void *itemdata, + const char *s, __unused int done) +{ + struct window_customize_itemdata *item = itemdata; + struct window_customize_modedata *data = item->data; + struct key_binding *bd; + + if (s == NULL || *s == '\0' || data->dead) + return (0); + if (item == NULL || !window_customize_get_key(item, NULL, &bd)) + return (0); + + free((void *)bd->note); + bd->note = xstrdup(s); + + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (0); +} + +static void +window_customize_set_key(struct client *c, + struct window_customize_modedata *data, + struct window_customize_itemdata *item) +{ + key_code key = item->key; + struct key_binding *bd; + const char *s; + char *prompt, *value; + struct window_customize_itemdata *new_item; + + if (item == NULL || !window_customize_get_key(item, NULL, &bd)) + return; + + s = mode_tree_get_current_name(data->data); + if (strcmp(s, "Repeat") == 0) + bd->flags ^= KEY_BINDING_REPEAT; + else if (strcmp(s, "Command") == 0) { + xasprintf(&prompt, "(%s) ", key_string_lookup_key(key)); + value = cmd_list_print(bd->cmdlist, 0); + + new_item = xcalloc(1, sizeof *new_item); + new_item->data = data; + new_item->scope = item->scope; + new_item->table = xstrdup(item->table); + new_item->key = key; + + data->references++; + status_prompt_set(c, prompt, value, + window_customize_set_command_callback, + window_customize_free_item_callback, new_item, + PROMPT_NOFORMAT); + free(prompt); + free(value); + } else if (strcmp(s, "Note") == 0) { + xasprintf(&prompt, "(%s) ", key_string_lookup_key(key)); + + new_item = xcalloc(1, sizeof *new_item); + new_item->data = data; + new_item->scope = item->scope; + new_item->table = xstrdup(item->table); + new_item->key = key; + + data->references++; + status_prompt_set(c, prompt, (bd->note == NULL ? "" : bd->note), + window_customize_set_note_callback, + window_customize_free_item_callback, new_item, + PROMPT_NOFORMAT); + free(prompt); + } +} + +static void +window_customize_unset_key(struct window_customize_modedata *data, + struct window_customize_itemdata *item) +{ + struct key_table *kt; + struct key_binding *bd; + + if (item == NULL || !window_customize_get_key(item, &kt, &bd)) + return; + + if (item == mode_tree_get_current(data->data)) { + mode_tree_collapse_current(data->data); + mode_tree_up(data->data, 0); + } + key_bindings_remove(kt->name, bd->key); +} + +static void +window_customize_unset_each(void *modedata, void *itemdata, + __unused struct client *c, __unused key_code key) +{ + struct window_customize_itemdata *item = itemdata; + + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_unset_key(modedata, item); + else { + window_customize_unset_option(modedata, item); + options_push_changes(item->name); + } +} + +static int +window_customize_unset_current_callback(__unused struct client *c, + void *modedata, const char *s, __unused int done) +{ + struct window_customize_modedata *data = modedata; + struct window_customize_itemdata *item; + + if (s == NULL || *s == '\0' || data->dead) + return (0); + if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') + return (0); + + item = mode_tree_get_current(data->data); + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_unset_key(data, item); + else { + window_customize_unset_option(data, item); + options_push_changes(item->name); + } + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (0); +} + +static int +window_customize_unset_tagged_callback(struct client *c, void *modedata, + const char *s, __unused int done) +{ + struct window_customize_modedata *data = modedata; + + if (s == NULL || *s == '\0' || data->dead) + return (0); + if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') + return (0); + + mode_tree_each_tagged(data->data, window_customize_unset_each, c, + KEYC_NONE, 0); + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (0); +} + +static void +window_customize_key(struct window_mode_entry *wme, struct client *c, + __unused struct session *s, __unused struct winlink *wl, key_code key, + struct mouse_event *m) +{ + struct window_pane *wp = wme->wp; + struct window_customize_modedata *data = wme->data; + struct window_customize_itemdata *item, *new_item; + int finished; + char *prompt; + u_int tagged; + + item = mode_tree_get_current(data->data); + finished = mode_tree_key(data->data, c, &key, m, NULL, NULL); + if (item != (new_item = mode_tree_get_current(data->data))) + item = new_item; + + switch (key) { + case '\r': + case 's': + if (item == NULL) + break; + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_set_key(c, data, item); + else { + window_customize_set_option(c, data, item, 0, 1); + options_push_changes(item->name); + } + mode_tree_build(data->data); + break; + case 'w': + if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY) + break; + window_customize_set_option(c, data, item, 0, 0); + options_push_changes(item->name); + mode_tree_build(data->data); + break; + case 'S': + case 'W': + if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY) + break; + window_customize_set_option(c, data, item, 1, 0); + options_push_changes(item->name); + mode_tree_build(data->data); + break; + case 'u': + if (item == NULL) + break; + if (item->scope == WINDOW_CUSTOMIZE_KEY) { + xasprintf(&prompt, "Unbind key %s? ", + key_string_lookup_key(item->key)); + } else + xasprintf(&prompt, "Unset option %s? ", item->name); + data->references++; + status_prompt_set(c, prompt, "", + window_customize_unset_current_callback, + window_customize_free_callback, data, + PROMPT_SINGLE|PROMPT_NOFORMAT); + free(prompt); + break; + case 'U': + tagged = mode_tree_count_tagged(data->data); + if (tagged == 0) + break; + xasprintf(&prompt, "Unset or unbind %u tagged? ", tagged); + data->references++; + status_prompt_set(c, prompt, "", + window_customize_unset_tagged_callback, + window_customize_free_callback, data, + PROMPT_SINGLE|PROMPT_NOFORMAT); + free(prompt); + break; + case 'H': + data->hide_global = !data->hide_global; + mode_tree_build(data->data); + break; + } + if (finished) + window_pane_reset_mode(wp); + else { + mode_tree_draw(data->data); + wp->flags |= PANE_REDRAW; + } +} diff --git a/window-tree.c b/window-tree.c index 2eff4b8a..5731fff6 100644 --- a/window-tree.c +++ b/window-tree.c @@ -885,7 +885,7 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->squash_groups = !args_has(args, 'G'); data->data = mode_tree_start(wp, args, window_tree_build, - window_tree_draw, window_tree_search, window_tree_menu, data, + window_tree_draw, window_tree_search, window_tree_menu, NULL, data, window_tree_menu_items, window_tree_sort_list, nitems(window_tree_sort_list), &s); mode_tree_zoom(data->data, args); From 592f141deef2583d6dd09f5a53a358671e7312b8 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:03:30 +0000 Subject: [PATCH 0418/1006] Fix next-matching-bracket logic, from Chris Barber. --- window-copy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/window-copy.c b/window-copy.c index f500a65e..4104d2ad 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1365,9 +1365,9 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) px = data->cx; py = screen_hsize(s) + data->cy - data->oy; grid_get_cell(s->grid, px, py, &gc); - if (gc.data.size != 1 || - (gc.flags & GRID_FLAG_PADDING) || - strchr(close, *gc.data.data) == NULL) + if (gc.data.size == 1 && + (~gc.flags & GRID_FLAG_PADDING) && + strchr(close, *gc.data.data) != NULL) window_copy_scroll_to(wme, sx, sy); break; } From 126bacb473f7950a156944caba42fad5f1764287 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:03:57 +0000 Subject: [PATCH 0419/1006] Do not loop forever when search finds an empty match, GitHub issue 2203. --- window-copy.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index 4104d2ad..95b4cc6e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2445,7 +2445,8 @@ window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, len += gd->sx; } - if (regexec(reg, buf, 1, ®match, eflags) == 0) { + if (regexec(reg, buf, 1, ®match, eflags) == 0 && + regmatch.rm_so != regmatch.rm_eo) { foundx = first; foundy = py; window_copy_cstrtocellpos(gd, len, &foundx, &foundy, @@ -2547,6 +2548,8 @@ window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, foundy = py; oldx = first; while (regexec(preg, buf + px, 1, ®match, eflags) == 0) { + if (regmatch.rm_so == regmatch.rm_eo) + break; window_copy_cstrtocellpos(gd, len, &foundx, &foundy, buf + px + regmatch.rm_so); if (foundy > py || foundx >= last) From dceb6a15d04a2b2e050ab41816d0df3fe224b416 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:07:55 +0000 Subject: [PATCH 0420/1006] Add a -D flag to ask tmux not to daemonize, useful both for running a debugger (lldb does not have follow-fork-mode) and for running with a managed supervisor init system. GitHub issue 2190. --- proc.c | 9 +++++++-- server-client.c | 15 +++++++++++++-- server.c | 44 +++++++++++++++++++++++++++----------------- tmux.1 | 13 ++++++++++++- tmux.c | 9 +++++++-- tmux.h | 1 + tty.c | 7 ++++++- 7 files changed, 73 insertions(+), 25 deletions(-) diff --git a/proc.c b/proc.c index 3fc190a2..ce3bc8af 100644 --- a/proc.c +++ b/proc.c @@ -37,6 +37,7 @@ struct tmuxproc { void (*signalcb)(int); + struct event ev_sigint; struct event ev_sighup; struct event ev_sigchld; struct event ev_sigcont; @@ -221,10 +222,13 @@ proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int)) sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_IGN; - sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); + sigaction(SIGTTIN, &sa, NULL); + sigaction(SIGTTOU, &sa, NULL); + signal_set(&tp->ev_sigint, SIGINT, proc_signal_cb, tp); + signal_add(&tp->ev_sigint, NULL); signal_set(&tp->ev_sighup, SIGHUP, proc_signal_cb, tp); signal_add(&tp->ev_sighup, NULL); signal_set(&tp->ev_sigchld, SIGCHLD, proc_signal_cb, tp); @@ -251,10 +255,10 @@ proc_clear_signals(struct tmuxproc *tp, int defaults) sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_DFL; - sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); + signal_del(&tp->ev_sigint); signal_del(&tp->ev_sighup); signal_del(&tp->ev_sigchld); signal_del(&tp->ev_sigcont); @@ -264,6 +268,7 @@ proc_clear_signals(struct tmuxproc *tp, int defaults) signal_del(&tp->ev_sigwinch); if (defaults) { + sigaction(SIGINT, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); sigaction(SIGCONT, &sa, NULL); diff --git a/server-client.c b/server-client.c index 0bd00fc4..f42c9843 100644 --- a/server-client.c +++ b/server-client.c @@ -238,11 +238,22 @@ server_client_create(int fd) int server_client_open(struct client *c, char **cause) { + const char *ttynam = _PATH_TTY; + if (c->flags & CLIENT_CONTROL) return (0); - if (strcmp(c->ttyname, "/dev/tty") == 0) { - *cause = xstrdup("can't use /dev/tty"); + if (strcmp(c->ttyname, ttynam) == 0|| + ((isatty(STDIN_FILENO) && + (ttynam = ttyname(STDIN_FILENO)) != NULL && + strcmp(c->ttyname, ttynam) == 0) || + (isatty(STDOUT_FILENO) && + (ttynam = ttyname(STDOUT_FILENO)) != NULL && + strcmp(c->ttyname, ttynam) == 0) || + (isatty(STDERR_FILENO) && + (ttynam = ttyname(STDERR_FILENO)) != NULL && + strcmp(c->ttyname, ttynam) == 0))) { + xasprintf(cause, "can't use %s", c->ttyname); return (-1); } diff --git a/server.c b/server.c index a4216b87..f07479ae 100644 --- a/server.c +++ b/server.c @@ -161,29 +161,35 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, struct client *c; char *cause = NULL; - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) - fatal("socketpair failed"); - server_client_flags = flags; - sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); - switch (fork()) { - case -1: - fatal("fork failed"); - case 0: - break; - default: - sigprocmask(SIG_SETMASK, &oldset, NULL); - close(pair[1]); - return (pair[0]); + + if (~flags & CLIENT_NOFORK) { + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) + fatal("socketpair failed"); + + switch (fork()) { + case -1: + fatal("fork failed"); + case 0: + break; + default: + sigprocmask(SIG_SETMASK, &oldset, NULL); + close(pair[1]); + return (pair[0]); + } + close(pair[0]); + if (daemon(1, 0) != 0) + fatal("daemon failed"); } - close(pair[0]); - if (daemon(1, 0) != 0) - fatal("daemon failed"); + + server_client_flags = flags; proc_clear_signals(client, 0); + if (event_reinit(base) != 0) fatalx("event_reinit failed"); server_proc = proc_start("server"); + proc_set_signals(server_proc, server_signal); sigprocmask(SIG_SETMASK, &oldset, NULL); @@ -205,7 +211,10 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, server_fd = server_create_socket(flags, &cause); if (server_fd != -1) server_update_socket(); - c = server_client_create(pair[1]); + if (~flags & CLIENT_NOFORK) + c = server_client_create(pair[1]); + else + options_set_number(global_options, "exit-empty", 0); if (lockfd >= 0) { unlink(lockfile); @@ -396,6 +405,7 @@ server_signal(int sig) log_debug("%s: %s", __func__, strsignal(sig)); switch (sig) { + case SIGINT: case SIGTERM: server_exit = 1; server_send_exit(); diff --git a/tmux.1 b/tmux.1 index 45c004ff..6c19283e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 2CluvV +.Op Fl 2CDluvV .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name @@ -122,6 +122,17 @@ This option is for compatibility with when .Nm is used as a login shell. +.It Fl D +Do not start the +.Nm +server as a daemon. +This also turns the +.Ic exit-empty +option off. +With +.Fl D , +.Ar command +may not be specified. .It Fl f Ar file Specify an alternative configuration file. By default, diff --git a/tmux.c b/tmux.c index 137c7eeb..cfc84cd0 100644 --- a/tmux.c +++ b/tmux.c @@ -57,7 +57,7 @@ static __dead void usage(void) { fprintf(stderr, - "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2CDluvV] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [-T features] [command [flags]]\n", getprogname()); exit(1); @@ -336,7 +336,7 @@ main(int argc, char **argv) if (**argv == '-') flags = CLIENT_LOGIN; - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:T:uUvV")) != -1) { + while ((opt = getopt(argc, argv, "2c:CDdf:lL:qS:T:uUvV")) != -1) { switch (opt) { case '2': tty_add_features(&feat, "256", ":,"); @@ -344,6 +344,9 @@ main(int argc, char **argv) case 'c': shell_command = optarg; break; + case 'D': + flags |= CLIENT_NOFORK; + break; case 'C': if (flags & CLIENT_CONTROL) flags |= CLIENT_CONTROLCONTROL; @@ -387,6 +390,8 @@ main(int argc, char **argv) if (shell_command != NULL && argc != 0) usage(); + if ((flags & CLIENT_NOFORK) && argc != 0) + usage(); if ((ptm_fd = getptmfd()) == -1) err(1, "getptmfd"); diff --git a/tmux.h b/tmux.h index 1abb6ca6..8ab78d7a 100644 --- a/tmux.h +++ b/tmux.h @@ -1584,6 +1584,7 @@ struct client { #define CLIENT_DEFAULTSOCKET 0x8000000 #define CLIENT_STARTSERVER 0x10000000 #define CLIENT_REDRAWPANES 0x20000000 +#define CLIENT_NOFORK 0x40000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ diff --git a/tty.c b/tty.c index c8efeac7..a1e2f2c6 100644 --- a/tty.c +++ b/tty.c @@ -154,16 +154,21 @@ tty_read_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; struct client *c = tty->client; + const char *name = c->name; size_t size = EVBUFFER_LENGTH(tty->in); int nread; nread = evbuffer_read(tty->in, tty->fd, -1); if (nread == 0 || nread == -1) { + if (nread == 0) + log_debug("%s: read closed", name); + else + log_debug("%s: read error: %s", name, strerror(errno)); event_del(&tty->event_in); server_client_lost(tty->client); return; } - log_debug("%s: read %d bytes (already %zu)", c->name, nread, size); + log_debug("%s: read %d bytes (already %zu)", name, nread, size); while (tty_keys_next(tty)) ; From ff8dd150e0c59b37d9a4942e499a2a025820db8d Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:10:28 +0000 Subject: [PATCH 0421/1006] Add a mark in copy mode. Set with set-mark command (bound to 'X') by default and the mark and cursor position are swapped with 'jump-to-mark' (bound to M-x). The line containing the mark is shown in copy-mode-mark-style with the horizontal position in reverse. From Anindya Mukherjee in GitHub issue 2209. --- key-bindings.c | 4 ++ options-table.c | 9 ++++ tmux.1 | 10 +++++ window-copy.c | 107 ++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 121 insertions(+), 9 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index ecd2f7dc..05089bab 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -426,6 +426,7 @@ key_bindings_init(void) "bind -Tcopy-mode N send -X search-reverse", "bind -Tcopy-mode R send -X rectangle-toggle", "bind -Tcopy-mode T command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"'", + "bind -Tcopy-mode X send -X set-mark", "bind -Tcopy-mode f command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"'", "bind -Tcopy-mode g command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode n send -X search-again", @@ -467,6 +468,7 @@ key_bindings_init(void) "bind -Tcopy-mode M-r send -X middle-line", "bind -Tcopy-mode M-v send -X page-up", "bind -Tcopy-mode M-w send -X copy-pipe-and-cancel", + "bind -Tcopy-mode M-x send -X jump-to-mark", "bind -Tcopy-mode 'M-{' send -X previous-paragraph", "bind -Tcopy-mode 'M-}' send -X next-paragraph", "bind -Tcopy-mode M-Up send -X halfpage-up", @@ -521,6 +523,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi T command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"'", "bind -Tcopy-mode-vi V send -X select-line", "bind -Tcopy-mode-vi W send -X next-space", + "bind -Tcopy-mode-vi X send -X set-mark", "bind -Tcopy-mode-vi ^ send -X back-to-indentation", "bind -Tcopy-mode-vi b send -X previous-word", "bind -Tcopy-mode-vi e send -X next-word-end", @@ -554,6 +557,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi Down send -X cursor-down", "bind -Tcopy-mode-vi Left send -X cursor-left", "bind -Tcopy-mode-vi Right send -X cursor-right", + "bind -Tcopy-mode-vi M-x send -X jump-to-mark", "bind -Tcopy-mode-vi C-Up send -X scroll-up", "bind -Tcopy-mode-vi C-Down send -X scroll-down", }; diff --git a/options-table.c b/options-table.c index 55eb5b9f..b1c4ec1b 100644 --- a/options-table.c +++ b/options-table.c @@ -795,6 +795,15 @@ const struct options_table_entry options_table[] = { .text = "Style of the current search match in copy mode." }, + { .name = "copy-mode-mark-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "bg=red,fg=black", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = ",", + .text = "Style of the marked line in copy mode." + }, + { .name = "main-pane-height", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, diff --git a/tmux.1 b/tmux.1 index 6c19283e..baa296fb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1555,6 +1555,7 @@ The following commands are supported in copy mode: .It Li "jump-reverse" Ta "," Ta "," .It Li "jump-to-backward " Ta "T" Ta "" .It Li "jump-to-forward " Ta "t" Ta "" +.It Li "jump-to-mark" Ta "M-x" Ta "M-x" .It Li "middle-line" Ta "M" Ta "M-r" .It Li "next-matching-bracket" Ta "%" Ta "M-C-f" .It Li "next-paragraph" Ta "}" Ta "M-}" @@ -1585,6 +1586,7 @@ The following commands are supported in copy mode: .It Li "search-reverse" Ta "N" Ta "N" .It Li "select-line" Ta "V" Ta "" .It Li "select-word" Ta "" Ta "" +.It Li "set-mark" Ta "X" Ta "X" .It Li "start-of-line" Ta "0" Ta "C-a" .It Li "stop-selection" Ta "" Ta "" .It Li "top-line" Ta "H" Ta "M-R" @@ -3817,6 +3819,14 @@ see the .Sx STYLES section. .Pp +.It Ic copy-mode-mark-style Ar style +Set the style of the line containing the mark in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp .It Ic copy-mode-current-match-style Ar style Set the style of the current search match in copy mode. For how to specify diff --git a/window-copy.c b/window-copy.c index 95b4cc6e..e572eaa8 100644 --- a/window-copy.c +++ b/window-copy.c @@ -131,6 +131,7 @@ static void window_copy_rectangle_toggle(struct window_mode_entry *); static void window_copy_move_mouse(struct mouse_event *); static void window_copy_drag_update(struct client *, struct mouse_event *); static void window_copy_drag_release(struct client *, struct mouse_event *); +static void window_copy_jump_to_mark(struct window_mode_entry *); const struct window_mode window_copy_mode = { .name = "copy-mode", @@ -254,6 +255,10 @@ struct window_copy_mode_data { u_int lastcx; /* position in last line w/ content */ u_int lastsx; /* size of last line w/ content */ + u_int mx; /* mark position */ + u_int my; + int showmark; + int searchtype; int searchregex; char *searchstr; @@ -424,6 +429,9 @@ window_copy_init(struct window_mode_entry *wme, data->screen.cx = data->cx; data->screen.cy = data->cy; + data->mx = data->cx; + data->my = screen_hsize(data->backing) + data->cy - data->oy; + data->showmark = 0; screen_write_start(&ctx, &data->screen); for (i = 0; i < screen_size_y(&data->screen); i++) @@ -448,6 +456,9 @@ window_copy_view_init(struct window_mode_entry *wme, data->backing = s = xmalloc(sizeof *data->backing); screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX); + data->mx = data->cx; + data->my = screen_hsize(data->backing) + data->cy - data->oy; + data->showmark = 0; return (&data->screen); } @@ -1733,6 +1744,17 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_REDRAW); } +static enum window_copy_cmd_action +window_copy_cmd_set_mark(struct window_copy_cmd_state *cs) +{ + struct window_copy_mode_data *data = cs->wme->data; + + data->mx = data->cx; + data->my = screen_hsize(data->backing) + data->cy - data->oy; + data->showmark = 1; + return (WINDOW_COPY_CMD_REDRAW); +} + static enum window_copy_cmd_action window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs) { @@ -1877,6 +1899,15 @@ window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_jump_to_mark(wme); + return (WINDOW_COPY_CMD_NOTHING); +} + static enum window_copy_cmd_action window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) { @@ -2154,6 +2185,8 @@ static const struct { window_copy_cmd_jump_to_backward }, { "jump-to-forward", 1, 1, 1, window_copy_cmd_jump_to_forward }, + { "jump-to-mark", 0, 0, 0, + window_copy_cmd_jump_to_mark }, { "middle-line", 0, 0, 1, window_copy_cmd_middle_line }, { "next-matching-bracket", 0, 0, 0, @@ -2214,6 +2247,8 @@ static const struct { window_copy_cmd_select_line }, { "select-word", 0, 0, 0, window_copy_cmd_select_word }, + { "set-mark", 0, 0, 0, + window_copy_cmd_set_mark }, { "start-of-line", 0, 0, 1, window_copy_cmd_start_of_line }, { "stop-selection", 0, 0, 0, @@ -3129,11 +3164,26 @@ window_copy_match_at_cursor(struct window_copy_mode_data *data) static void window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, struct grid_cell *gc, const struct grid_cell *mgc, - const struct grid_cell *cgc) + const struct grid_cell *cgc, const struct grid_cell *mkgc) { struct window_copy_mode_data *data = wme->data; u_int mark, start, end, cy, cursor, current; u_int sx = screen_size_x(data->backing); + int inv = 0; + + if (data->showmark && fy == data->my) { + gc->attr = mkgc->attr; + if (fx == data->mx) + inv = 1; + if (inv) { + gc->fg = mkgc->bg; + gc->bg = mkgc->fg; + } + else { + gc->fg = mkgc->fg; + gc->bg = mkgc->bg; + } + } if (data->searchmark == NULL) return; @@ -3150,21 +3200,34 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, window_copy_match_start_end(data, cursor, &start, &end); if (current >= start && current <= end) { gc->attr = cgc->attr; - gc->fg = cgc->fg; - gc->bg = cgc->bg; + if (inv) { + gc->fg = cgc->bg; + gc->bg = cgc->fg; + } + else { + gc->fg = cgc->fg; + gc->bg = cgc->bg; + } return; } } gc->attr = mgc->attr; - gc->fg = mgc->fg; - gc->bg = mgc->bg; + if (inv) { + gc->fg = mgc->bg; + gc->bg = mgc->fg; + } + else { + gc->fg = mgc->fg; + gc->bg = mgc->bg; + } } static void window_copy_write_one(struct window_mode_entry *wme, struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx, - const struct grid_cell *mgc, const struct grid_cell *cgc) + const struct grid_cell *mgc, const struct grid_cell *cgc, + const struct grid_cell *mkgc) { struct window_copy_mode_data *data = wme->data; struct grid *gd = data->backing->grid; @@ -3175,7 +3238,8 @@ window_copy_write_one(struct window_mode_entry *wme, for (fx = 0; fx < nx; fx++) { grid_get_cell(gd, fx, fy, &gc); if (fx + gc.data.width <= nx) { - window_copy_update_style(wme, fx, fy, &gc, mgc, cgc); + window_copy_update_style(wme, fx, fy, &gc, mgc, cgc, + mkgc); screen_write_cell(ctx, &gc); } } @@ -3189,7 +3253,7 @@ window_copy_write_line(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct options *oo = wp->window->options; - struct grid_cell gc, mgc, cgc; + struct grid_cell gc, mgc, cgc, mkgc; char hdr[512]; size_t size = 0; u_int hsize = screen_hsize(data->backing); @@ -3200,6 +3264,8 @@ window_copy_write_line(struct window_mode_entry *wme, mgc.flags |= GRID_FLAG_NOPALETTE; style_apply(&cgc, oo, "copy-mode-current-match-style", NULL); cgc.flags |= GRID_FLAG_NOPALETTE; + style_apply(&mkgc, oo, "copy-mode-mark-style", NULL); + mkgc.flags |= GRID_FLAG_NOPALETTE; if (py == 0 && s->rupper < s->rlower && !data->hide_position) { if (data->searchmark == NULL) { @@ -3233,7 +3299,7 @@ window_copy_write_line(struct window_mode_entry *wme, if (size < screen_size_x(s)) { window_copy_write_one(wme, ctx, py, hsize - data->oy + py, - screen_size_x(s) - size, &mgc, &cgc); + screen_size_x(s) - size, &mgc, &cgc, &mkgc); } if (py == data->cy && data->cx == screen_size_x(s)) { @@ -4687,3 +4753,26 @@ window_copy_drag_release(struct client *c, struct mouse_event *m) data = wme->data; evtimer_del(&data->dragtimer); } + +static void +window_copy_jump_to_mark(struct window_mode_entry *wme) +{ + struct window_copy_mode_data *data = wme->data; + u_int tmx, tmy; + + tmx = data->cx; + tmy = screen_hsize(data->backing) + data->cy - data->oy; + data->cx = data->mx; + if (data->my < screen_hsize(data->backing)) { + data->cy = 0; + data->oy = screen_hsize(data->backing) - data->my; + } else { + data->cy = data->my - screen_hsize(data->backing); + data->oy = 0; + } + data->mx = tmx; + data->my = tmy; + data->showmark = 1; + window_copy_update_selection(wme, 0, 0); + window_copy_redraw_screen(wme); +} From 72984c48347ddfd1d435f8a9ffcdf334c4b28389 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:13:09 +0000 Subject: [PATCH 0422/1006] Move editor stuff to common code in popup.c. --- popup.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ tmux.h | 3 ++ window-buffer.c | 71 +++++------------------------------ 3 files changed, 111 insertions(+), 61 deletions(-) diff --git a/popup.c b/popup.c index 4b47df2b..93ecd2a1 100644 --- a/popup.c +++ b/popup.c @@ -19,9 +19,11 @@ #include #include +#include #include #include #include +#include #include "tmux.h" @@ -57,6 +59,12 @@ struct popup_data { u_int lb; }; +struct popup_editor { + char *path; + popup_finish_edit_cb cb; + void *arg; +}; + static void popup_redraw_cb(const struct tty_ctx *ttyctx) { @@ -519,3 +527,93 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, popup_draw_cb, popup_key_cb, popup_free_cb, pd); return (0); } + +static void +popup_editor_free(struct popup_editor *pe) +{ + unlink(pe->path); + free(pe->path); + free(pe); +} + +static void +popup_editor_close_cb(int status, void *arg) +{ + struct popup_editor *pe = arg; + FILE *f; + char *buf = NULL; + off_t len = 0; + + if (status != 0) { + pe->cb(NULL, 0, pe->arg); + popup_editor_free(pe); + return; + } + + f = fopen(pe->path, "r"); + if (f != NULL) { + fseeko(f, 0, SEEK_END); + len = ftello(f); + fseeko(f, 0, SEEK_SET); + + if (len == 0 || + (uintmax_t)len > (uintmax_t)SIZE_MAX || + (buf = malloc(len)) == NULL || + fread(buf, len, 1, f) != 1) { + free(buf); + buf = NULL; + len = 0; + } + fclose(f); + } + pe->cb(buf, len, pe->arg); /* callback now owns buffer */ + popup_editor_free(pe); +} + +int +popup_editor(struct client *c, const char *buf, size_t len, + popup_finish_edit_cb cb, void *arg) +{ + struct popup_editor *pe; + int fd; + FILE *f; + char *cmd; + char path[] = _PATH_TMP "tmux.XXXXXXXX"; + const char *editor; + u_int px, py, sx, sy; + + editor = options_get_string(global_options, "editor"); + if (*editor == '\0') + return (-1); + + fd = mkstemp(path); + if (fd == -1) + return (-1); + f = fdopen(fd, "w"); + if (fwrite(buf, len, 1, f) != 1) { + fclose(f); + return (-1); + } + fclose(f); + + pe = xcalloc(1, sizeof *pe); + pe->path = xstrdup(path); + pe->cb = cb; + pe->arg = arg; + + sx = c->tty.sx * 9 / 10; + sy = c->tty.sy * 9 / 10; + px = (c->tty.sx / 2) - (sx / 2); + py = (c->tty.sy / 2) - (sy / 2); + + xasprintf(&cmd, "%s %s", editor, path); + if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, + 0, NULL, cmd, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb, + pe) != 0) { + popup_editor_free(pe); + free(cmd); + return (-1); + } + free(cmd); + return (0); +} diff --git a/tmux.h b/tmux.h index 8ab78d7a..c2b8af40 100644 --- a/tmux.h +++ b/tmux.h @@ -2869,6 +2869,7 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int, #define POPUP_CLOSEEXIT 0x2 #define POPUP_CLOSEEXITZERO 0x4 typedef void (*popup_close_cb)(int, void *); +typedef void (*popup_finish_edit_cb)(char *, size_t, void *); u_int popup_width(struct cmdq_item *, u_int, const char **, struct client *, struct cmd_find_state *); u_int popup_height(u_int, const char **); @@ -2876,6 +2877,8 @@ int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, u_int, u_int, const char **, const char *, const char *, const char *, struct client *, struct cmd_find_state *, popup_close_cb, void *); +int popup_editor(struct client *, const char *, size_t, + popup_finish_edit_cb, void *); /* style.c */ int style_parse(struct style *,const struct grid_cell *, diff --git a/window-buffer.c b/window-buffer.c index 6156e6b4..ae6f13ce 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -18,8 +18,6 @@ #include -#include -#include #include #include #include @@ -100,7 +98,6 @@ struct window_buffer_modedata { struct window_buffer_editdata { u_int wp_id; - char *path; char *name; struct paste_buffer *pb; }; @@ -366,19 +363,14 @@ window_buffer_do_paste(void *modedata, void *itemdata, struct client *c, static void window_buffer_finish_edit(struct window_buffer_editdata *ed) { - unlink(ed->path); - free(ed->path); free(ed->name); free(ed); } static void -window_buffer_edit_close_cb(int status, void *arg) +window_buffer_edit_close_cb(char *buf, size_t len, void *arg) { struct window_buffer_editdata *ed = arg; - FILE *f; - off_t len; - char *buf; size_t oldlen; const char *oldbuf; struct paste_buffer *pb; @@ -386,7 +378,7 @@ window_buffer_edit_close_cb(int status, void *arg) struct window_buffer_modedata *data; struct window_mode_entry *wme; - if (status != 0) { + if (buf == NULL || len == 0) { window_buffer_finish_edit(ed); return; } @@ -397,26 +389,13 @@ window_buffer_edit_close_cb(int status, void *arg) return; } - f = fopen(ed->path, "r"); - if (f != NULL) { - fseeko(f, 0, SEEK_END); - len = ftello(f); - fseeko(f, 0, SEEK_SET); - - if (len > 0 && - (uintmax_t)len <= (uintmax_t)SIZE_MAX && - (buf = malloc(len)) != NULL && - fread(buf, len, 1, f) == 1) { - oldbuf = paste_buffer_data(pb, &oldlen); - if (oldlen != '\0' && - oldbuf[oldlen - 1] != '\n' && - buf[len - 1] == '\n') - len--; - if (len != 0) - paste_replace(pb, buf, len); - } - fclose(f); - } + oldbuf = paste_buffer_data(pb, &oldlen); + if (oldlen != '\0' && + oldbuf[oldlen - 1] != '\n' && + buf[len - 1] == '\n') + len--; + if (len != 0) + paste_replace(pb, buf, len); wp = window_pane_find_by_id(ed->wp_id); if (wp != NULL) { @@ -436,51 +415,21 @@ window_buffer_start_edit(struct window_buffer_modedata *data, struct window_buffer_itemdata *item, struct client *c) { struct paste_buffer *pb; - int fd; - FILE *f; const char *buf; size_t len; struct window_buffer_editdata *ed; - char *cmd; - char path[] = _PATH_TMP "tmux.XXXXXXXX"; - const char *editor; - u_int px, py, sx, sy; if ((pb = paste_get_name(item->name)) == NULL) return; buf = paste_buffer_data(pb, &len); - editor = options_get_string(global_options, "editor"); - if (*editor == '\0') - return; - - fd = mkstemp(path); - if (fd == -1) - return; - f = fdopen(fd, "w"); - if (fwrite(buf, len, 1, f) != 1) { - fclose(f); - return; - } - fclose(f); - ed = xcalloc(1, sizeof *ed); ed->wp_id = data->wp->id; - ed->path = xstrdup(path); ed->name = xstrdup(paste_buffer_name(pb)); ed->pb = pb; - sx = c->tty.sx * 9 / 10; - sy = c->tty.sy * 9 / 10; - px = (c->tty.sx / 2) - (sx / 2); - py = (c->tty.sy / 2) - (sy / 2); - - xasprintf(&cmd, "%s %s", editor, path); - if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, - 0, NULL, cmd, NULL, _PATH_TMP, c, NULL, window_buffer_edit_close_cb, - ed) != 0) + if (popup_editor(c, buf, len, window_buffer_edit_close_cb, ed) != 0) window_buffer_finish_edit(ed); - free(cmd); } static void From c914abfa19938fe0e41941879649b7a40e192082 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:16:07 +0000 Subject: [PATCH 0423/1006] Expand target from client and use it to expand the prompt. --- cmd-command-prompt.c | 6 ++++-- cmd-confirm-before.c | 6 ++++-- cmd-queue.c | 3 +-- mode-tree.c | 4 ++-- server-client.c | 7 +++---- status.c | 11 +++++++---- tmux.h | 7 ++++--- window-customize.c | 11 ++++++----- window-tree.c | 9 +++++---- 9 files changed, 36 insertions(+), 28 deletions(-) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index b8e3bd5c..c82235c5 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -66,6 +66,7 @@ cmd_command_prompt_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_find_state *target = cmdq_get_target(item); const char *inputs, *prompts; struct cmd_command_prompt_cdata *cdata; char *prompt, *ptr, *input = NULL; @@ -125,8 +126,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_WINDOW; else if (args_has(args, 'T')) cdata->flags |= PROMPT_TARGET; - status_prompt_set(tc, prompt, input, cmd_command_prompt_callback, - cmd_command_prompt_free, cdata, cdata->flags); + status_prompt_set(tc, target, prompt, input, + cmd_command_prompt_callback, cmd_command_prompt_free, cdata, + cdata->flags); free(prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 0d881178..0c562836 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -56,6 +56,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_confirm_before_data *cdata; struct client *tc = cmdq_get_target_client(item); + struct cmd_find_state *target = cmdq_get_target(item); char *cmd, *copy, *new_prompt, *ptr; const char *prompt; @@ -71,8 +72,9 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) cdata = xmalloc(sizeof *cdata); cdata->cmd = xstrdup(args->argv[0]); - status_prompt_set(tc, new_prompt, NULL, cmd_confirm_before_callback, - cmd_confirm_before_free, cdata, PROMPT_SINGLE); + status_prompt_set(tc, target, new_prompt, NULL, + cmd_confirm_before_callback, cmd_confirm_before_free, cdata, + PROMPT_SINGLE); free(new_prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-queue.c b/cmd-queue.c index a40053a6..6bc6d0d2 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -522,7 +522,7 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs, const char *value; if (flag->flag == 0) { - cmd_find_clear_state(fs, 0); + cmd_find_from_client(fs, item->target_client, 0); return (CMD_RETURN_NORMAL); } @@ -610,7 +610,6 @@ cmdq_fire_command(struct cmdq_item *item) if (retval == CMD_RETURN_ERROR) goto out; - retval = entry->exec(cmd, item); if (retval == CMD_RETURN_ERROR) goto out; diff --git a/mode-tree.c b/mode-tree.c index 131830d6..a523478e 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1125,7 +1125,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, case '/': case '\023': /* C-s */ mtd->references++; - status_prompt_set(c, "(search) ", "", + status_prompt_set(c, NULL, "(search) ", "", mode_tree_search_callback, mode_tree_search_free, mtd, PROMPT_NOFORMAT); break; @@ -1134,7 +1134,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, break; case 'f': mtd->references++; - status_prompt_set(c, "(filter) ", mtd->filter, + status_prompt_set(c, NULL, "(filter) ", mtd->filter, mode_tree_filter_callback, mode_tree_filter_free, mtd, PROMPT_NOFORMAT); break; diff --git a/server-client.c b/server-client.c index f42c9843..1e0d992d 100644 --- a/server-client.c +++ b/server-client.c @@ -213,7 +213,6 @@ server_client_create(int fd) c->queue = cmdq_new(); c->tty.fd = -1; - c->tty.sx = 80; c->tty.sy = 24; @@ -272,7 +271,7 @@ server_client_open(struct client *c, char **cause) void server_client_lost(struct client *c) { - struct client_file *cf; + struct client_file *cf, *cf1; c->flags |= CLIENT_DEAD; @@ -280,7 +279,7 @@ server_client_lost(struct client *c) status_prompt_clear(c); status_message_clear(c); - RB_FOREACH(cf, client_files, &c->files) { + RB_FOREACH_SAFE(cf, client_files, &c->files, cf1) { cf->error = EINTR; file_fire_done(cf); } @@ -2250,7 +2249,7 @@ server_client_set_flags(struct client *c, const char *flags) } -/*Get client flags. This is only flags useful to show to users. */ +/* Get client flags. This is only flags useful to show to users. */ const char * server_client_get_flags(struct client *c) { diff --git a/status.c b/status.c index b5fa0824..56af02f5 100644 --- a/status.c +++ b/status.c @@ -532,14 +532,17 @@ status_message_redraw(struct client *c) /* Enable status line prompt. */ void -status_prompt_set(struct client *c, const char *msg, const char *input, - prompt_input_cb inputcb, prompt_free_cb freecb, void *data, int flags) +status_prompt_set(struct client *c, struct cmd_find_state *fs, + const char *msg, const char *input, prompt_input_cb inputcb, + prompt_free_cb freecb, void *data, int flags) { struct format_tree *ft; char *tmp, *cp; - ft = format_create(c, NULL, FORMAT_NONE, 0); - format_defaults(ft, c, NULL, NULL, NULL); + if (fs != NULL) + ft = format_create_from_state(NULL, c, fs); + else + ft = format_create_defaults(NULL, c, NULL, NULL, NULL); if (input == NULL) input = ""; diff --git a/tmux.h b/tmux.h index c2b8af40..3ccb005a 100644 --- a/tmux.h +++ b/tmux.h @@ -1504,7 +1504,7 @@ struct client_file { client_file_cb cb; void *data; - RB_ENTRY (client_file) entry; + RB_ENTRY(client_file) entry; }; RB_HEAD(client_files, client_file); @@ -2359,8 +2359,9 @@ void printflike(3, 4) status_message_set(struct client *, int, const char *, ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); -void status_prompt_set(struct client *, const char *, const char *, - prompt_input_cb, prompt_free_cb, void *, int); +void status_prompt_set(struct client *, struct cmd_find_state *, + const char *, const char *, prompt_input_cb, prompt_free_cb, + void *, int); void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); int status_prompt_key(struct client *, key_code); diff --git a/window-customize.c b/window-customize.c index 2d82897f..f444dc6d 100644 --- a/window-customize.c +++ b/window-customize.c @@ -1111,7 +1111,7 @@ window_customize_set_option(struct client *c, new_item->idx = idx; data->references++; - status_prompt_set(c, prompt, value, + status_prompt_set(c, NULL, prompt, value, window_customize_set_option_callback, window_customize_free_item_callback, new_item, PROMPT_NOFORMAT); @@ -1243,7 +1243,7 @@ window_customize_set_key(struct client *c, new_item->key = key; data->references++; - status_prompt_set(c, prompt, value, + status_prompt_set(c, NULL, prompt, value, window_customize_set_command_callback, window_customize_free_item_callback, new_item, PROMPT_NOFORMAT); @@ -1259,7 +1259,8 @@ window_customize_set_key(struct client *c, new_item->key = key; data->references++; - status_prompt_set(c, prompt, (bd->note == NULL ? "" : bd->note), + status_prompt_set(c, NULL, prompt, + (bd->note == NULL ? "" : bd->note), window_customize_set_note_callback, window_customize_free_item_callback, new_item, PROMPT_NOFORMAT); @@ -1398,7 +1399,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, } else xasprintf(&prompt, "Unset option %s? ", item->name); data->references++; - status_prompt_set(c, prompt, "", + status_prompt_set(c, NULL, prompt, "", window_customize_unset_current_callback, window_customize_free_callback, data, PROMPT_SINGLE|PROMPT_NOFORMAT); @@ -1410,7 +1411,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, break; xasprintf(&prompt, "Unset or unbind %u tagged? ", tagged); data->references++; - status_prompt_set(c, prompt, "", + status_prompt_set(c, NULL, prompt, "", window_customize_unset_tagged_callback, window_customize_free_callback, data, PROMPT_SINGLE|PROMPT_NOFORMAT); diff --git a/window-tree.c b/window-tree.c index 5731fff6..32b94e15 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1234,7 +1234,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, if (prompt == NULL) break; data->references++; - status_prompt_set(c, prompt, "", + status_prompt_set(c, NULL, prompt, "", window_tree_kill_current_callback, window_tree_command_free, data, PROMPT_SINGLE|PROMPT_NOFORMAT); free(prompt); @@ -1245,7 +1245,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, break; xasprintf(&prompt, "Kill %u tagged? ", tagged); data->references++; - status_prompt_set(c, prompt, "", + status_prompt_set(c, NULL, prompt, "", window_tree_kill_tagged_callback, window_tree_command_free, data, PROMPT_SINGLE|PROMPT_NOFORMAT); free(prompt); @@ -1257,8 +1257,9 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, else xasprintf(&prompt, "(current) "); data->references++; - status_prompt_set(c, prompt, "", window_tree_command_callback, - window_tree_command_free, data, PROMPT_NOFORMAT); + status_prompt_set(c, NULL, prompt, "", + window_tree_command_callback, window_tree_command_free, + data, PROMPT_NOFORMAT); free(prompt); break; case '\r': From 303d342d5fa5903983c08e4cae429e4f9480eea3 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:20:59 +0000 Subject: [PATCH 0424/1006] Add a client flag 'active-pane' which stores the active pane in the client and allows it to be changed independently from the real active pane stored in the window. This is can be used with session groups which allow an independent current window (although it would be nice to have a flag for this too and remove session groups). The client active pane is only really useful interactively, many things (hooks, window-style, zooming) still use the window active pane. --- cmd-break-pane.c | 1 + cmd-find.c | 23 +++++++++--- cmd-join-pane.c | 1 + cmd-kill-pane.c | 1 + cmd-queue.c | 2 +- cmd-select-pane.c | 18 ++++++--- cmd-split-window.c | 1 + cmd-swap-pane.c | 3 ++ screen-redraw.c | 15 ++++---- server-client.c | 91 +++++++++++++++++++++++++++++++++++++++++++++- server-fn.c | 2 + spawn.c | 1 + tmux.1 | 9 +++++ tmux.h | 17 ++++++++- tty.c | 5 +-- 15 files changed, 164 insertions(+), 26 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 87892d73..9483aa7e 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -89,6 +89,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) } TAILQ_REMOVE(&w->panes, wp, entry); + server_client_remove_pane(wp); window_lost_pane(w, wp); layout_close_pane(wp); diff --git a/cmd-find.c b/cmd-find.c index 9c8bcbc1..9f04c4a8 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -588,22 +588,22 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) return (-1); return (0); } else if (strcmp(pane, "{up-of}") == 0) { - fs->wp = window_pane_find_up(fs->w->active); + fs->wp = window_pane_find_up(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{down-of}") == 0) { - fs->wp = window_pane_find_down(fs->w->active); + fs->wp = window_pane_find_down(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{left-of}") == 0) { - fs->wp = window_pane_find_left(fs->w->active); + fs->wp = window_pane_find_left(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{right-of}") == 0) { - fs->wp = window_pane_find_right(fs->w->active); + fs->wp = window_pane_find_right(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); @@ -615,7 +615,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) n = strtonum(pane + 1, 1, INT_MAX, NULL); else n = 1; - wp = fs->w->active; + wp = fs->current->wp; if (pane[0] == '+') fs->wp = window_pane_next_by_number(fs->w, wp, n); else @@ -867,7 +867,18 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) /* If this is an attached client, all done. */ if (c->session != NULL) { - cmd_find_from_session(fs, c->session, flags); + cmd_find_clear_state(fs, flags); + + fs->wp = server_client_get_pane(c); + if (fs->wp == NULL) { + cmd_find_from_session(fs, c->session, flags); + return (0); + } + fs->s = c->session; + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + + cmd_find_log_state(__func__, fs); return (0); } cmd_find_clear_state(fs, flags); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 3efe769b..9802083d 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -136,6 +136,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) layout_close_pane(src_wp); + server_client_remove_pane(src_wp); window_lost_pane(src_w, src_wp); TAILQ_REMOVE(&src_w->panes, src_wp, entry); diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 2302d7bb..3bf6e26e 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -54,6 +54,7 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { if (loopwp == wp) continue; + server_client_remove_pane(loopwp); layout_close_pane(loopwp); window_remove_pane(wl->window, loopwp); } diff --git a/cmd-queue.c b/cmd-queue.c index 6bc6d0d2..5620fdad 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -809,7 +809,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) } file_print(c, "%s\n", msg); } else { - wp = c->session->curw->window->active; + wp = server_client_get_pane(c); wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) { window_pane_set_mode(wp, NULL, &window_view_mode, NULL, diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 224370ab..3b639e06 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -87,10 +87,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) const struct cmd_entry *entry = cmd_get_entry(self); struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); + struct client *c = cmdq_get_client(item); struct winlink *wl = target->wl; struct window *w = wl->window; struct session *s = target->s; - struct window_pane *wp = target->wp, *lastwp, *markedwp; + struct window_pane *wp = target->wp, *activewp, *lastwp, *markedwp; struct options *oo = wp->options; char *title; const char *style; @@ -201,16 +202,21 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (wp == w->active) + if (c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) + activewp = server_client_get_pane(c); + else + activewp = w->active; + if (wp == activewp) return (CMD_RETURN_NORMAL); if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); - if (window_set_active_pane(w, wp, 1)) { + if (c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) + server_client_set_pane(c, wp); + else if (window_set_active_pane(w, wp, 1)) cmd_find_from_winlink_pane(current, wl, wp, 0); - cmdq_insert_hook(s, item, current, "after-select-pane"); - cmd_select_pane_redraw(w); - } + cmdq_insert_hook(s, item, current, "after-select-pane"); + cmd_select_pane_redraw(w); if (window_pop_zoom(w)) server_redraw_window(w); diff --git a/cmd-split-window.c b/cmd-split-window.c index c0e0e22a..c9d92fae 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -160,6 +160,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } if (input && window_pane_start_input(new_wp, item, &cause) != 0) { + server_client_remove_pane(new_wp); layout_close_pane(new_wp); window_remove_pane(wp->window, new_wp); cmdq_error(item, "%s", cause); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 021ac224..dd981b9a 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -79,6 +79,9 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) if (src_wp == dst_wp) goto out; + server_client_remove_pane(src_wp); + server_client_remove_pane(dst_wp); + tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry); TAILQ_REMOVE(&dst_w->panes, dst_wp, entry); TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry); diff --git a/screen-redraw.c b/screen-redraw.c index 0f83479c..ffa7aecf 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -242,7 +242,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, struct window_pane **wpp) { struct window *w = c->session->curw->window; - struct window_pane *wp; + struct window_pane *wp, *active; int border; u_int right, line; @@ -254,7 +254,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, return (screen_redraw_type_of_cell(c, px, py, pane_status)); if (pane_status != PANE_STATUS_OFF) { - wp = w->active; + active = wp = server_client_get_pane(c); do { if (!window_pane_visible(wp)) goto next1; @@ -272,10 +272,10 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, wp = TAILQ_NEXT(wp, entry); if (wp == NULL) wp = TAILQ_FIRST(&w->panes); - } while (wp != w->active); + } while (wp != active); } - wp = w->active; + active = wp = server_client_get_pane(c); do { if (!window_pane_visible(wp)) goto next2; @@ -296,7 +296,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, wp = TAILQ_NEXT(wp, entry); if (wp == NULL) wp = TAILQ_FIRST(&w->panes); - } while (wp != w->active); + } while (wp != active); return (CELL_OUTSIDE); } @@ -330,7 +330,7 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); format_defaults(ft, c, c->session, c->session->curw, wp); - if (wp == w->active) + if (wp == server_client_get_pane(c)) style_apply(&gc, w->options, "pane-active-border-style", ft); else style_apply(&gc, w->options, "pane-border-style", ft); @@ -558,6 +558,7 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, struct client *c = ctx->c; struct session *s = c->session; struct window *w = s->curw->window; + struct window_pane *active = server_client_get_pane(c); struct options *oo = w->options; struct grid_cell *gc; struct format_tree *ft; @@ -569,7 +570,7 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, ft = format_create_defaults(NULL, c, s, s->curw, wp); gc = &wp->border_gc; - if (screen_redraw_check_is(x, y, ctx->pane_status, w->active)) { + if (screen_redraw_check_is(x, y, ctx->pane_status, active)) { style_apply(gc, oo, "pane-active-border-style", ft); gc->attr |= GRID_ATTR_CHARSET; } else { diff --git a/server-client.c b/server-client.c index 1e0d992d..3ce393cd 100644 --- a/server-client.c +++ b/server-client.c @@ -56,6 +56,19 @@ static void server_client_dispatch_read_data(struct client *, static void server_client_dispatch_read_done(struct client *, struct imsg *); +/* Compare client windows. */ +static int +server_client_window_cmp(struct client_window *cw1, + struct client_window *cw2) +{ + if (cw1->window < cw2->window) + return (-1); + if (cw1->window > cw2->window) + return (1); + return (0); +} +RB_GENERATE(client_windows, client_window, entry, server_client_window_cmp); + /* Number of attached clients. */ u_int server_client_how_many(void) @@ -211,6 +224,7 @@ server_client_create(int fd) c->cwd = NULL; c->queue = cmdq_new(); + RB_INIT(&c->windows); c->tty.fd = -1; c->tty.sx = 80; @@ -272,6 +286,7 @@ void server_client_lost(struct client *c) { struct client_file *cf, *cf1; + struct client_window *cw, *cw1; c->flags |= CLIENT_DEAD; @@ -283,6 +298,10 @@ server_client_lost(struct client *c) cf->error = EINTR; file_fire_done(cf); } + RB_FOREACH_SAFE(cw, client_windows, &c->windows, cw1) { + RB_REMOVE(client_windows, &c->windows, cw); + free(cw); + } TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); @@ -1126,7 +1145,7 @@ server_client_key_callback(struct cmdq_item *item, void *data) /* Find affected pane. */ if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0) - cmd_find_from_session(&fs, s, 0); + cmd_find_from_client(&fs, c, 0); wp = fs.wp; /* Forward mouse keys if disabled. */ @@ -1535,7 +1554,7 @@ server_client_reset_state(struct client *c) { struct tty *tty = &c->tty; struct window *w = c->session->curw->window; - struct window_pane *wp = w->active, *loop; + struct window_pane *wp = server_client_get_pane(c), *loop; struct screen *s = NULL; struct options *oo = c->session->options; int mode = 0, cursor, flags; @@ -2236,6 +2255,8 @@ server_client_set_flags(struct client *c, const char *flags) flag = CLIENT_READONLY; else if (strcmp(next, "ignore-size") == 0) flag = CLIENT_IGNORESIZE; + else if (strcmp(next, "active-pane") == 0) + flag = CLIENT_ACTIVEPANE; else continue; @@ -2266,6 +2287,8 @@ server_client_get_flags(struct client *c) strlcat(s, "no-output,", sizeof s); if (c->flags & CLIENT_READONLY) strlcat(s, "read-only,", sizeof s); + if (c->flags & CLIENT_ACTIVEPANE) + strlcat(s, "active-pane,", sizeof s); if (c->flags & CLIENT_SUSPENDED) strlcat(s, "suspended,", sizeof s); if (c->flags & CLIENT_UTF8) @@ -2274,3 +2297,67 @@ server_client_get_flags(struct client *c) s[strlen(s) - 1] = '\0'; return (s); } + +/* Get client window. */ +static struct client_window * +server_client_get_client_window(struct client *c, u_int id) +{ + struct client_window cw = { .window = id }; + + return (RB_FIND(client_windows, &c->windows, &cw)); +} + +/* Get client active pane. */ +struct window_pane * +server_client_get_pane(struct client *c) +{ + struct session *s = c->session; + struct client_window *cw; + + if (s == NULL) + return (NULL); + + if (~c->flags & CLIENT_ACTIVEPANE) + return (s->curw->window->active); + cw = server_client_get_client_window(c, s->curw->window->id); + if (cw == NULL) + return (s->curw->window->active); + return (cw->pane); +} + +/* Set client active pane. */ +void +server_client_set_pane(struct client *c, struct window_pane *wp) +{ + struct session *s = c->session; + struct client_window *cw; + + if (s == NULL) + return; + + cw = server_client_get_client_window(c, s->curw->window->id); + if (cw == NULL) { + cw = xcalloc(1, sizeof *cw); + cw->window = s->curw->window->id; + RB_INSERT(client_windows, &c->windows, cw); + } + cw->pane = wp; + log_debug("%s pane now %%%u", c->name, wp->id); +} + +/* Remove pane from client lists. */ +void +server_client_remove_pane(struct window_pane *wp) +{ + struct client *c; + struct window *w = wp->window; + struct client_window *cw; + + TAILQ_FOREACH(c, &clients, entry) { + cw = server_client_get_client_window(c, w->id); + if (cw != NULL && cw->pane == wp) { + RB_REMOVE(client_windows, &c->windows, cw); + free(cw); + } + } +} diff --git a/server-fn.c b/server-fn.c index fde1d8e8..d66aed0b 100644 --- a/server-fn.c +++ b/server-fn.c @@ -187,6 +187,7 @@ server_kill_pane(struct window_pane *wp) recalculate_sizes(); } else { server_unzoom_window(w); + server_client_remove_pane(wp); layout_close_pane(wp); window_remove_pane(w, wp); server_redraw_window(w); @@ -348,6 +349,7 @@ server_destroy_pane(struct window_pane *wp, int notify) notify_pane("pane-exited", wp); server_unzoom_window(w); + server_client_remove_pane(wp); layout_close_pane(wp); window_remove_pane(w, wp); diff --git a/spawn.c b/spawn.c index f05887b2..01f19097 100644 --- a/spawn.c +++ b/spawn.c @@ -362,6 +362,7 @@ spawn_pane(struct spawn_context *sc, char **cause) xasprintf(cause, "fork failed: %s", strerror(errno)); new_wp->fd = -1; if (~sc->flags & SPAWN_RESPAWN) { + server_client_remove_pane(new_wp); layout_close_pane(new_wp); window_remove_pane(w, new_wp); } diff --git a/tmux.1 b/tmux.1 index baa296fb..0b03becc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -986,6 +986,8 @@ the client is read-only the client does not affect the size of other clients .It no-output the client does not receive pane output in control mode +.It active-pane +the client has an independent active pane .El .Pp A leading @@ -1000,6 +1002,13 @@ When a client is read-only, only keys bound to the or .Ic switch-client commands have any effect. +A client with the +.Ar active-pane +flag allows the active pane to be selected independently of the window's active +pane used by clients without the flag. +This only affects the cursor position and commands issued from the client; +other features such as hooks and styles continue to use the window's active +pane. .Pp If no server is started, .Ic attach-session diff --git a/tmux.h b/tmux.h index 3ccb005a..3bd37f05 100644 --- a/tmux.h +++ b/tmux.h @@ -1508,6 +1508,14 @@ struct client_file { }; RB_HEAD(client_files, client_file); +/* Client window. */ +struct client_window { + u_int window; + struct window_pane *pane; + RB_ENTRY(client_window) entry; +}; +RB_HEAD(client_windows, client_window); + /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); @@ -1521,6 +1529,8 @@ struct client { struct tmuxpeer *peer; struct cmdq_list *queue; + struct client_windows windows; + pid_t pid; int fd; struct event event; @@ -1585,6 +1595,7 @@ struct client { #define CLIENT_STARTSERVER 0x10000000 #define CLIENT_REDRAWPANES 0x20000000 #define CLIENT_NOFORK 0x40000000 +#define CLIENT_ACTIVEPANE 0x80000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -1600,7 +1611,7 @@ struct client { (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_DETACHING) - int flags; + uint64_t flags; struct key_table *keytable; uint64_t redraw_panes; @@ -2299,6 +2310,7 @@ void server_add_accept(int); void printflike(1, 2) server_add_message(const char *, ...); /* server-client.c */ +RB_PROTOTYPE(client_windows, client_window, entry, server_client_window_cmp); u_int server_client_how_many(void); void server_client_set_overlay(struct client *, u_int, overlay_check_cb, overlay_mode_cb, overlay_draw_cb, overlay_key_cb, @@ -2321,6 +2333,9 @@ void server_client_push_stderr(struct client *); const char *server_client_get_cwd(struct client *, struct session *); void server_client_set_flags(struct client *, const char *); const char *server_client_get_flags(struct client *); +struct window_pane *server_client_get_pane(struct client *); +void server_client_set_pane(struct client *, struct window_pane *); +void server_client_remove_pane(struct window_pane *); /* server-fn.c */ void server_redraw_client(struct client *); diff --git a/tty.c b/tty.c index a1e2f2c6..a4f2acee 100644 --- a/tty.c +++ b/tty.c @@ -693,8 +693,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } if (s != NULL && tty->cstyle != s->cstyle) { if (tty_term_has(tty->term, TTYC_SS)) { - if (s->cstyle == 0 && - tty_term_has(tty->term, TTYC_SE)) + if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) tty_putcode(tty, TTYC_SE); else tty_putcode1(tty, TTYC_SS, s->cstyle); @@ -792,7 +791,7 @@ tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) { struct client *c = tty->client; struct window *w = c->session->curw->window; - struct window_pane *wp = w->active; + struct window_pane *wp = server_client_get_pane(c); u_int cx, cy, lines; lines = status_line_size(c); From ecbdcc256fd2c69c60c9d900a28922914d6b9896 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:22:01 +0000 Subject: [PATCH 0425/1006] Add screen write flags instead of individual bits and fix line length calculation with padding. --- grid.c | 4 +++- screen-write.c | 4 ++-- tmux.h | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/grid.c b/grid.c index 2437e2ea..06a82522 100644 --- a/grid.c +++ b/grid.c @@ -1391,7 +1391,9 @@ grid_line_length(struct grid *gd, u_int py) px = gd->sx; while (px > 0) { grid_get_cell(gd, px - 1, py, &gc); - if (gc.data.size != 1 || *gc.data.data != ' ') + if ((gc.flags & GRID_FLAG_PADDING) || + gc.data.size != 1 || + *gc.data.data != ' ') break; px--; } diff --git a/screen-write.c b/screen-write.c index 9571cbec..062a2929 100644 --- a/screen-write.c +++ b/screen-write.c @@ -184,10 +184,10 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, } if (ctx->wp != NULL && - !ctx->sync && + (~ctx->flags & SCREEN_WRITE_SYNC) && (sync || ctx->wp != ctx->wp->window->active)) { tty_write(tty_cmd_syncstart, ttyctx); - ctx->sync = 1; + ctx->flags |= SCREEN_WRITE_SYNC; } } diff --git a/tmux.h b/tmux.h index 3bd37f05..921a51f9 100644 --- a/tmux.h +++ b/tmux.h @@ -796,7 +796,9 @@ typedef void (*screen_write_init_ctx_cb)(struct screen_write_ctx *, struct screen_write_ctx { struct window_pane *wp; struct screen *s; - int sync; + + int flags; +#define SCREEN_WRITE_SYNC 0x1 screen_write_init_ctx_cb init_ctx_cb; void *arg; From e2a26740b9880d0066c8a04ca2d7202e7f99bd07 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:26:34 +0000 Subject: [PATCH 0426/1006] Add an option to set the pane border lines style from a choice of single lines (ACS or UTF-8), double or heavy (UTF-8), simple (plain ASCII) or number (the pane numbers). Lines that won't work on a non-UTF-8 terminal are translated back into ACS when they are output. --- format-draw.c | 3 +- options-table.c | 11 +++++ screen-redraw.c | 129 +++++++++++++++++++++++++++++++++++------------- tmux.1 | 22 +++++++++ tmux.h | 11 +++++ tty-acs.c | 85 ++++++++++++++++++++++++++++--- tty.c | 34 +++++++------ 7 files changed, 239 insertions(+), 56 deletions(-) diff --git a/format-draw.c b/format-draw.c index bd32b2a8..ec98ba95 100644 --- a/format-draw.c +++ b/format-draw.c @@ -600,7 +600,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, /* If this style pushed or popped the default, update it. */ if (sy.default_type == STYLE_DEFAULT_PUSH) { - memcpy(¤t_default, &saved_sy.gc, sizeof current_default); + memcpy(¤t_default, &saved_sy.gc, + sizeof current_default); sy.default_type = STYLE_DEFAULT_BASE; } else if (sy.default_type == STYLE_DEFAULT_POP) { memcpy(¤t_default, base, sizeof current_default); diff --git a/options-table.c b/options-table.c index b1c4ec1b..4ac0d0c3 100644 --- a/options-table.c +++ b/options-table.c @@ -60,6 +60,9 @@ static const char *options_table_visual_bell_list[] = { static const char *options_table_pane_status_list[] = { "off", "top", "bottom", NULL }; +static const char *options_table_pane_lines_list[] = { + "single", "double", "heavy", "simple", "number", NULL +}; static const char *options_table_set_clipboard_list[] = { "off", "external", "on", NULL }; @@ -904,6 +907,14 @@ const struct options_table_entry options_table[] = { .text = "Format of text in the pane status lines." }, + { .name = "pane-border-lines", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, + .choices = options_table_pane_lines_list, + .default_num = PANE_LINES_SINGLE, + .text = "Type of the pane type lines." + }, + { .name = "pane-border-status", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, diff --git a/screen-redraw.c b/screen-redraw.c index ffa7aecf..002970e9 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -45,8 +45,36 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *, #define CELL_BORDERS " xqlkmjwvtun~" -const struct grid_cell screen_redraw_border_cell = { - { { ' ' }, 0, 1, 1 }, GRID_ATTR_CHARSET, 0, 8, 8, 0 +static const struct utf8_data screen_redraw_double_borders[] = { + { "", 0, 0, 0 }, + { "\342\225\221", 0, 3, 1 }, /* U+2551 */ + { "\342\225\220", 0, 3, 1 }, /* U+2550 */ + { "\342\225\224", 0, 3, 1 }, /* U+2554 */ + { "\342\225\227", 0, 3, 1 }, /* U+2557 */ + { "\342\225\232", 0, 3, 1 }, /* U+255A */ + { "\342\225\235", 0, 3, 1 }, /* U+255D */ + { "\342\225\246", 0, 3, 1 }, /* U+2566 */ + { "\342\225\251", 0, 3, 1 }, /* U+2569 */ + { "\342\225\240", 0, 3, 1 }, /* U+2560 */ + { "\342\225\243", 0, 3, 1 }, /* U+2563 */ + { "\342\225\254", 0, 3, 1 }, /* U+256C */ + { "\302\267", 0, 2, 1 } /* U+00B7 */ +}; + +static const struct utf8_data screen_redraw_heavy_borders[] = { + { "", 0, 0, 0 }, + { "\342\224\203", 0, 3, 1 }, /* U+2503 */ + { "\342\224\201", 0, 3, 1 }, /* U+2501 */ + { "\342\224\223", 0, 3, 1 }, /* U+2513 */ + { "\342\224\217", 0, 3, 1 }, /* U+250F */ + { "\342\224\227", 0, 3, 1 }, /* U+2517 */ + { "\342\224\233", 0, 3, 1 }, /* U+251B */ + { "\342\224\263", 0, 3, 1 }, /* U+2533 */ + { "\342\224\273", 0, 3, 1 }, /* U+253B */ + { "\342\224\243", 0, 3, 1 }, /* U+2523 */ + { "\342\224\253", 0, 3, 1 }, /* U+252B */ + { "\342\225\213", 0, 3, 1 }, /* U+254B */ + { "\302\267", 0, 2, 1 } /* U+00B7 */ }; enum screen_redraw_border_type { @@ -55,6 +83,45 @@ enum screen_redraw_border_type { SCREEN_REDRAW_BORDER }; +/* Get cell border character. */ +static void +screen_redraw_border_set(struct window_pane *wp, int pane_lines, int cell_type, + struct grid_cell *gc) +{ + u_int idx; + + switch (pane_lines) { + case PANE_LINES_NUMBER: + if (cell_type == CELL_OUTSIDE) { + gc->attr |= GRID_ATTR_CHARSET; + utf8_set(&gc->data, CELL_BORDERS[CELL_OUTSIDE]); + break; + } + gc->attr &= ~GRID_ATTR_CHARSET; + if (wp != NULL && window_pane_index(wp, &idx) == 0) + utf8_set(&gc->data, '0' + (idx % 10)); + else + utf8_set(&gc->data, '*'); + break; + case PANE_LINES_DOUBLE: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_copy(&gc->data, &screen_redraw_double_borders[cell_type]); + break; + case PANE_LINES_HEAVY: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_copy(&gc->data, &screen_redraw_heavy_borders[cell_type]); + break; + case PANE_LINES_SIMPLE: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_set(&gc->data, " |-+++++++++."[cell_type]); + break; + default: + gc->attr |= GRID_ATTR_CHARSET; + utf8_set(&gc->data, CELL_BORDERS[cell_type]); + break; + } +} + /* Return if window has only two panes. */ static int screen_redraw_two_panes(struct window *w, int direction) @@ -317,7 +384,7 @@ screen_redraw_check_is(u_int px, u_int py, int pane_status, /* Update pane status. */ static int screen_redraw_make_pane_status(struct client *c, struct window *w, - struct window_pane *wp) + struct window_pane *wp, int pane_lines) { struct grid_cell gc; const char *fmt; @@ -348,9 +415,9 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, screen_write_start(&ctx, &wp->status_screen); - gc.attr |= GRID_ATTR_CHARSET; + screen_redraw_border_set(wp, pane_lines, CELL_TOPBOTTOM, &gc); for (i = 0; i < width; i++) - screen_write_putc(&ctx, &gc, 'q'); + screen_write_cell(&ctx, &gc); gc.attr &= ~GRID_ATTR_CHARSET; screen_write_cursormove(&ctx, 0, 0, 0); @@ -436,7 +503,7 @@ screen_redraw_update(struct client *c, int flags) struct window *w = c->session->curw->window; struct window_pane *wp; struct options *wo = w->options; - int redraw; + int redraw, lines; if (c->message_string != NULL) redraw = status_message_redraw(c); @@ -451,9 +518,10 @@ screen_redraw_update(struct client *c, int flags) flags |= CLIENT_REDRAWOVERLAY; if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) { + lines = options_get_number(wo, "pane-border-lines"); redraw = 0; TAILQ_FOREACH(wp, &w->panes, entry) { - if (screen_redraw_make_pane_status(c, w, wp)) + if (screen_redraw_make_pane_status(c, w, wp, lines)) redraw = 1; } if (redraw) @@ -483,6 +551,7 @@ screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx) ctx->statuslines = lines; ctx->pane_status = options_get_number(wo, "pane-border-status"); + ctx->pane_lines = options_get_number(wo, "pane-border-lines"); tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); @@ -560,7 +629,6 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, struct window *w = s->curw->window; struct window_pane *active = server_client_get_pane(c); struct options *oo = w->options; - struct grid_cell *gc; struct format_tree *ft; if (wp->border_gc_set) @@ -568,18 +636,13 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, wp->border_gc_set = 1; ft = format_create_defaults(NULL, c, s, s->curw, wp); - gc = &wp->border_gc; - - if (screen_redraw_check_is(x, y, ctx->pane_status, active)) { - style_apply(gc, oo, "pane-active-border-style", ft); - gc->attr |= GRID_ATTR_CHARSET; - } else { - style_apply(gc, oo, "pane-border-style", ft); - gc->attr |= GRID_ATTR_CHARSET; - } - + if (screen_redraw_check_is(x, y, ctx->pane_status, active)) + style_apply(&wp->border_gc, oo, "pane-active-border-style", ft); + else + style_apply(&wp->border_gc, oo, "pane-border-style", ft); format_free(ft); - return (gc); + + return (&wp->border_gc); } /* Draw a border cell. */ @@ -590,39 +653,37 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; - u_int type, x = ctx->ox + i, y = ctx->oy + j; + u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; int pane_status = ctx->pane_status; - const struct grid_cell *gc; - struct grid_cell copy; + struct grid_cell gc; + const struct grid_cell *tmp; if (c->overlay_check != NULL && !c->overlay_check(c, x, y)) return; - type = screen_redraw_check_cell(c, x, y, pane_status, &wp); - if (type == CELL_INSIDE) + cell_type = screen_redraw_check_cell(c, x, y, pane_status, &wp); + if (cell_type == CELL_INSIDE) return; if (wp == NULL) - gc = &screen_redraw_border_cell; + memcpy(&gc, &grid_default_cell, sizeof gc); else { - gc = screen_redraw_draw_borders_style(ctx, x, y, wp); - if (gc == NULL) + tmp = screen_redraw_draw_borders_style(ctx, x, y, wp); + if (tmp == NULL) return; + memcpy(&gc, tmp, sizeof gc); if (server_is_marked(s, s->curw, marked_pane.wp) && - screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) { - memcpy(©, gc, sizeof copy); - copy.attr ^= GRID_ATTR_REVERSE; - gc = © - } + screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) + gc.attr ^= GRID_ATTR_REVERSE; } + screen_redraw_border_set(wp, ctx->pane_lines, cell_type, &gc); - tty_attributes(tty, gc, &grid_default_cell, NULL); if (ctx->statustop) tty_cursor(tty, i, ctx->statuslines + j); else tty_cursor(tty, i, j); - tty_putc(tty, CELL_BORDERS[type]); + tty_cell(tty, &gc, &grid_default_cell, NULL); } /* Draw the borders. */ diff --git a/tmux.1 b/tmux.1 index 0b03becc..421f8889 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3924,6 +3924,28 @@ but set the starting index for pane numbers. .It Ic pane-border-format Ar format Set the text shown in pane border status lines. .Pp +.It Ic pane-border-lines Ar type +Set the type of characters used for drawing pane borders. +.Ar type +may be one of: +.Bl -tag -width Ds +.It single +single lines using ACS or UTF-8 characters +.It double +double lines using UTF-8 characters +.It heavy +heavy lines using UTF-8 characters +.It simple +simple ASCII characters +.It number +the pane number +.El +.Pp +.Ql double +and +.Ql heavy +will fall back to standard ACS line drawing when UTF-8 is not supported. +.Pp .It Xo Ic pane-border-status .Op Ic off | top | bottom .Xc diff --git a/tmux.h b/tmux.h index 921a51f9..a413e0cf 100644 --- a/tmux.h +++ b/tmux.h @@ -820,6 +820,7 @@ struct screen_redraw_ctx { int statustop; int pane_status; + int pane_lines; u_int sx; u_int sy; @@ -1050,6 +1051,13 @@ TAILQ_HEAD(winlink_stack, winlink); #define PANE_STATUS_TOP 1 #define PANE_STATUS_BOTTOM 2 +/* Pane border lines option. */ +#define PANE_LINES_SINGLE 0 +#define PANE_LINES_DOUBLE 1 +#define PANE_LINES_HEAVY 2 +#define PANE_LINES_SIMPLE 3 +#define PANE_LINES_NUMBER 4 + /* Layout direction. */ enum layout_type { LAYOUT_LEFTRIGHT, @@ -2030,6 +2038,8 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); +void tty_cell(struct tty *, const struct grid_cell *, + const struct grid_cell *, int *); int tty_init(struct tty *, struct client *, int); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); @@ -2103,6 +2113,7 @@ void tty_default_features(int *, const char *, u_int); /* tty-acs.c */ int tty_acs_needed(struct tty *); const char *tty_acs_get(struct tty *, u_char); +int tty_acs_reverse_get(struct tty *, const char *, size_t); /* tty-keys.c */ void tty_keys_build(struct tty *); diff --git a/tty-acs.c b/tty-acs.c index 3e811103..63eccb93 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -19,11 +19,10 @@ #include #include +#include #include "tmux.h" -static int tty_acs_cmp(const void *, const void *); - /* Table mapping ACS entries to UTF-8. */ struct tty_acs_entry { u_char key; @@ -68,14 +67,65 @@ static const struct tty_acs_entry tty_acs_table[] = { { '~', "\302\267" } /* bullet */ }; +/* Table mapping UTF-8 to ACS entries. */ +struct tty_acs_reverse_entry { + const char *string; + u_char key; +}; +static const struct tty_acs_reverse_entry tty_acs_reverse2[] = { + { "\302\267", '~' } +}; +static const struct tty_acs_reverse_entry tty_acs_reverse3[] = { + { "\342\224\200", 'q' }, + { "\342\224\201", 'q' }, + { "\342\224\202", 'x' }, + { "\342\224\203", 'x' }, + { "\342\224\214", 'l' }, + { "\342\224\217", 'k' }, + { "\342\224\220", 'k' }, + { "\342\224\223", 'l' }, + { "\342\224\224", 'm' }, + { "\342\224\227", 'm' }, + { "\342\224\230", 'j' }, + { "\342\224\233", 'j' }, + { "\342\224\234", 't' }, + { "\342\224\243", 't' }, + { "\342\224\244", 'u' }, + { "\342\224\253", 'u' }, + { "\342\224\263", 'w' }, + { "\342\224\264", 'v' }, + { "\342\224\273", 'v' }, + { "\342\224\274", 'n' }, + { "\342\225\213", 'n' }, + { "\342\225\220", 'q' }, + { "\342\225\221", 'x' }, + { "\342\225\224", 'l' }, + { "\342\225\227", 'k' }, + { "\342\225\232", 'm' }, + { "\342\225\235", 'j' }, + { "\342\225\240", 't' }, + { "\342\225\243", 'u' }, + { "\342\225\246", 'w' }, + { "\342\225\251", 'v' }, + { "\342\225\254", 'n' }, +}; + static int tty_acs_cmp(const void *key, const void *value) { const struct tty_acs_entry *entry = value; - u_char ch; + int test = *(u_char *)key; - ch = *(u_char *) key; - return (ch - entry->key); + return (test - entry->key); +} + +static int +tty_acs_reverse_cmp(const void *key, const void *value) +{ + const struct tty_acs_reverse_entry *entry = value; + const char *test = key; + + return (strcmp(test, entry->string)); } /* Should this terminal use ACS instead of UTF-8 line drawing? */ @@ -104,11 +154,11 @@ tty_acs_needed(struct tty *tty) return (1); } -/* Retrieve ACS to output as a string. */ +/* Retrieve ACS to output as UTF-8. */ const char * tty_acs_get(struct tty *tty, u_char ch) { - struct tty_acs_entry *entry; + const struct tty_acs_entry *entry; /* Use the ACS set instead of UTF-8 if needed. */ if (tty_acs_needed(tty)) { @@ -124,3 +174,24 @@ tty_acs_get(struct tty *tty, u_char ch) return (NULL); return (entry->string); } + +/* Reverse UTF-8 into ACS. */ +int +tty_acs_reverse_get(__unused struct tty *tty, const char *s, size_t slen) +{ + const struct tty_acs_reverse_entry *table, *entry; + u_int items; + + if (slen == 2) { + table = tty_acs_reverse2; + items = nitems(tty_acs_reverse2); + } else if (slen == 3) { + table = tty_acs_reverse3; + items = nitems(tty_acs_reverse3); + } else + return (-1); + entry = bsearch(s, table, items, sizeof table[0], tty_acs_reverse_cmp); + if (entry == NULL) + return (-1); + return (entry->key); +} diff --git a/tty.c b/tty.c index a4f2acee..99996dfa 100644 --- a/tty.c +++ b/tty.c @@ -65,8 +65,6 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, u_int); static void tty_repeat_space(struct tty *, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); -static void tty_cell(struct tty *, const struct grid_cell *, - const struct grid_cell *, int *); static void tty_default_attributes(struct tty *, const struct grid_cell *, int *, u_int); @@ -1243,7 +1241,7 @@ static const struct grid_cell * tty_check_codeset(struct tty *tty, const struct grid_cell *gc) { static struct grid_cell new; - u_int n; + int c; /* Characters less than 0x7f are always fine, no matter what. */ if (gc->data.size == 1 && *gc->data.data < 0x7f) @@ -1252,14 +1250,21 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) /* UTF-8 terminal and a UTF-8 character - fine. */ if (tty->client->flags & CLIENT_UTF8) return (gc); + memcpy(&new, gc, sizeof new); + + /* See if this can be mapped to an ACS character. */ + c = tty_acs_reverse_get(tty, gc->data.data, gc->data.size); + if (c != -1) { + utf8_set(&new.data, c); + new.attr |= GRID_ATTR_CHARSET; + return (&new); + } /* Replace by the right number of underscores. */ - n = gc->data.width; - if (n > UTF8_SIZE) - n = UTF8_SIZE; - memcpy(&new, gc, sizeof new); - new.data.size = n; - memset(new.data.data, '_', n); + new.data.size = gc->data.width; + if (new.data.size > UTF8_SIZE) + new.data.size = UTF8_SIZE; + memset(new.data.data, '_', new.data.size); return (&new); } @@ -1924,7 +1929,7 @@ tty_cmd_syncstart(struct tty *tty, __unused const struct tty_ctx *ctx) tty_sync_start(tty); } -static void +void tty_cell(struct tty *tty, const struct grid_cell *gc, const struct grid_cell *defaults, int *palette) { @@ -1940,12 +1945,13 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, if (gc->flags & GRID_FLAG_PADDING) return; - /* Set the attributes. */ - tty_attributes(tty, gc, defaults, palette); - - /* Get the cell and if ASCII write with putc to do ACS translation. */ + /* Check the output codeset and apply attributes. */ gcp = tty_check_codeset(tty, gc); + tty_attributes(tty, gcp, defaults, palette); + + /* If it is a single character, write with putc to handle ACS. */ if (gcp->data.size == 1) { + tty_attributes(tty, gcp, defaults, palette); if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f) return; tty_putc(tty, *gcp->data.data); From 292b335ca5b594729cf9ff79f0f4273c725537a4 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:35:13 +0000 Subject: [PATCH 0427/1006] Separate key flags and modifiers, log key flags, make the "xterm" flag more explicit and fix M- keys with a leading escape. --- cmd-list-keys.c | 10 ++++----- cmd-queue.c | 2 +- cmd-send-keys.c | 4 +--- key-bindings.c | 4 ++-- key-string.c | 51 +++++++++++++++++++++++++++++++--------------- menu.c | 4 ++-- mode-tree.c | 6 +++--- options.c | 4 ++-- popup.c | 4 ++-- status.c | 12 +++++------ window-customize.c | 8 ++++---- 11 files changed, 63 insertions(+), 46 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 60ef73af..51c90dfe 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -73,7 +73,7 @@ cmd_list_keys_get_width(const char *tablename, key_code only) bd = key_bindings_next(table, bd); continue; } - width = utf8_cstrwidth(key_string_lookup_key(bd->key)); + width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0)); if (width > keywidth) keywidth = width; @@ -106,7 +106,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, continue; } found = 1; - key = key_string_lookup_key(bd->key); + key = key_string_lookup_key(bd->key, 0); if (bd->note == NULL || *bd->note == '\0') note = cmd_list_print(bd->cmdlist, 1); @@ -135,7 +135,7 @@ cmd_list_keys_get_prefix(struct args *args, key_code *prefix) *prefix = options_get_number(global_s_options, "prefix"); if (!args_has(args, 'P')) { if (*prefix != KEYC_NONE) - xasprintf(&s, "%s ", key_string_lookup_key(*prefix)); + xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0)); else s = xstrdup(""); } else @@ -221,7 +221,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) bd = key_bindings_next(table, bd); continue; } - key = args_escape(key_string_lookup_key(bd->key)); + key = args_escape(key_string_lookup_key(bd->key, 0)); if (bd->flags & KEY_BINDING_REPEAT) repeat = 1; @@ -255,7 +255,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) continue; } found = 1; - key = args_escape(key_string_lookup_key(bd->key)); + key = args_escape(key_string_lookup_key(bd->key, 0)); if (!repeat) r = ""; diff --git a/cmd-queue.c b/cmd-queue.c index 5620fdad..b0c70428 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -547,7 +547,7 @@ cmdq_add_message(struct cmdq_item *item) if (c != NULL) { name = c->name; if (c->session != NULL && state->event.key != KEYC_NONE) { - key = key_string_lookup_key(state->event.key); + key = key_string_lookup_key(state->event.key, 0); server_add_message("%s key %s: %s", name, key, tmp); } else server_add_message("%s command: %s", name, tmp); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index afaf0a81..a9ecc807 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -71,15 +71,13 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode->key_table == NULL) { - if (options_get_number(wp->window->options, "xterm-keys")) - key |= KEYC_XTERM; if (window_pane_key(wp, tc, s, wl, key, NULL) != 0) return (NULL); return (item); } table = key_bindings_get_table(wme->mode->key_table(wme), 1); - bd = key_bindings_get(table, key & ~KEYC_XTERM); + bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); if (bd != NULL) { table->references++; after = key_bindings_dispatch(bd, after, tc, NULL, target); diff --git a/key-bindings.c b/key-bindings.c index 05089bab..59cfbb0d 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -190,7 +190,7 @@ 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_XTERM); + bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); if (bd != NULL) { RB_REMOVE(key_bindings, &table->key_bindings, bd); key_bindings_free(bd); @@ -217,7 +217,7 @@ key_bindings_remove(const char *name, key_code key) if (table == NULL) return; - bd = key_bindings_get(table, key & ~KEYC_XTERM); + bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); if (bd == NULL) return; diff --git a/key-string.c b/key-string.c index 2a0602b2..65f1afe5 100644 --- a/key-string.c +++ b/key-string.c @@ -143,7 +143,7 @@ key_string_get_modifiers(const char **string) break; case 'M': case 'm': - modifiers |= KEYC_ESCAPE; + modifiers |= KEYC_META; break; case 'S': case 's': @@ -212,7 +212,7 @@ key_string_lookup_string(const char *string) return (KEYC_UNKNOWN); if (utf8_combine(&ud, &wc) != UTF8_DONE) return (KEYC_UNKNOWN); - return (wc | modifiers); + return (wc|modifiers); } /* Otherwise look the key up in the table. */ @@ -236,14 +236,15 @@ key_string_lookup_string(const char *string) modifiers &= ~KEYC_CTRL; } - return (key | modifiers); + return (key|modifiers); } /* Convert a key code into string format, with prefix if necessary. */ const char * -key_string_lookup_key(key_code key) +key_string_lookup_key(key_code key, int with_flags) { - static char out[32]; + key_code saved = key; + static char out[64]; char tmp[8]; const char *s; u_int i; @@ -255,25 +256,27 @@ key_string_lookup_key(key_code key) /* Literal keys are themselves. */ if (key & KEYC_LITERAL) { snprintf(out, sizeof out, "%c", (int)(key & 0xff)); - return (out); + goto out; } /* Display C-@ as C-Space. */ - if ((key & KEYC_MASK_KEY) == 0) - key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); + if ((key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)) == 0) + key = ' '|KEYC_CTRL; /* Fill in the modifiers. */ if (key & KEYC_CTRL) strlcat(out, "C-", sizeof out); - if (key & KEYC_ESCAPE) + if (key & KEYC_META) strlcat(out, "M-", sizeof out); if (key & KEYC_SHIFT) strlcat(out, "S-", sizeof out); key &= KEYC_MASK_KEY; /* Handle no key. */ - if (key == KEYC_NONE) - return ("None"); + if (key == KEYC_NONE) { + s = "None"; + goto append; + } /* Handle special keys. */ if (key == KEYC_UNKNOWN) { @@ -331,7 +334,7 @@ key_string_lookup_key(key_code key) if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) { snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER)); strlcat(out, tmp, sizeof out); - return (out); + goto out; } /* Try the key against the string table. */ @@ -341,7 +344,7 @@ key_string_lookup_key(key_code key) } if (i != nitems(key_string_table)) { strlcat(out, key_string_table[i].string, sizeof out); - return (out); + goto out; } /* Is this a UTF-8 key? */ @@ -350,14 +353,14 @@ key_string_lookup_key(key_code key) off = strlen(out); memcpy(out + off, ud.data, ud.size); out[off + ud.size] = '\0'; - return (out); + goto out; } } /* Invalid keys are errors. */ if (key > 255) { snprintf(out, sizeof out, "Invalid#%llx", key); - return (out); + goto out; } /* Check for standard or control key. */ @@ -375,9 +378,25 @@ key_string_lookup_key(key_code key) xsnprintf(tmp, sizeof tmp, "\\%llo", key); strlcat(out, tmp, sizeof out); - return (out); + goto out; append: strlcat(out, s, sizeof out); + +out: + if (with_flags && (saved & KEYC_MASK_FLAGS) != 0) { + strlcat(out, "[", sizeof out); + if (saved & KEYC_LITERAL) + strlcat(out, "L", sizeof out); + if (saved & KEYC_KEYPAD) + strlcat(out, "K", sizeof out); + if (saved & KEYC_CURSOR) + strlcat(out, "C", sizeof out); + if (saved & KEYC_IMPLIED_META) + strlcat(out, "I", sizeof out); + if (saved & KEYC_BUILD_MODIFIERS) + strlcat(out, "B", sizeof out); + strlcat(out, "]", sizeof out); + } return (out); } diff --git a/menu.c b/menu.c index 62010a58..7f6933c5 100644 --- a/menu.c +++ b/menu.c @@ -81,7 +81,7 @@ menu_add_item(struct menu *menu, const struct menu_item *item, return; } if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) { - key = key_string_lookup_key(item->key); + key = key_string_lookup_key(item->key, 0); xasprintf(&name, "%s#[default] #[align=right](%s)", s, key); } else xasprintf(&name, "%s", s); @@ -226,7 +226,7 @@ menu_key_cb(struct client *c, struct key_event *event) goto chosen; } } - switch (event->key) { + switch (event->key & ~KEYC_MASK_FLAGS) { case KEYC_UP: case 'k': if (old == -1) diff --git a/mode-tree.c b/mode-tree.c index a523478e..993070ec 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -987,7 +987,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, choice = -1; if (*key >= '0' && *key <= '9') choice = (*key) - '0'; - else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) { + else if (((*key) & KEYC_MASK_MODIFIERS) == KEYC_META) { tmp = (*key) & KEYC_MASK_KEY; if (tmp >= 'a' && tmp <= 'z') choice = 10 + (tmp - 'a'); @@ -1111,12 +1111,12 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_build(mtd); } break; - case '-'|KEYC_ESCAPE: + case '-'|KEYC_META: TAILQ_FOREACH(mti, &mtd->children, entry) mti->expanded = 0; mode_tree_build(mtd); break; - case '+'|KEYC_ESCAPE: + case '+'|KEYC_META: TAILQ_FOREACH(mti, &mtd->children, entry) mti->expanded = 1; mode_tree_build(mtd); diff --git a/options.c b/options.c index 58b7f7c1..6ed38bcd 100644 --- a/options.c +++ b/options.c @@ -130,7 +130,7 @@ options_value_to_string(struct options_entry *o, union options_value *ov, xasprintf(&s, "%lld", ov->number); break; case OPTIONS_TABLE_KEY: - s = xstrdup(key_string_lookup_key(ov->number)); + s = xstrdup(key_string_lookup_key(ov->number, 0)); break; case OPTIONS_TABLE_COLOUR: s = xstrdup(colour_tostring(ov->number)); @@ -283,7 +283,7 @@ options_default_to_string(const struct options_table_entry *oe) xasprintf(&s, "%lld", oe->default_num); break; case OPTIONS_TABLE_KEY: - s = xstrdup(key_string_lookup_key(oe->default_num)); + s = xstrdup(key_string_lookup_key(oe->default_num, 0)); break; case OPTIONS_TABLE_COLOUR: s = xstrdup(colour_tostring(oe->default_num)); diff --git a/popup.c b/popup.c index 93ecd2a1..87658a6f 100644 --- a/popup.c +++ b/popup.c @@ -329,7 +329,7 @@ popup_key_cb(struct client *c, struct key_event *event) bufferevent_write(job_get_event(pd->job), buf, len); return (0); } - input_key(NULL, &pd->s, job_get_event(pd->job), event->key); + input_key(&pd->s, job_get_event(pd->job), event->key); return (0); } @@ -341,7 +341,7 @@ popup_key_cb(struct client *c, struct key_event *event) format_defaults(ft, c, fs->s, fs->wl, fs->wp); else format_defaults(ft, c, NULL, NULL, NULL); - format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key)); + format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key, 0)); if (KEYC_IS_MOUSE(event->key)) { format_add(ft, "popup_mouse", "1"); format_add(ft, "popup_mouse_x", "%u", m->x - pd->px); diff --git a/status.c b/status.c index 56af02f5..93ac70df 100644 --- a/status.c +++ b/status.c @@ -827,7 +827,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) return (1); case 'b': case 'B': - *new_key = 'b'|KEYC_ESCAPE; + *new_key = 'b'|KEYC_META; return (1); case 'd': *new_key = '\025'; @@ -836,7 +836,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) case 'E': case 'w': case 'W': - *new_key = 'f'|KEYC_ESCAPE; + *new_key = 'f'|KEYC_META; return (1); case 'p': *new_key = '\031'; /* C-y */ @@ -1023,7 +1023,7 @@ status_prompt_key(struct client *c, key_code key) int keys; if (c->prompt_flags & PROMPT_KEY) { - keystring = key_string_lookup_key(key); + keystring = key_string_lookup_key(key, 0); c->prompt_inputcb(c, c->prompt_data, keystring, 1); status_prompt_clear(c); return (0); @@ -1039,7 +1039,7 @@ status_prompt_key(struct client *c, key_code key) free(s); return (1); } - key &= ~KEYC_XTERM; + key &= ~KEYC_MASK_FLAGS; keys = options_get_number(c->session->options, "status-keys"); if (keys == MODEKEY_VI) { @@ -1158,7 +1158,7 @@ process_key: c->prompt_index = idx; goto changed; - case 'f'|KEYC_ESCAPE: + case 'f'|KEYC_META: case KEYC_RIGHT|KEYC_CTRL: ws = options_get_string(oo, "word-separators"); @@ -1182,7 +1182,7 @@ process_key: c->prompt_index--; goto changed; - case 'b'|KEYC_ESCAPE: + case 'b'|KEYC_META: case KEYC_LEFT|KEYC_CTRL: ws = options_get_string(oo, "word-separators"); diff --git a/window-customize.c b/window-customize.c index f444dc6d..093ebbe4 100644 --- a/window-customize.c +++ b/window-customize.c @@ -460,7 +460,7 @@ window_customize_build_keys(struct window_customize_modedata *data, bd = key_bindings_first(kt); while (bd != NULL) { - format_add(ft, "key", "%s", key_string_lookup_key(bd->key)); + format_add(ft, "key", "%s", key_string_lookup_key(bd->key, 0)); if (bd->note != NULL) format_add(ft, "key_note", "%s", bd->note); if (filter != NULL) { @@ -1233,7 +1233,7 @@ window_customize_set_key(struct client *c, if (strcmp(s, "Repeat") == 0) bd->flags ^= KEY_BINDING_REPEAT; else if (strcmp(s, "Command") == 0) { - xasprintf(&prompt, "(%s) ", key_string_lookup_key(key)); + xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0)); value = cmd_list_print(bd->cmdlist, 0); new_item = xcalloc(1, sizeof *new_item); @@ -1250,7 +1250,7 @@ window_customize_set_key(struct client *c, free(prompt); free(value); } else if (strcmp(s, "Note") == 0) { - xasprintf(&prompt, "(%s) ", key_string_lookup_key(key)); + xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0)); new_item = xcalloc(1, sizeof *new_item); new_item->data = data; @@ -1395,7 +1395,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, break; if (item->scope == WINDOW_CUSTOMIZE_KEY) { xasprintf(&prompt, "Unbind key %s? ", - key_string_lookup_key(item->key)); + key_string_lookup_key(item->key, 0)); } else xasprintf(&prompt, "Unset option %s? ", item->name); data->references++; From 0ab82d95314e7a26a48452c77ad710f3aff97dd7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:44:54 +0000 Subject: [PATCH 0428/1006] Add a terminal feature for enable/disable extended keys (supported by xterm and mintty) and add an option to make tmux send it. Only forward extended keys if the application has requested them, even though we use the CSI u sequence and xterm uses CSI 27 ~ - this is what mintty does as well. --- input-keys.c | 565 ++++++++++++++++++++++++++++++++++++------------ input.c | 19 +- options-table.c | 13 +- tmux.1 | 18 +- tty-features.c | 17 +- tty-keys.c | 476 ++++++++++++++++++++++++++-------------- tty-term.c | 2 + tty.c | 21 +- 8 files changed, 802 insertions(+), 329 deletions(-) diff --git a/input-keys.c b/input-keys.c index e9c595b4..19134c1a 100644 --- a/input-keys.c +++ b/input-keys.c @@ -33,112 +33,340 @@ static void input_key_mouse(struct window_pane *, struct mouse_event *); -struct input_key_ent { - key_code key; - const char *data; +/* Entry in the key tree. */ +struct input_key_entry { + key_code key; + const char *data; - int flags; -#define INPUTKEY_KEYPAD 0x1 /* keypad key */ -#define INPUTKEY_CURSOR 0x2 /* cursor key */ + RB_ENTRY(input_key_entry) entry; }; +RB_HEAD(input_key_tree, input_key_entry); -static const struct input_key_ent input_keys[] = { +/* Tree of input keys. */ +static int input_key_cmp(struct input_key_entry *, + struct input_key_entry *); +RB_GENERATE_STATIC(input_key_tree, input_key_entry, entry, input_key_cmp); +struct input_key_tree input_key_tree = RB_INITIALIZER(&input_key_tree); + +/* List of default keys, the tree is built from this. */ +static struct input_key_entry input_key_defaults[] = { /* Paste keys. */ - { KEYC_PASTE_START, "\033[200~", 0 }, - { KEYC_PASTE_END, "\033[201~", 0 }, + { .key = KEYC_PASTE_START, + .data = "\033[200~" + }, + { .key = KEYC_PASTE_END, + .data = "\033[201~" + }, /* Function keys. */ - { KEYC_F1, "\033OP", 0 }, - { KEYC_F2, "\033OQ", 0 }, - { KEYC_F3, "\033OR", 0 }, - { KEYC_F4, "\033OS", 0 }, - { KEYC_F5, "\033[15~", 0 }, - { KEYC_F6, "\033[17~", 0 }, - { KEYC_F7, "\033[18~", 0 }, - { KEYC_F8, "\033[19~", 0 }, - { KEYC_F9, "\033[20~", 0 }, - { KEYC_F10, "\033[21~", 0 }, - { KEYC_F11, "\033[23~", 0 }, - { KEYC_F12, "\033[24~", 0 }, - { KEYC_F1|KEYC_SHIFT, "\033[25~", 0 }, - { KEYC_F2|KEYC_SHIFT, "\033[26~", 0 }, - { KEYC_F3|KEYC_SHIFT, "\033[28~", 0 }, - { KEYC_F4|KEYC_SHIFT, "\033[29~", 0 }, - { KEYC_F5|KEYC_SHIFT, "\033[31~", 0 }, - { KEYC_F6|KEYC_SHIFT, "\033[32~", 0 }, - { KEYC_F7|KEYC_SHIFT, "\033[33~", 0 }, - { KEYC_F8|KEYC_SHIFT, "\033[34~", 0 }, - { KEYC_IC, "\033[2~", 0 }, - { KEYC_DC, "\033[3~", 0 }, - { KEYC_HOME, "\033[1~", 0 }, - { KEYC_END, "\033[4~", 0 }, - { KEYC_NPAGE, "\033[6~", 0 }, - { KEYC_PPAGE, "\033[5~", 0 }, - { KEYC_BTAB, "\033[Z", 0 }, + { .key = KEYC_F1, + .data = "\033OP" + }, + { .key = KEYC_F2, + .data = "\033OQ" + }, + { .key = KEYC_F3, + .data = "\033OR" + }, + { .key = KEYC_F4, + .data = "\033OS" + }, + { .key = KEYC_F5, + .data = "\033[15~" + }, + { .key = KEYC_F6, + .data = "\033[17~" + }, + { .key = KEYC_F7, + .data = "\033[18~" + }, + { .key = KEYC_F8, + .data = "\033[19~" + }, + { .key = KEYC_F9, + .data = "\033[20~" + }, + { .key = KEYC_F10, + .data = "\033[21~" + }, + { .key = KEYC_F11, + .data = "\033[23~" + }, + { .key = KEYC_F12, + .data = "\033[24~" + }, + { .key = KEYC_F1|KEYC_SHIFT, + .data = "\033[25~" + }, + { .key = KEYC_F2|KEYC_SHIFT, + .data = "\033[26~" + }, + { .key = KEYC_F3|KEYC_SHIFT, + .data = "\033[28~" + }, + { .key = KEYC_F4|KEYC_SHIFT, + .data = "\033[29~" + }, + { .key = KEYC_F5|KEYC_SHIFT, + .data = "\033[31~" + }, + { .key = KEYC_F6|KEYC_SHIFT, + .data = "\033[32~" + }, + { .key = KEYC_F7|KEYC_SHIFT, + .data = "\033[33~" + }, + { .key = KEYC_F8|KEYC_SHIFT, + .data = "\033[34~" + }, + { .key = KEYC_IC, + .data = "\033[2~" + }, + { .key = KEYC_DC, + .data = "\033[3~" + }, + { .key = KEYC_HOME, + .data = "\033[1~" + }, + { .key = KEYC_END, + .data = "\033[4~" + }, + { .key = KEYC_NPAGE, + .data = "\033[6~" + }, + { .key = KEYC_PPAGE, + .data = "\033[5~" + }, + { .key = KEYC_BTAB, + .data = "\033[Z" + }, - /* - * Arrow keys. Cursor versions must come first. The codes are toggled - * between CSI and SS3 versions when ctrl is pressed. - */ - { KEYC_UP|KEYC_CTRL, "\033[A", INPUTKEY_CURSOR }, - { KEYC_DOWN|KEYC_CTRL, "\033[B", INPUTKEY_CURSOR }, - { KEYC_RIGHT|KEYC_CTRL, "\033[C", INPUTKEY_CURSOR }, - { KEYC_LEFT|KEYC_CTRL, "\033[D", INPUTKEY_CURSOR }, + /* Arrow keys. */ + { .key = KEYC_UP|KEYC_CURSOR, + .data = "\033OA" + }, + { .key = KEYC_DOWN|KEYC_CURSOR, + .data = "\033OB" + }, + { .key = KEYC_RIGHT|KEYC_CURSOR, + .data = "\033OC" + }, + { .key = KEYC_LEFT|KEYC_CURSOR, + .data = "\033OD" + }, + { .key = KEYC_UP, + .data = "\033[A" + }, + { .key = KEYC_DOWN, + .data = "\033[B" + }, + { .key = KEYC_RIGHT, + .data = "\033[C" + }, + { .key = KEYC_LEFT, + .data = "\033[D" + }, - { KEYC_UP, "\033OA", INPUTKEY_CURSOR }, - { KEYC_DOWN, "\033OB", INPUTKEY_CURSOR }, - { KEYC_RIGHT, "\033OC", INPUTKEY_CURSOR }, - { KEYC_LEFT, "\033OD", INPUTKEY_CURSOR }, + /* Keypad keys. */ + { .key = KEYC_KP_SLASH|KEYC_KEYPAD, + .data = "\033Oo" + }, + { .key = KEYC_KP_STAR|KEYC_KEYPAD, + .data = "\033Oj" + }, + { .key = KEYC_KP_MINUS|KEYC_KEYPAD, + .data = "\033Om" + }, + { .key = KEYC_KP_SEVEN|KEYC_KEYPAD, + .data = "\033Ow" + }, + { .key = KEYC_KP_EIGHT|KEYC_KEYPAD, + .data = "\033Ox" + }, + { .key = KEYC_KP_NINE|KEYC_KEYPAD, + .data = "\033Oy" + }, + { .key = KEYC_KP_PLUS|KEYC_KEYPAD, + .data = "\033Ok" + }, + { .key = KEYC_KP_FOUR|KEYC_KEYPAD, + .data = "\033Ot" + }, + { .key = KEYC_KP_FIVE|KEYC_KEYPAD, + .data = "\033Ou" + }, + { .key = KEYC_KP_SIX|KEYC_KEYPAD, + .data = "\033Ov" + }, + { .key = KEYC_KP_ONE|KEYC_KEYPAD, + .data = "\033Oq" + }, + { .key = KEYC_KP_TWO|KEYC_KEYPAD, + .data = "\033Or" + }, + { .key = KEYC_KP_THREE|KEYC_KEYPAD, + .data = "\033Os" + }, + { .key = KEYC_KP_ENTER|KEYC_KEYPAD, + .data = "\033OM" + }, + { .key = KEYC_KP_ZERO|KEYC_KEYPAD, + .data = "\033Op" + }, + { .key = KEYC_KP_PERIOD|KEYC_KEYPAD, + .data = "\033On" + }, + { .key = KEYC_KP_SLASH, + .data = "/" + }, + { .key = KEYC_KP_STAR, + .data = "*" + }, + { .key = KEYC_KP_MINUS, + .data = "-" + }, + { .key = KEYC_KP_SEVEN, + .data = "7" + }, + { .key = KEYC_KP_EIGHT, + .data = "8" + }, + { .key = KEYC_KP_NINE, + .data = "9" + }, + { .key = KEYC_KP_PLUS, + .data = "+" + }, + { .key = KEYC_KP_FOUR, + .data = "4" + }, + { .key = KEYC_KP_FIVE, + .data = "5" + }, + { .key = KEYC_KP_SIX, + .data = "6" + }, + { .key = KEYC_KP_ONE, + .data = "1" + }, + { .key = KEYC_KP_TWO, + .data = "2" + }, + { .key = KEYC_KP_THREE, + .data = "3" + }, + { .key = KEYC_KP_ENTER, + .data = "\n" + }, + { .key = KEYC_KP_ZERO, + .data = "0" + }, + { .key = KEYC_KP_PERIOD, + .data = "." + }, - { KEYC_UP|KEYC_CTRL, "\033OA", 0 }, - { KEYC_DOWN|KEYC_CTRL, "\033OB", 0 }, - { KEYC_RIGHT|KEYC_CTRL, "\033OC", 0 }, - { KEYC_LEFT|KEYC_CTRL, "\033OD", 0 }, - - { KEYC_UP, "\033[A", 0 }, - { KEYC_DOWN, "\033[B", 0 }, - { KEYC_RIGHT, "\033[C", 0 }, - { KEYC_LEFT, "\033[D", 0 }, - - /* Keypad keys. Keypad versions must come first. */ - { KEYC_KP_SLASH, "\033Oo", INPUTKEY_KEYPAD }, - { KEYC_KP_STAR, "\033Oj", INPUTKEY_KEYPAD }, - { KEYC_KP_MINUS, "\033Om", INPUTKEY_KEYPAD }, - { KEYC_KP_SEVEN, "\033Ow", INPUTKEY_KEYPAD }, - { KEYC_KP_EIGHT, "\033Ox", INPUTKEY_KEYPAD }, - { KEYC_KP_NINE, "\033Oy", INPUTKEY_KEYPAD }, - { KEYC_KP_PLUS, "\033Ok", INPUTKEY_KEYPAD }, - { KEYC_KP_FOUR, "\033Ot", INPUTKEY_KEYPAD }, - { KEYC_KP_FIVE, "\033Ou", INPUTKEY_KEYPAD }, - { KEYC_KP_SIX, "\033Ov", INPUTKEY_KEYPAD }, - { KEYC_KP_ONE, "\033Oq", INPUTKEY_KEYPAD }, - { KEYC_KP_TWO, "\033Or", INPUTKEY_KEYPAD }, - { KEYC_KP_THREE, "\033Os", INPUTKEY_KEYPAD }, - { KEYC_KP_ENTER, "\033OM", INPUTKEY_KEYPAD }, - { KEYC_KP_ZERO, "\033Op", INPUTKEY_KEYPAD }, - { KEYC_KP_PERIOD, "\033On", INPUTKEY_KEYPAD }, - - { KEYC_KP_SLASH, "/", 0 }, - { KEYC_KP_STAR, "*", 0 }, - { KEYC_KP_MINUS, "-", 0 }, - { KEYC_KP_SEVEN, "7", 0 }, - { KEYC_KP_EIGHT, "8", 0 }, - { KEYC_KP_NINE, "9", 0 }, - { KEYC_KP_PLUS, "+", 0 }, - { KEYC_KP_FOUR, "4", 0 }, - { KEYC_KP_FIVE, "5", 0 }, - { KEYC_KP_SIX, "6", 0 }, - { KEYC_KP_ONE, "1", 0 }, - { KEYC_KP_TWO, "2", 0 }, - { KEYC_KP_THREE, "3", 0 }, - { KEYC_KP_ENTER, "\n", 0 }, - { KEYC_KP_ZERO, "0", 0 }, - { KEYC_KP_PERIOD, ".", 0 }, + /* Keys with an embedded modifier. */ + { .key = KEYC_F1|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_P" + }, + { .key = KEYC_F2|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_Q" + }, + { .key = KEYC_F3|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_R" + }, + { .key = KEYC_F4|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_S" + }, + { .key = KEYC_F5|KEYC_BUILD_MODIFIERS, + .data = "\033[15;_~" + }, + { .key = KEYC_F6|KEYC_BUILD_MODIFIERS, + .data = "\033[17;_~" + }, + { .key = KEYC_F7|KEYC_BUILD_MODIFIERS, + .data = "\033[18;_~" + }, + { .key = KEYC_F8|KEYC_BUILD_MODIFIERS, + .data = "\033[19;_~" + }, + { .key = KEYC_F9|KEYC_BUILD_MODIFIERS, + .data = "\033[20;_~" + }, + { .key = KEYC_F10|KEYC_BUILD_MODIFIERS, + .data = "\033[21;_~" + }, + { .key = KEYC_F11|KEYC_BUILD_MODIFIERS, + .data = "\033[23;_~" + }, + { .key = KEYC_F12|KEYC_BUILD_MODIFIERS, + .data = "\033[24;_~" + }, + { .key = KEYC_UP|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_A" + }, + { .key = KEYC_DOWN|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_B" + }, + { .key = KEYC_RIGHT|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_C" + }, + { .key = KEYC_LEFT|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_D" + }, + { .key = KEYC_HOME|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_H" + }, + { .key = KEYC_END|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_F" + }, + { .key = KEYC_PPAGE|KEYC_BUILD_MODIFIERS, + .data = "\033[5;_~" + }, + { .key = KEYC_NPAGE|KEYC_BUILD_MODIFIERS, + .data = "\033[6;_~" + }, + { .key = KEYC_IC|KEYC_BUILD_MODIFIERS, + .data = "\033[2;_~" + }, + { .key = KEYC_DC|KEYC_BUILD_MODIFIERS, + .data = "\033[3;_~" } }; +static const key_code input_key_modifiers[] = { + 0, + 0, + KEYC_SHIFT, + KEYC_META|KEYC_IMPLIED_META, + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META, + KEYC_CTRL, + KEYC_SHIFT|KEYC_CTRL, + KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL +}; + +/* Input key comparison function. */ +static int +input_key_cmp(struct input_key_entry *ike1, struct input_key_entry *ike2) +{ + if (ike1->key < ike2->key) + return (-1); + if (ike1->key > ike2->key) + return (1); + return (0); +} + +/* Look for key in tree. */ +static struct input_key_entry * +input_key_get (key_code key) +{ + struct input_key_entry entry = { .key = key }; + + return (RB_FIND(input_key_tree, &input_key_tree, &entry)); +} /* Split a character into two UTF-8 bytes. */ static size_t -input_split2(u_int c, u_char *dst) +input_key_split2(u_int c, u_char *dst) { if (c > 0x7f) { dst[0] = (c >> 6) | 0xc0; @@ -149,32 +377,65 @@ input_split2(u_int c, u_char *dst) return (1); } +/* Build input key tree. */ +void +input_key_build(void) +{ + struct input_key_entry *ike, *new; + u_int i, j; + char *data; + key_code key; + + for (i = 0; i < nitems(input_key_defaults); i++) { + ike = &input_key_defaults[i]; + if (~ike->key & KEYC_BUILD_MODIFIERS) { + RB_INSERT(input_key_tree, &input_key_tree, ike); + continue; + } + + for (j = 2; j < nitems(input_key_modifiers); j++) { + key = (ike->key & ~KEYC_BUILD_MODIFIERS); + data = xstrdup(ike->data); + data[strcspn(data, "_")] = '0' + j; + + new = xcalloc(1, sizeof *new); + new->key = key|input_key_modifiers[j]; + new->data = data; + RB_INSERT(input_key_tree, &input_key_tree, new); + } + } + + RB_FOREACH(ike, input_key_tree, &input_key_tree) { + log_debug("%s: 0x%llx (%s) is %s", __func__, ike->key, + key_string_lookup_key(ike->key, 1), ike->data); + } +} + /* Translate a key code into an output key sequence for a pane. */ int input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) { - log_debug("writing key 0x%llx (%s) to %%%u", key, - key_string_lookup_key(key), wp->id); + if (log_get_level() != 0) { + log_debug("writing key 0x%llx (%s) to %%%u", key, + key_string_lookup_key(key, 1), wp->id); + } if (KEYC_IS_MOUSE(key)) { if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) input_key_mouse(wp, m); return (0); } - return (input_key(wp, wp->screen, wp->event, key)); + return (input_key(wp->screen, wp->event, key)); } /* Translate a key code into an output key sequence. */ int -input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, - key_code key) +input_key(struct screen *s, struct bufferevent *bev, key_code key) { - const struct input_key_ent *ike; - u_int i; - size_t dlen; - char *out; - key_code justkey, newkey; - struct utf8_data ud; + struct input_key_entry *ike; + key_code justkey, newkey, outkey; + struct utf8_data ud; + char tmp[64], modifier; /* Mouse keys need a pane. */ if (KEYC_IS_MOUSE(key)) @@ -192,16 +453,16 @@ input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, newkey = options_get_number(global_options, "backspace"); if (newkey >= 0x7f) newkey = '\177'; - key = newkey|(key & KEYC_MASK_MOD); + key = newkey|(key & (KEYC_MASK_MODIFIERS|KEYC_MASK_FLAGS)); } /* * If this is a normal 7-bit key, just send it, with a leading escape * if necessary. If it is a UTF-8 key, split it and send it. */ - justkey = (key & ~(KEYC_XTERM|KEYC_ESCAPE)); + justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META)); if (justkey <= 0x7f) { - if (key & KEYC_ESCAPE) + if (key & KEYC_META) bufferevent_write(bev, "\033", 1); ud.data[0] = justkey; bufferevent_write(bev, &ud.data[0], 1); @@ -210,51 +471,69 @@ input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) return (-1); - if (key & KEYC_ESCAPE) + if (key & KEYC_META) bufferevent_write(bev, "\033", 1); bufferevent_write(bev, ud.data, ud.size); return (0); } /* - * Then try to look this up as an xterm key, if the flag to output them - * is set. + * Look up in the tree. If not in application keypad or cursor mode, + * remove the flags from the key. */ - if (wp == NULL || options_get_number(wp->window->options, "xterm-keys")) { - if ((out = xterm_keys_lookup(key)) != NULL) { - bufferevent_write(bev, out, strlen(out)); - free(out); - return (0); - } + if (~s->mode & MODE_KKEYPAD) + key &= ~KEYC_KEYPAD; + if (~s->mode & MODE_KCURSOR) + key &= ~KEYC_CURSOR; + ike = input_key_get(key); + if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META)) + ike = input_key_get(key & ~KEYC_META); + if (ike != NULL) { + log_debug("found key 0x%llx: \"%s\"", key, ike->data); + if (key & KEYC_META && (~key & KEYC_IMPLIED_META)) + bufferevent_write(bev, "\033", 1); + bufferevent_write(bev, ike->data, strlen(ike->data)); + return (0); } - key &= ~KEYC_XTERM; - /* Otherwise look the key up in the table. */ - for (i = 0; i < nitems(input_keys); i++) { - ike = &input_keys[i]; - - if ((ike->flags & INPUTKEY_KEYPAD) && (~s->mode & MODE_KKEYPAD)) - continue; - if ((ike->flags & INPUTKEY_CURSOR) && (~s->mode & MODE_KCURSOR)) - continue; - - if ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key) - break; - if (ike->key == key) - break; + /* No builtin key sequence; construct an extended key sequence. */ + if (~s->mode & MODE_KEXTENDED) { + if ((key & KEYC_MASK_MODIFIERS) == KEYC_CTRL && + (key & KEYC_MASK_KEY) < KEYC_BASE) + return (input_key(s, bev, key & ~KEYC_CTRL)); + goto missing; } - if (i == nitems(input_keys)) { - log_debug("key 0x%llx missing", key); - return (-1); + outkey = (key & KEYC_MASK_KEY); + switch (key & KEYC_MASK_MODIFIERS) { + case KEYC_SHIFT: + modifier = '2'; + break; + case KEYC_META: + modifier = '3'; + break; + case KEYC_SHIFT|KEYC_META: + modifier = '4'; + break; + case KEYC_CTRL: + modifier = '5'; + break; + case KEYC_SHIFT|KEYC_CTRL: + modifier = '6'; + break; + case KEYC_META|KEYC_CTRL: + modifier = '7'; + break; + case KEYC_SHIFT|KEYC_META|KEYC_CTRL: + modifier = '8'; + break; } - dlen = strlen(ike->data); - log_debug("found key 0x%llx: \"%s\"", key, ike->data); - - /* Prefix a \033 for escape. */ - if (key & KEYC_ESCAPE) - bufferevent_write(bev, "\033", 1); - bufferevent_write(bev, ike->data, dlen); + xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier); + bufferevent_write(bev, tmp, strlen(tmp)); return (0); + +missing: + log_debug("key 0x%llx missing", key); + return (-1); } /* Get mouse event string. */ @@ -309,9 +588,9 @@ input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y, if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) return (0); len = xsnprintf(buf, sizeof buf, "\033[M"); - len += input_split2(m->b + 32, &buf[len]); - len += input_split2(x + 33, &buf[len]); - len += input_split2(y + 33, &buf[len]); + len += input_key_split2(m->b + 32, &buf[len]); + len += input_key_split2(x + 33, &buf[len]); + len += input_key_split2(y + 33, &buf[len]); } else { if (m->b > 223) return (0); diff --git a/input.c b/input.c index 03b81c69..44b1b948 100644 --- a/input.c +++ b/input.c @@ -241,6 +241,8 @@ enum input_csi_type { INPUT_CSI_HPA, INPUT_CSI_ICH, INPUT_CSI_IL, + INPUT_CSI_MODOFF, + INPUT_CSI_MODSET, INPUT_CSI_RCP, INPUT_CSI_REP, INPUT_CSI_RM, @@ -289,7 +291,9 @@ static const struct input_table_entry input_csi_table[] = { { 'l', "", INPUT_CSI_RM }, { 'l', "?", INPUT_CSI_RM_PRIVATE }, { 'm', "", INPUT_CSI_SGR }, + { 'm', ">", INPUT_CSI_MODSET }, { 'n', "", INPUT_CSI_DSR }, + { 'n', ">", INPUT_CSI_MODOFF }, { 'q', " ", INPUT_CSI_DECSCUSR }, { 'q', ">", INPUT_CSI_XDA }, { 'r', "", INPUT_CSI_DECSTBM }, @@ -1380,6 +1384,19 @@ input_csi_dispatch(struct input_ctx *ictx) if (n != -1 && m != -1) screen_write_cursormove(sctx, m - 1, n - 1, 1); break; + case INPUT_CSI_MODSET: + n = input_get(ictx, 0, 0, 0); + m = input_get(ictx, 1, 0, 0); + if (n == 0 || (n == 4 && m == 0)) + screen_write_mode_clear(sctx, MODE_KEXTENDED); + else if (n == 4 && (m == 1 || m == 2)) + screen_write_mode_set(sctx, MODE_KEXTENDED); + break; + case INPUT_CSI_MODOFF: + n = input_get(ictx, 0, 0, 0); + if (n == 4) + screen_write_mode_clear(sctx, MODE_KEXTENDED); + break; case INPUT_CSI_WINOPS: input_csi_dispatch_winops(ictx); break; @@ -1593,7 +1610,7 @@ input_csi_dispatch(struct input_ctx *ictx) break; case INPUT_CSI_XDA: n = input_get(ictx, 0, 0, 0); - if (n != 0) + if (n == 0) input_reply(ictx, "\033P>|tmux %s\033\\", getversion()); break; diff --git a/options-table.c b/options-table.c index 4ac0d0c3..87670b12 100644 --- a/options-table.c +++ b/options-table.c @@ -252,6 +252,14 @@ const struct options_table_entry options_table[] = { "clients." }, + { .name = "extended-keys", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, + .default_num = 0, + .text = "Whether to request extended key sequences from terminals " + "that support it." + }, + { .name = "focus-events", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, @@ -1053,11 +1061,12 @@ const struct options_table_entry options_table[] = { "bottom." }, - { .name = "xterm-keys", + { .name = "xterm-keys", /* no longer used */ .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1, - .text = "Whether xterm-style function key sequences should be sent." + .text = "Whether xterm-style function key sequences should be sent. " + "This option is no longer used." }, /* Hook options. */ diff --git a/tmux.1 b/tmux.1 index 421f8889..9f687bd2 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3225,6 +3225,12 @@ sessions. .Op Ic on | off .Xc If enabled, the server will exit when there are no attached clients. +.It Xo Ic extended-keys +.Op Ic on | off +.Xc +When enabled, extended keys are requested from the terminal and if supported +are recognised by +.Nm . .It Xo Ic focus-events .Op Ic on | off .Xc @@ -4054,16 +4060,6 @@ option. .Xc If this option is set, searches will wrap around the end of the pane contents. The default is on. -.Pp -.It Xo Ic xterm-keys -.Op Ic on | off -.Xc -If this option is set, -.Nm -will generate -.Xr xterm 1 -style -function key sequences; these have a number included to indicate modifiers such -as Shift, Alt or Ctrl. .El .Pp Available pane options are: @@ -5754,6 +5750,8 @@ Disable and enable bracketed paste. These are set automatically if the .Em XT capability is present. +.It Em \&Dseks , \&Eneks +Disable and enable extended keys. .It Em \&Dsfcs , \&Enfcs Disable and enable focus reporting. These are set automatically if the diff --git a/tty-features.c b/tty-features.c index 30d3d1a0..26344b90 100644 --- a/tty-features.c +++ b/tty-features.c @@ -190,6 +190,18 @@ static const struct tty_feature tty_feature_sync = { 0 }; +/* Terminal supports extended keys. */ +static const char *tty_feature_extkeys_capabilities[] = { + "Eneks=\\E[>4;1m", + "Dseks=\\E[>4m", + NULL +}; +static const struct tty_feature tty_feature_extkeys = { + "extkeys", + tty_feature_extkeys_capabilities, + 0 +}; + /* Terminal supports DECSLRM margins. */ static const char *tty_feature_margins_capabilities[] = { "Enmg=\\E[?69h", @@ -218,6 +230,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_ccolour, &tty_feature_clipboard, &tty_feature_cstyle, + &tty_feature_extkeys, &tty_feature_focus, &tty_feature_margins, &tty_feature_overline, @@ -321,7 +334,7 @@ tty_default_features(int *feat, const char *name, u_int version) } table[] = { #define TTY_FEATURES_BASE_MODERN_XTERM "256,RGB,bpaste,clipboard,strikethrough,title" { .name = "mintty", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,margins,overline" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,margins,overline" }, { .name = "tmux", .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,overline,usstyle" @@ -333,7 +346,7 @@ tty_default_features(int *feat, const char *name, u_int version) .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,margins,sync" }, { .name = "XTerm", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,margins,rectfill" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,focus,margins,rectfill" } }; u_int i; diff --git a/tty-keys.c b/tty-keys.c index dc064a17..5a2b3817 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -46,6 +47,8 @@ static struct tty_key *tty_keys_find(struct tty *, const char *, size_t, static int tty_keys_next1(struct tty *, const char *, size_t, key_code *, size_t *, int); static void tty_keys_callback(int, short, void *); +static int tty_keys_extended_key(struct tty *, const char *, size_t, + size_t *, key_code *); static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *, struct mouse_event *); static int tty_keys_clipboard(struct tty *, const char *, size_t, @@ -69,33 +72,33 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { * put the terminal into keypad_xmit mode. Translation of numbers * mode/applications mode is done in input-keys.c. */ - { "\033Oo", KEYC_KP_SLASH }, - { "\033Oj", KEYC_KP_STAR }, - { "\033Om", KEYC_KP_MINUS }, - { "\033Ow", KEYC_KP_SEVEN }, - { "\033Ox", KEYC_KP_EIGHT }, - { "\033Oy", KEYC_KP_NINE }, - { "\033Ok", KEYC_KP_PLUS }, - { "\033Ot", KEYC_KP_FOUR }, - { "\033Ou", KEYC_KP_FIVE }, - { "\033Ov", KEYC_KP_SIX }, - { "\033Oq", KEYC_KP_ONE }, - { "\033Or", KEYC_KP_TWO }, - { "\033Os", KEYC_KP_THREE }, - { "\033OM", KEYC_KP_ENTER }, - { "\033Op", KEYC_KP_ZERO }, - { "\033On", KEYC_KP_PERIOD }, + { "\033Oo", KEYC_KP_SLASH|KEYC_KEYPAD }, + { "\033Oj", KEYC_KP_STAR|KEYC_KEYPAD }, + { "\033Om", KEYC_KP_MINUS|KEYC_KEYPAD }, + { "\033Ow", KEYC_KP_SEVEN|KEYC_KEYPAD }, + { "\033Ox", KEYC_KP_EIGHT|KEYC_KEYPAD }, + { "\033Oy", KEYC_KP_NINE|KEYC_KEYPAD }, + { "\033Ok", KEYC_KP_PLUS|KEYC_KEYPAD }, + { "\033Ot", KEYC_KP_FOUR|KEYC_KEYPAD }, + { "\033Ou", KEYC_KP_FIVE|KEYC_KEYPAD }, + { "\033Ov", KEYC_KP_SIX|KEYC_KEYPAD }, + { "\033Oq", KEYC_KP_ONE|KEYC_KEYPAD }, + { "\033Or", KEYC_KP_TWO|KEYC_KEYPAD }, + { "\033Os", KEYC_KP_THREE|KEYC_KEYPAD }, + { "\033OM", KEYC_KP_ENTER|KEYC_KEYPAD }, + { "\033Op", KEYC_KP_ZERO|KEYC_KEYPAD }, + { "\033On", KEYC_KP_PERIOD|KEYC_KEYPAD }, /* Arrow keys. */ - { "\033OA", KEYC_UP }, - { "\033OB", KEYC_DOWN }, - { "\033OC", KEYC_RIGHT }, - { "\033OD", KEYC_LEFT }, + { "\033OA", KEYC_UP|KEYC_CURSOR }, + { "\033OB", KEYC_DOWN|KEYC_CURSOR }, + { "\033OC", KEYC_RIGHT|KEYC_CURSOR }, + { "\033OD", KEYC_LEFT|KEYC_CURSOR }, - { "\033[A", KEYC_UP }, - { "\033[B", KEYC_DOWN }, - { "\033[C", KEYC_RIGHT }, - { "\033[D", KEYC_LEFT }, + { "\033[A", KEYC_UP|KEYC_CURSOR }, + { "\033[B", KEYC_DOWN|KEYC_CURSOR }, + { "\033[C", KEYC_RIGHT|KEYC_CURSOR }, + { "\033[D", KEYC_LEFT|KEYC_CURSOR }, /* Other (xterm) "cursor" keys. */ { "\033OH", KEYC_HOME }, @@ -182,11 +185,59 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[201~", KEYC_PASTE_END }, }; +/* Default xterm keys. */ +struct tty_default_key_xterm { + const char *template; + key_code key; +}; +static const struct tty_default_key_xterm tty_default_xterm_keys[] = { + { "\033[1;_P", KEYC_F1 }, + { "\033O1;_P", KEYC_F1 }, + { "\033O_P", KEYC_F1 }, + { "\033[1;_Q", KEYC_F2 }, + { "\033O1;_Q", KEYC_F2 }, + { "\033O_Q", KEYC_F2 }, + { "\033[1;_R", KEYC_F3 }, + { "\033O1;_R", KEYC_F3 }, + { "\033O_R", KEYC_F3 }, + { "\033[1;_S", KEYC_F4 }, + { "\033O1;_S", KEYC_F4 }, + { "\033O_S", KEYC_F4 }, + { "\033[15;_~", KEYC_F5 }, + { "\033[17;_~", KEYC_F6 }, + { "\033[18;_~", KEYC_F7 }, + { "\033[19;_~", KEYC_F8 }, + { "\033[20;_~", KEYC_F9 }, + { "\033[21;_~", KEYC_F10 }, + { "\033[23;_~", KEYC_F11 }, + { "\033[24;_~", KEYC_F12 }, + { "\033[1;_A", KEYC_UP }, + { "\033[1;_B", KEYC_DOWN }, + { "\033[1;_C", KEYC_RIGHT }, + { "\033[1;_D", KEYC_LEFT }, + { "\033[1;_H", KEYC_HOME }, + { "\033[1;_F", KEYC_END }, + { "\033[5;_~", KEYC_PPAGE }, + { "\033[6;_~", KEYC_NPAGE }, + { "\033[2;_~", KEYC_IC }, + { "\033[3;_~", KEYC_DC }, +}; +static const key_code tty_default_xterm_modifiers[] = { + 0, + 0, + KEYC_SHIFT, + KEYC_META|KEYC_IMPLIED_META, + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META, + KEYC_CTRL, + KEYC_SHIFT|KEYC_CTRL, + KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL +}; + /* - * Default terminfo(5) keys. Any keys that have builtin modifiers - * (that is, where the key itself contains the modifiers) has the - * KEYC_XTERM flag set so a leading escape is not treated as meta (and - * probably removed). + * Default terminfo(5) keys. Any keys that have builtin modifiers (that is, + * where the key itself contains the modifiers) has the KEYC_XTERM flag set so + * a leading escape is not treated as meta (and probably removed). */ struct tty_default_key_code { enum tty_code_code code; @@ -207,61 +258,61 @@ static const struct tty_default_key_code tty_default_code_keys[] = { { TTYC_KF11, KEYC_F11 }, { TTYC_KF12, KEYC_F12 }, - { TTYC_KF13, KEYC_F1|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF14, KEYC_F2|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF15, KEYC_F3|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF16, KEYC_F4|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF17, KEYC_F5|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF18, KEYC_F6|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF19, KEYC_F7|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF20, KEYC_F8|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF21, KEYC_F9|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF22, KEYC_F10|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF23, KEYC_F11|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF24, KEYC_F12|KEYC_SHIFT|KEYC_XTERM }, + { TTYC_KF13, KEYC_F1|KEYC_SHIFT }, + { TTYC_KF14, KEYC_F2|KEYC_SHIFT }, + { TTYC_KF15, KEYC_F3|KEYC_SHIFT }, + { TTYC_KF16, KEYC_F4|KEYC_SHIFT }, + { TTYC_KF17, KEYC_F5|KEYC_SHIFT }, + { TTYC_KF18, KEYC_F6|KEYC_SHIFT }, + { TTYC_KF19, KEYC_F7|KEYC_SHIFT }, + { TTYC_KF20, KEYC_F8|KEYC_SHIFT }, + { TTYC_KF21, KEYC_F9|KEYC_SHIFT }, + { TTYC_KF22, KEYC_F10|KEYC_SHIFT }, + { TTYC_KF23, KEYC_F11|KEYC_SHIFT }, + { TTYC_KF24, KEYC_F12|KEYC_SHIFT }, - { TTYC_KF25, KEYC_F1|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF26, KEYC_F2|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF27, KEYC_F3|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF28, KEYC_F4|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF29, KEYC_F5|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF30, KEYC_F6|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF31, KEYC_F7|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF32, KEYC_F8|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF33, KEYC_F9|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF34, KEYC_F10|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF35, KEYC_F11|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF36, KEYC_F12|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KF25, KEYC_F1|KEYC_CTRL }, + { TTYC_KF26, KEYC_F2|KEYC_CTRL }, + { TTYC_KF27, KEYC_F3|KEYC_CTRL }, + { TTYC_KF28, KEYC_F4|KEYC_CTRL }, + { TTYC_KF29, KEYC_F5|KEYC_CTRL }, + { TTYC_KF30, KEYC_F6|KEYC_CTRL }, + { TTYC_KF31, KEYC_F7|KEYC_CTRL }, + { TTYC_KF32, KEYC_F8|KEYC_CTRL }, + { TTYC_KF33, KEYC_F9|KEYC_CTRL }, + { TTYC_KF34, KEYC_F10|KEYC_CTRL }, + { TTYC_KF35, KEYC_F11|KEYC_CTRL }, + { TTYC_KF36, KEYC_F12|KEYC_CTRL }, - { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL }, - { TTYC_KF49, KEYC_F1|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF50, KEYC_F2|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF51, KEYC_F3|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF52, KEYC_F4|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF53, KEYC_F5|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF54, KEYC_F6|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF55, KEYC_F7|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF56, KEYC_F8|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF57, KEYC_F9|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF58, KEYC_F10|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF59, KEYC_F11|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF60, KEYC_F12|KEYC_ESCAPE|KEYC_XTERM }, + { TTYC_KF49, KEYC_F1|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF50, KEYC_F2|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF51, KEYC_F3|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF52, KEYC_F4|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF53, KEYC_F5|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF54, KEYC_F6|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF55, KEYC_F7|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF56, KEYC_F8|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF57, KEYC_F9|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF58, KEYC_F10|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF59, KEYC_F11|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF60, KEYC_F12|KEYC_META|KEYC_IMPLIED_META }, - { TTYC_KF61, KEYC_F1|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF62, KEYC_F2|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF63, KEYC_F3|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, + { TTYC_KF61, KEYC_F1|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, + { TTYC_KF62, KEYC_F2|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, + { TTYC_KF63, KEYC_F3|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, { TTYC_KICH1, KEYC_IC }, { TTYC_KDCH1, KEYC_DC }, @@ -272,74 +323,74 @@ static const struct tty_default_key_code tty_default_code_keys[] = { { TTYC_KCBT, KEYC_BTAB }, /* Arrow keys from terminfo. */ - { TTYC_KCUU1, KEYC_UP }, - { TTYC_KCUD1, KEYC_DOWN }, - { TTYC_KCUB1, KEYC_LEFT }, - { TTYC_KCUF1, KEYC_RIGHT }, + { TTYC_KCUU1, KEYC_UP|KEYC_CURSOR }, + { TTYC_KCUD1, KEYC_DOWN|KEYC_CURSOR }, + { TTYC_KCUB1, KEYC_LEFT|KEYC_CURSOR }, + { TTYC_KCUF1, KEYC_RIGHT|KEYC_CURSOR }, /* Key and modifier capabilities. */ - { TTYC_KDC2, KEYC_DC|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDC3, KEYC_DC|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KDC5, KEYC_DC|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDC7, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIND, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDN3, KEYC_DOWN|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDN7, KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KEND2, KEYC_END|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KEND3, KEYC_END|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KEND5, KEYC_END|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KEND7, KEYC_END|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KHOM3, KEYC_HOME|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KHOM7, KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIC2, KEYC_IC|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KIC3, KEYC_IC|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KIC5, KEYC_IC|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIC7, KEYC_IC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KLFT3, KEYC_LEFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KLFT7, KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KNXT3, KEYC_NPAGE|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KNXT7, KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KPRV3, KEYC_PPAGE|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KPRV7, KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KRIT3, KEYC_RIGHT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRIT7, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRI, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KUP2, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KUP3, KEYC_UP|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KUP5, KEYC_UP|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KUP7, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KDC2, KEYC_DC|KEYC_SHIFT }, + { TTYC_KDC3, KEYC_DC|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDC5, KEYC_DC|KEYC_CTRL }, + { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KDC7, KEYC_DC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KIND, KEYC_DOWN|KEYC_SHIFT }, + { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT }, + { TTYC_KDN3, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL }, + { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KDN7, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KEND2, KEYC_END|KEYC_SHIFT }, + { TTYC_KEND3, KEYC_END|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KEND5, KEYC_END|KEYC_CTRL }, + { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KEND7, KEYC_END|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT }, + { TTYC_KHOM3, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL }, + { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KHOM7, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KIC2, KEYC_IC|KEYC_SHIFT }, + { TTYC_KIC3, KEYC_IC|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KIC5, KEYC_IC|KEYC_CTRL }, + { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KIC7, KEYC_IC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT }, + { TTYC_KLFT3, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL }, + { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KLFT7, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT }, + { TTYC_KNXT3, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL }, + { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KNXT7, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT }, + { TTYC_KPRV3, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL }, + { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KPRV7, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT }, + { TTYC_KRIT3, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL }, + { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KRIT7, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KRI, KEYC_UP|KEYC_SHIFT }, + { TTYC_KUP2, KEYC_UP|KEYC_SHIFT }, + { TTYC_KUP3, KEYC_UP|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KUP5, KEYC_UP|KEYC_CTRL }, + { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KUP7, KEYC_UP|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, }; /* Add key to tree. */ @@ -350,7 +401,7 @@ tty_keys_add(struct tty *tty, const char *s, key_code key) size_t size; const char *keystr; - keystr = key_string_lookup_key(key); + keystr = key_string_lookup_key(key, 1); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { log_debug("new key %s: 0x%llx (%s)", s, key, keystr); tty_keys_add1(&tty->key_tree, s, key); @@ -403,17 +454,30 @@ void tty_keys_build(struct tty *tty) { const struct tty_default_key_raw *tdkr; + const struct tty_default_key_xterm *tdkx; const struct tty_default_key_code *tdkc; - u_int i; + u_int i, j; const char *s; struct options_entry *o; struct options_array_item *a; union options_value *ov; + char copy[16]; + key_code key; if (tty->key_tree != NULL) tty_keys_free(tty); tty->key_tree = NULL; + for (i = 0; i < nitems(tty_default_xterm_keys); i++) { + tdkx = &tty_default_xterm_keys[i]; + for (j = 2; j < nitems(tty_default_xterm_modifiers); j++) { + strlcpy(copy, tdkx->template, sizeof copy); + copy[strcspn(copy, "_")] = '0' + j; + + key = tdkx->key|tty_default_xterm_modifiers[j]; + tty_keys_add(tty, copy, key); + } + } for (i = 0; i < nitems(tty_default_raw_keys); i++) { tdkr = &tty_default_raw_keys[i]; @@ -516,7 +580,6 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, enum utf8_state more; u_int i; wchar_t wc; - int n; log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len, (int)len, buf, expired); @@ -534,13 +597,6 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, return (0); } - /* Is this an an xterm(1) key? */ - n = xterm_keys_find(buf, len, size, key); - if (n == 0) - return (0); - if (n == 1 && !expired) - return (1); - /* Is this valid UTF-8? */ more = utf8_open(&ud, (u_char)*buf); if (more == UTF8_MORE) { @@ -637,6 +693,16 @@ tty_keys_next(struct tty *tty) goto partial_key; } + /* Is this an extended key press? */ + switch (tty_keys_extended_key(tty, buf, len, &size, &key)) { + case 0: /* yes */ + goto complete_key; + case -1: /* no, or not valid */ + break; + case 1: /* partial */ + goto partial_key; + } + first_key: /* Try to lookup complete key. */ n = tty_keys_next1(tty, buf, len, &key, &size, expired); @@ -653,7 +719,7 @@ first_key: /* Look for a key without the escape. */ n = tty_keys_next1(tty, buf + 1, len - 1, &key, &size, expired); if (n == 0) { /* found */ - if (key & KEYC_XTERM) { + if (key & KEYC_IMPLIED_META) { /* * We want the escape key as well as the xterm * key, because the xterm sequence implicitly @@ -665,7 +731,7 @@ first_key: size = 1; goto complete_key; } - key |= KEYC_ESCAPE; + key |= KEYC_META; size++; goto complete_key; } @@ -678,7 +744,7 @@ first_key: * escape). So pass it through even if the timer has not expired. */ if (*buf == '\033' && len >= 2) { - key = (u_char)buf[1] | KEYC_ESCAPE; + key = (u_char)buf[1] | KEYC_META; size = 2; } else { key = (u_char)buf[0]; @@ -723,7 +789,7 @@ complete_key: */ bspace = tty->tio.c_cc[VERASE]; if (bspace != _POSIX_VDISABLE && (key & KEYC_MASK_KEY) == bspace) - key = (key & KEYC_MASK_MOD) | KEYC_BSPACE; + key = (key & KEYC_MASK_MODIFIERS)|KEYC_BSPACE; /* Remove data from buffer. */ evbuffer_drain(tty->in, size); @@ -774,6 +840,96 @@ tty_keys_callback(__unused int fd, __unused short events, void *data) } } +/* + * Handle extended key input. This has two forms: \033[27;m;k~ and \033[k;mu, + * where k is key as a number and m is a modifier. Returns 0 for success, -1 + * for failure, 1 for partial; + */ +static int +tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, + size_t *size, key_code *key) +{ + struct client *c = tty->client; + size_t end; + u_int number, modifiers; + char tmp[64]; + + *size = 0; + + /* First two bytes are always \033[. */ + if (buf[0] != '\033') + return (-1); + if (len == 1) + return (1); + if (buf[1] != '[') + return (-1); + if (len == 2) + return (1); + + /* + * Look for a terminator. Stop at either '~' or anything that isn't a + * number or ';'. + */ + for (end = 2; end < len && end != sizeof tmp; end++) { + if (buf[end] == '~') + break; + if (!isdigit((u_char)buf[end]) && buf[end] != ';') + break; + } + if (end == len) + return (1); + if (end == sizeof tmp || (buf[end] != '~' && buf[end] != 'u')) + return (-1); + + /* Copy to the buffer. */ + memcpy(tmp, buf + 2, end); + tmp[end] = '\0'; + + /* Try to parse either form of key. */ + if (buf[end] == '~') { + if (sscanf(tmp, "27;%u;%u", &modifiers, &number) != 2) + return (-1); + } else { + if (sscanf(tmp ,"%u;%u", &number, &modifiers) != 2) + return (-1); + } + *size = end + 1; + + /* Store the key and modifiers. */ + *key = number; + switch (modifiers) { + case 2: + (*key) |= KEYC_SHIFT; + break; + case 3: + (*key) |= (KEYC_META|KEYC_IMPLIED_META); + break; + case 4: + (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META); + break; + case 5: + (*key) |= KEYC_CTRL; + break; + case 6: + (*key) |= (KEYC_SHIFT|KEYC_CTRL); + break; + case 7: + (*key) |= (KEYC_META|KEYC_CTRL); + break; + case 8: + (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); + break; + default: + *key = KEYC_NONE; + break; + } + if (log_get_level() != 0) { + log_debug("%s: extended key %.*s is %llx (%s)", c->name, + (int)*size, buf, *key, key_string_lookup_key(*key, 1)); + } + return (0); +} + /* * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial * (probably a mouse sequence but need more data). @@ -1137,7 +1293,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, else if (strncmp(tmp, "tmux ", 5) == 0) tty_default_features(&c->term_features, "tmux", 0); else if (strncmp(tmp, "XTerm(", 6) == 0) - tty_default_features(&c->term_features, "xterm", 0); + tty_default_features(&c->term_features, "XTerm", 0); else if (strncmp(tmp, "mintty ", 7) == 0) tty_default_features(&c->term_features, "mintty", 0); log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); diff --git a/tty-term.c b/tty-term.c index 67ca1bac..76c4fb57 100644 --- a/tty-term.c +++ b/tty-term.c @@ -83,6 +83,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DSEKS] = { TTYCODE_STRING, "Dseks" }, [TTYC_DSFCS] = { TTYCODE_STRING, "Dsfcs" }, [TTYC_DSBP] = { TTYCODE_STRING, "Dsbp" }, [TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" }, @@ -93,6 +94,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_EL] = { TTYCODE_STRING, "el" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, [TTYC_ENBP] = { TTYCODE_STRING, "Enbp" }, + [TTYC_ENEKS] = { TTYCODE_STRING, "Eneks" }, [TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" }, [TTYC_ENMG] = { TTYCODE_STRING, "Enmg" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, diff --git a/tty.c b/tty.c index 99996dfa..770c8b78 100644 --- a/tty.c +++ b/tty.c @@ -286,6 +286,8 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data) struct client *c = tty->client; log_debug("%s: start timer fired", c->name); + if ((tty->flags & (TTY_HAVEDA|TTY_HAVEXDA)) == 0) + tty_update_features(tty); tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); } @@ -329,13 +331,6 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1006l\033[?1005l"); } - if (options_get_number(global_options, "focus-events")) { - tty->flags |= TTY_FOCUS; - tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); - } - if (tty->term->flags & TERM_VT100LIKE) - tty_puts(tty, "\033[?7727h"); - evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); @@ -415,12 +410,10 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, "\033[?1006l\033[?1005l"); } - if (tty->flags & TTY_FOCUS) { - tty->flags &= ~TTY_FOCUS; - tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); - } if (tty->term->flags & TERM_VT100LIKE) tty_raw(tty, "\033[?7727l"); + tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); + tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS)); if (tty_use_margin(tty)) tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); @@ -471,6 +464,12 @@ tty_update_features(struct tty *tty) if (tty_use_margin(tty)) tty_putcode(tty, TTYC_ENMG); + if (options_get_number(global_options, "extended-keys")) + tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS)); + if (options_get_number(global_options, "focus-events")) + tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); + if (tty->term->flags & TERM_VT100LIKE) + tty_puts(tty, "\033[?7727h"); } void From 844b363baf64fbaff91cf8fa01d4fd782e7274a8 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:45:55 +0000 Subject: [PATCH 0429/1006] On select-window, make this client the latest client for the window. --- cmd-select-window.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd-select-window.c b/cmd-select-window.c index 377e3633..c85f36be 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -85,6 +85,7 @@ static enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *c = cmdq_get_client(item); struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct winlink *wl = target->wl; @@ -141,6 +142,8 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) } cmdq_insert_hook(s, item, current, "after-select-window"); } + if (c->session != NULL) + s->curw->window->latest = c; recalculate_sizes(); return (CMD_RETURN_NORMAL); From 574a9e4b6c7a419b60668c30916327aa7e65d2c8 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 May 2020 16:50:55 +0000 Subject: [PATCH 0430/1006] Move lazy resize from the pane to the window, there is no point in resizing the window unless it is the current window, and if we do and don't resize the pane until later there are problems if the size changes from A to B then back to A. --- cmd-set-option.c | 1 - cmd-show-messages.c | 2 +- resize.c | 25 +++++++++++++---- server-client.c | 68 ++++++++++++++++++++++++--------------------- tmux.h | 57 ++++++++++++++++++++++--------------- window.c | 11 +++++--- 6 files changed, 98 insertions(+), 66 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index c6f83796..36579f29 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -18,7 +18,6 @@ #include -#include #include #include diff --git a/cmd-show-messages.c b/cmd-show-messages.c index deb0487c..ef5acf44 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -27,7 +27,7 @@ #include "tmux.h" /* - * Show client message log. + * Show message log. */ #define SHOW_MESSAGES_TEMPLATE \ diff --git a/resize.c b/resize.c index 15d146d8..68717e35 100644 --- a/resize.c +++ b/resize.c @@ -61,6 +61,7 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) tty_update_window_offset(w); server_redraw_window(w); notify_window("window-layout-changed", w); + w->flags &= ~WINDOW_RESIZE; } static int @@ -346,16 +347,30 @@ recalculate_size(struct window *w) changed = 0; break; } - if (changed && w->sx == sx && w->sy == sy) - changed = 0; + if (w->flags & WINDOW_RESIZE) { + if (changed && w->new_sx == sx && w->new_sy == sy) + changed = 0; + } else { + if (changed && w->sx == sx && w->sy == sy) + changed = 0; + } if (!changed) { tty_update_window_offset(w); return; } - log_debug("%s: @%u changed to %u,%u (%ux%u)", __func__, w->id, sx, sy, - xpixel, ypixel); - resize_window(w, sx, sy, xpixel, ypixel); + log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy); + if (type == WINDOW_SIZE_MANUAL) + resize_window(w, sx, sy, xpixel, ypixel); + else { + w->new_sx = sx; + w->new_sy = sy; + w->new_xpixel = xpixel; + w->new_ypixel = ypixel; + + w->flags |= WINDOW_RESIZE; + tty_update_window_offset(w); + } } void diff --git a/server-client.c b/server-client.c index 3ce393cd..3cd2083d 100644 --- a/server-client.c +++ b/server-client.c @@ -33,8 +33,9 @@ #include "tmux.h" static void server_client_free(int, short, void *); -static void server_client_check_focus(struct window_pane *); -static void server_client_check_resize(struct window_pane *); +static void server_client_check_pane_focus(struct window_pane *); +static void server_client_check_pane_resize(struct window_pane *); +static void server_client_check_window_resize(struct window *); static key_code server_client_check_mouse(struct client *, struct key_event *); static void server_client_repeat_timer(int, short, void *); static void server_client_click_timer(int, short, void *); @@ -1035,14 +1036,14 @@ have_event: out: /* Apply modifiers if any. */ if (b & MOUSE_MASK_META) - key |= KEYC_ESCAPE; + key |= KEYC_META; if (b & MOUSE_MASK_CTRL) key |= KEYC_CTRL; if (b & MOUSE_MASK_SHIFT) key |= KEYC_SHIFT; if (log_get_level() != 0) - log_debug("mouse key is %s", key_string_lookup_key (key)); + log_debug("mouse key is %s", key_string_lookup_key (key, 1)); return (key); } @@ -1174,7 +1175,7 @@ table_changed: * The prefix always takes precedence and forces a switch to the prefix * table, unless we are already there. */ - key0 = (key & ~KEYC_XTERM); + key0 = (key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)); if ((key0 == (key_code)options_get_number(s->options, "prefix") || key0 == (key_code)options_get_number(s->options, "prefix2")) && strcmp(table->name, "prefix") != 0) { @@ -1341,10 +1342,13 @@ server_client_loop(void) struct client *c; struct window *w; struct window_pane *wp; - struct winlink *wl; - struct session *s; - int focus, attached, resize; + int focus; + /* Check for window resize. This is done before redrawing. */ + RB_FOREACH(w, windows, &windows) + server_client_check_window_resize(w); + + /* Check clients. */ TAILQ_FOREACH(c, &clients, entry) { server_client_check_exit(c); if (c->session != NULL) { @@ -1356,34 +1360,14 @@ server_client_loop(void) /* * Any windows will have been redrawn as part of clients, so clear * their flags now. Also check pane focus and resize. - * - * As an optimization, panes in windows that are in an attached session - * but not the current window are not resized (this reduces the amount - * of work needed when, for example, resizing an X terminal a - * lot). Windows in no attached session are resized immediately since - * that is likely to have come from a command like split-window and be - * what the user wanted. */ focus = options_get_number(global_options, "focus-events"); RB_FOREACH(w, windows, &windows) { - attached = resize = 0; - TAILQ_FOREACH(wl, &w->winlinks, wentry) { - s = wl->session; - if (s->attached != 0) - attached = 1; - if (s->attached != 0 && s->curw == wl) { - resize = 1; - break; - } - } - if (!attached) - resize = 1; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { if (focus) - server_client_check_focus(wp); - if (resize) - server_client_check_resize(wp); + server_client_check_pane_focus(wp); + server_client_check_pane_resize(wp); } wp->flags &= ~PANE_REDRAW; } @@ -1391,6 +1375,26 @@ server_client_loop(void) } } +/* Check if window needs to be resized. */ +static void +server_client_check_window_resize(struct window *w) +{ + struct winlink *wl; + + if (~w->flags & WINDOW_RESIZE) + return; + + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + if (wl->session->attached != 0 && wl->session->curw == wl) + break; + } + if (wl == NULL) + return; + + log_debug("%s: resizing window @%u", __func__, w->id); + resize_window(w, w->new_sx, w->new_sy, w->new_xpixel, w->new_ypixel); +} + /* Check if we need to force a resize. */ static int server_client_resize_force(struct window_pane *wp) @@ -1472,7 +1476,7 @@ server_client_resize_event(__unused int fd, __unused short events, void *data) /* Check if pane should be resized. */ static void -server_client_check_resize(struct window_pane *wp) +server_client_check_pane_resize(struct window_pane *wp) { if (~wp->flags & PANE_RESIZE) return; @@ -1490,7 +1494,7 @@ server_client_check_resize(struct window_pane *wp) /* Check whether pane should be focused. */ static void -server_client_check_focus(struct window_pane *wp) +server_client_check_pane_focus(struct window_pane *wp) { struct client *c; int push; diff --git a/tmux.h b/tmux.h index a413e0cf..cdc57474 100644 --- a/tmux.h +++ b/tmux.h @@ -112,25 +112,31 @@ struct winlink; #define VISUAL_BOTH 2 /* Special key codes. */ -#define KEYC_NONE 0x00ff000000000ULL -#define KEYC_UNKNOWN 0x00fe000000000ULL -#define KEYC_BASE 0x0001000000000ULL -#define KEYC_USER 0x0002000000000ULL +#define KEYC_NONE 0x00ff000000000ULL +#define KEYC_UNKNOWN 0x00fe000000000ULL +#define KEYC_BASE 0x0001000000000ULL +#define KEYC_USER 0x0002000000000ULL /* Key modifier bits. */ -#define KEYC_ESCAPE 0x0100000000000ULL -#define KEYC_CTRL 0x0200000000000ULL -#define KEYC_SHIFT 0x0400000000000ULL -#define KEYC_XTERM 0x0800000000000ULL -#define KEYC_LITERAL 0x1000000000000ULL +#define KEYC_META 0x00100000000000ULL +#define KEYC_CTRL 0x00200000000000ULL +#define KEYC_SHIFT 0x00400000000000ULL + +/* Key flag bits. */ +#define KEYC_LITERAL 0x01000000000000ULL +#define KEYC_KEYPAD 0x02000000000000ULL +#define KEYC_CURSOR 0x04000000000000ULL +#define KEYC_IMPLIED_META 0x08000000000000ULL +#define KEYC_BUILD_MODIFIERS 0x10000000000000ULL + +/* Masks for key bits. */ +#define KEYC_MASK_MODIFIERS 0x00f00000000000ULL +#define KEYC_MASK_FLAGS 0xff000000000000ULL +#define KEYC_MASK_KEY 0x000fffffffffffULL /* Available user keys. */ #define KEYC_NUSER 1000 -/* Mask to obtain key w/o modifiers. */ -#define KEYC_MASK_MOD 0xff00000000000ULL -#define KEYC_MASK_KEY 0x00fffffffffffULL - /* Is this a mouse key? */ #define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) @@ -284,6 +290,7 @@ enum tty_code_code { TTYC_DL, TTYC_DL1, TTYC_DSBP, + TTYC_DSEKS, TTYC_DSFCS, TTYC_DSMG, TTYC_E3, @@ -293,6 +300,7 @@ enum tty_code_code { TTYC_EL1, TTYC_ENACS, TTYC_ENBP, + TTYC_ENEKS, TTYC_ENFCS, TTYC_ENMG, TTYC_FSL, @@ -569,8 +577,8 @@ struct msg_write_close { #define MODE_CURSOR 0x1 #define MODE_INSERT 0x2 #define MODE_KCURSOR 0x4 -#define MODE_KKEYPAD 0x8 /* set = application, clear = number */ -#define MODE_WRAP 0x10 /* whether lines wrap */ +#define MODE_KKEYPAD 0x8 +#define MODE_WRAP 0x10 #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 #define MODE_BLINKING 0x80 @@ -581,6 +589,7 @@ struct msg_write_close { #define MODE_MOUSE_ALL 0x1000 #define MODE_ORIGIN 0x2000 #define MODE_CRLF 0x4000 +#define MODE_KEXTENDED 0x8000 #define ALL_MODES 0xffffff #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) @@ -1001,12 +1010,18 @@ struct window { u_int xpixel; u_int ypixel; + u_int new_sx; + u_int new_sy; + u_int new_xpixel; + u_int new_ypixel; + int flags; #define WINDOW_BELL 0x1 #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 #define WINDOW_ZOOMED 0x8 #define WINDOW_WASZOOMED 0x10 +#define WINDOW_RESIZE 0x20 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) int alerts_queued; @@ -1278,7 +1293,7 @@ struct tty { /* 0x8 unused */ #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 -#define TTY_FOCUS 0x40 +/* 0x40 unused */ #define TTY_BLOCK 0x80 #define TTY_HAVEDA 0x100 #define TTY_HAVEXDA 0x200 @@ -2282,7 +2297,7 @@ struct cmdq_item *key_bindings_dispatch(struct key_binding *, /* key-string.c */ key_code key_string_lookup_string(const char *); -const char *key_string_lookup_key(key_code); +const char *key_string_lookup_key(key_code, int); /* alerts.c */ void alerts_reset_all(void); @@ -2415,16 +2430,12 @@ void input_parse_screen(struct input_ctx *, struct screen *, screen_write_init_ctx_cb, void *, u_char *, size_t); /* input-key.c */ +void input_key_build(void); int input_key_pane(struct window_pane *, key_code, struct mouse_event *); -int input_key(struct window_pane *, struct screen *, struct bufferevent *, - key_code); +int input_key(struct screen *, struct bufferevent *, key_code); int input_key_get_mouse(struct screen *, struct mouse_event *, u_int, u_int, const char **, size_t *); -/* xterm-keys.c */ -char *xterm_keys_lookup(key_code); -int xterm_keys_find(const char *, size_t, size_t *, key_code *); - /* colour.c */ int colour_find_rgb(u_char, u_char, u_char); int colour_join_rgb(u_char, u_char, u_char); diff --git a/window.c b/window.c index b28a2257..2427bd6e 100644 --- a/window.c +++ b/window.c @@ -440,13 +440,15 @@ window_pane_send_resize(struct window_pane *wp, int yadjust) { struct window *w = wp->window; struct winsize ws; + u_int sy = wp->sy + yadjust; if (wp->fd == -1) return; + log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, sy); memset(&ws, 0, sizeof ws); ws.ws_col = wp->sx; - ws.ws_row = wp->sy + yadjust; + ws.ws_row = sy; ws.ws_xpixel = w->xpixel * ws.ws_col; ws.ws_ypixel = w->ypixel * ws.ws_row; if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) @@ -991,7 +993,6 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); - wp->flags |= (PANE_RESIZE|PANE_RESIZED); } @@ -1135,8 +1136,10 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { - if (wme->mode->key != NULL && c != NULL) - wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); + if (wme->mode->key != NULL && c != NULL) { + key &= ~KEYC_MASK_FLAGS; + wme->mode->key(wme, c, s, wl, key, m); + } return (0); } From 2bc05db54f22ae9d1e0c73a6b42518fdc8bec81e Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 16 May 2020 18:42:53 +0100 Subject: [PATCH 0431/1006] remove vis.h: portable doesn't need this --- session.c | 1 - 1 file changed, 1 deletion(-) diff --git a/session.c b/session.c index 5324c083..87c5b83c 100644 --- a/session.c +++ b/session.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "tmux.h" From dc56b3cd32d7d76946d162b63d3823ddf7cc470d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 16 May 2020 20:27:00 +0100 Subject: [PATCH 0432/1006] No paths.h. --- popup.c | 1 - tools/cmp-cvs.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/popup.c b/popup.c index 87658a6f..6f2ab101 100644 --- a/popup.c +++ b/popup.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include diff --git a/tools/cmp-cvs.sh b/tools/cmp-cvs.sh index b17d6f82..048ad15c 100644 --- a/tools/cmp-cvs.sh +++ b/tools/cmp-cvs.sh @@ -4,7 +4,7 @@ rm diff.out touch diff.out for i in *.[ch]; do - diff -u -I'\$OpenBSD' $i ../../OpenBSD/tmux/$i >diff.tmp + diff -u -I'\$OpenBSD' $i /usr/src/usr.bin/tmux/$i >diff.tmp set -- `wc -l diff.tmp` [ $1 -eq 8 ] && continue echo $i From 8425084b8ada98f6465d99d8bc8cad91612e50c5 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 19 May 2020 10:59:09 +0000 Subject: [PATCH 0433/1006] Some other ctrl keys need to be translated with extended keys on. --- input-keys.c | 33 +++++++++++++++++++++++++++++---- tty-features.c | 2 +- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/input-keys.c b/input-keys.c index 19134c1a..7a922ab8 100644 --- a/input-keys.c +++ b/input-keys.c @@ -498,10 +498,35 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) /* No builtin key sequence; construct an extended key sequence. */ if (~s->mode & MODE_KEXTENDED) { - if ((key & KEYC_MASK_MODIFIERS) == KEYC_CTRL && - (key & KEYC_MASK_KEY) < KEYC_BASE) - return (input_key(s, bev, key & ~KEYC_CTRL)); - goto missing; + justkey = (key & KEYC_MASK_KEY); + if ((key & KEYC_MASK_MODIFIERS) != KEYC_CTRL) + goto missing; + switch (justkey) { + case ' ': + case '2': + key = 0||(key & ~KEYC_MASK_KEY); + break; + case '|': + key = 28|(key & ~KEYC_MASK_KEY); + break; + case '6': + key = 30|(key & ~KEYC_MASK_KEY); + break; + case '-': + case '/': + key = 31|(key & ~KEYC_MASK_KEY); + break; + case '?': + key = 127|(key & ~KEYC_MASK_KEY); + break; + default: + if (justkey >= 'A' && justkey <= '_') + key = (justkey - 'A')|(key & ~KEYC_MASK_KEY); + else if (justkey >= 'a' && justkey <= '~') + key = (justkey - 96)|(key & ~KEYC_MASK_KEY); + break; + } + return (input_key(s, bev, key & ~KEYC_CTRL)); } outkey = (key & KEYC_MASK_KEY); switch (key & KEYC_MASK_MODIFIERS) { diff --git a/tty-features.c b/tty-features.c index 26344b90..8f712f64 100644 --- a/tty-features.c +++ b/tty-features.c @@ -343,7 +343,7 @@ tty_default_features(int *feat, const char *name, u_int version) .features = "256,bpaste,ccolour,cstyle,title" }, { .name = "iTerm2", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,margins,sync" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,extkeys,margins,sync" }, { .name = "XTerm", .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,focus,margins,rectfill" From e10f5a72ce671f3096aa242827e25b1c4dbf1de4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 19 May 2020 12:34:34 +0100 Subject: [PATCH 0434/1006] Add FreeBSD CI, from Jan Beich. --- .github/travis/before-install.sh | 7 +++++++ .travis.yml | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/.github/travis/before-install.sh b/.github/travis/before-install.sh index 9954e583..a589a743 100644 --- a/.github/travis/before-install.sh +++ b/.github/travis/before-install.sh @@ -15,3 +15,10 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then musl-tools fi fi + +if [ "$TRAVIS_OS_NAME" = "freebsd" ]; then + sudo pkg install -y \ + automake \ + libevent \ + pkgconf +fi diff --git a/.travis.yml b/.travis.yml index 16a04a16..ea3442af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: c os: - linux + - freebsd - osx compiler: @@ -28,6 +29,19 @@ jobs: - os: osx compiler: clang env: BUILD=static + # No musl on FreeBSD + - os: freebsd + compiler: gcc + env: BUILD=musl + - os: freebsd + compiler: clang + env: BUILD=musl + - os: freebsd + compiler: gcc + env: BUILD=musl-static + - os: freebsd + compiler: clang + env: BUILD=musl-static # No musl on OS X - os: osx compiler: gcc From 2a9e2b556a310366c69f8a72e1c20ed0392f11fd Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 20 May 2020 06:13:09 +0000 Subject: [PATCH 0435/1006] Key strings need to include the cursor and keypad flags now since the output key lookup expects them already set. --- key-string.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/key-string.c b/key-string.c index 65f1afe5..79f80554 100644 --- a/key-string.c +++ b/key-string.c @@ -62,28 +62,28 @@ static const struct { { "Escape", '\033' }, /* Arrow keys. */ - { "Up", KEYC_UP }, - { "Down", KEYC_DOWN }, - { "Left", KEYC_LEFT }, - { "Right", KEYC_RIGHT }, + { "Up", KEYC_UP|KEYC_CURSOR }, + { "Down", KEYC_DOWN|KEYC_CURSOR }, + { "Left", KEYC_LEFT|KEYC_CURSOR }, + { "Right", KEYC_RIGHT|KEYC_CURSOR }, /* Numeric keypad. */ - { "KP/", KEYC_KP_SLASH }, - { "KP*", KEYC_KP_STAR }, - { "KP-", KEYC_KP_MINUS }, - { "KP7", KEYC_KP_SEVEN }, - { "KP8", KEYC_KP_EIGHT }, - { "KP9", KEYC_KP_NINE }, - { "KP+", KEYC_KP_PLUS }, - { "KP4", KEYC_KP_FOUR }, - { "KP5", KEYC_KP_FIVE }, - { "KP6", KEYC_KP_SIX }, - { "KP1", KEYC_KP_ONE }, - { "KP2", KEYC_KP_TWO }, - { "KP3", KEYC_KP_THREE }, - { "KPEnter", KEYC_KP_ENTER }, - { "KP0", KEYC_KP_ZERO }, - { "KP.", KEYC_KP_PERIOD }, + { "KP/", KEYC_KP_SLASH|KEYC_KEYPAD }, + { "KP*", KEYC_KP_STAR|KEYC_KEYPAD }, + { "KP-", KEYC_KP_MINUS|KEYC_KEYPAD }, + { "KP7", KEYC_KP_SEVEN|KEYC_KEYPAD }, + { "KP8", KEYC_KP_EIGHT|KEYC_KEYPAD }, + { "KP9", KEYC_KP_NINE|KEYC_KEYPAD }, + { "KP+", KEYC_KP_PLUS|KEYC_KEYPAD }, + { "KP4", KEYC_KP_FOUR|KEYC_KEYPAD }, + { "KP5", KEYC_KP_FIVE|KEYC_KEYPAD }, + { "KP6", KEYC_KP_SIX|KEYC_KEYPAD }, + { "KP1", KEYC_KP_ONE|KEYC_KEYPAD }, + { "KP2", KEYC_KP_TWO|KEYC_KEYPAD }, + { "KP3", KEYC_KP_THREE|KEYC_KEYPAD }, + { "KPEnter", KEYC_KP_ENTER|KEYC_KEYPAD }, + { "KP0", KEYC_KP_ZERO|KEYC_KEYPAD }, + { "KP.", KEYC_KP_PERIOD|KEYC_KEYPAD }, /* Mouse keys. */ KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1), From b53e60f4c6d1e16dab996a58bb3ebd14691c2922 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 20 May 2020 06:18:22 +0000 Subject: [PATCH 0436/1006] Remove a redundant if statement. --- cmd-refresh-client.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index c53a6a78..d31bd2cc 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -113,26 +113,24 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) server_client_set_flags(tc, args_get(args, 'f')); if (args_has(args, 'C')) { - if (args_has(args, 'C')) { - if (!(tc->flags & CLIENT_CONTROL)) { - cmdq_error(item, "not a control client"); - return (CMD_RETURN_ERROR); - } - size = args_get(args, 'C'); - if (sscanf(size, "%u,%u", &x, &y) != 2 && - sscanf(size, "%ux%u", &x, &y) != 2) { - cmdq_error(item, "bad size argument"); - return (CMD_RETURN_ERROR); - } - if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || - y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { - cmdq_error(item, "size too small or too big"); - return (CMD_RETURN_ERROR); - } - tty_set_size(&tc->tty, x, y, 0, 0); - tc->flags |= CLIENT_SIZECHANGED; - recalculate_sizes(); + if (~tc->flags & CLIENT_CONTROL) { + cmdq_error(item, "not a control client"); + return (CMD_RETURN_ERROR); } + size = args_get(args, 'C'); + if (sscanf(size, "%u,%u", &x, &y) != 2 && + sscanf(size, "%ux%u", &x, &y) != 2) { + cmdq_error(item, "bad size argument"); + return (CMD_RETURN_ERROR); + } + if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || + y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { + cmdq_error(item, "size too small or too big"); + return (CMD_RETURN_ERROR); + } + tty_set_size(&tc->tty, x, y, 0, 0); + tc->flags |= CLIENT_SIZECHANGED; + recalculate_sizes(); return (CMD_RETURN_NORMAL); } From 6bde1c183766d0637633c1460cf6b17b57bc0280 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 20 May 2020 07:11:45 +0000 Subject: [PATCH 0437/1006] Fix a couple more places where the key flags need to be masked off. --- key-bindings.c | 2 +- key-string.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index 59cfbb0d..a518fbd8 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -197,7 +197,7 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, } bd = xcalloc(1, sizeof *bd); - bd->key = key; + bd->key = (key & ~KEYC_MASK_FLAGS); if (note != NULL) bd->note = xstrdup(note); RB_INSERT(key_bindings, &table->key_bindings, bd); diff --git a/key-string.c b/key-string.c index 79f80554..1efb6e0b 100644 --- a/key-string.c +++ b/key-string.c @@ -339,7 +339,7 @@ key_string_lookup_key(key_code key, int with_flags) /* Try the key against the string table. */ for (i = 0; i < nitems(key_string_table); i++) { - if (key == key_string_table[i].key) + if (key == (key_string_table[i].key & KEYC_MASK_KEY)) break; } if (i != nitems(key_string_table)) { @@ -359,7 +359,7 @@ key_string_lookup_key(key_code key, int with_flags) /* Invalid keys are errors. */ if (key > 255) { - snprintf(out, sizeof out, "Invalid#%llx", key); + snprintf(out, sizeof out, "Invalid#%llx", saved); goto out; } From 98a18d064aaf46e0b97a375b8c5b2b9ff15cf3df Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 21 May 2020 08:19:55 +0100 Subject: [PATCH 0438/1006] Fix a regression test, size is not updated until end of event loop. --- regress/control-client-size.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/regress/control-client-size.sh b/regress/control-client-size.sh index 5847ede3..dc275e52 100644 --- a/regress/control-client-size.sh +++ b/regress/control-client-size.sh @@ -20,9 +20,9 @@ sleep 1 cat <$TMP ls -F':#{window_width} #{window_height}' refresh -C 100,50 -ls -F':#{window_width} #{window_height}' EOF grep ^: $TMP >$OUT +$TMUX ls -F':#{window_width} #{window_height}' >>$OUT printf ":80 24\n:100 50\n"|cmp -s $OUT - || exit 1 $TMUX kill-server 2>/dev/null @@ -31,18 +31,18 @@ sleep 1 cat <$TMP ls -F':#{window_width} #{window_height}' refresh -C 80,24 -ls -F':#{window_width} #{window_height}' EOF grep ^: $TMP >$OUT +$TMUX ls -F':#{window_width} #{window_height}' >>$OUT printf ":80 24\n:80 24\n"|cmp -s $OUT - || exit 1 $TMUX kill-server 2>/dev/null cat <$TMP ls -F':#{window_width} #{window_height}' refresh -C 80,24 -ls -F':#{window_width} #{window_height}' EOF grep ^: $TMP >$OUT +$TMUX ls -F':#{window_width} #{window_height}' >>$OUT printf ":100 50\n:80 24\n"|cmp -s $OUT - || exit 1 $TMUX kill-server 2>/dev/null From 31e3f2d530090793815d145a16a1ce3b469c4266 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 21 May 2020 07:24:13 +0000 Subject: [PATCH 0439/1006] Support code for control mode flow control: allow clients to have separate offsets (used and acknowleged) into the pane buffers; turn off reading from panes when no clients can accept the data; and add a -A flag to refresh-client to let clients turn receiving a pane on and off. --- cmd-pipe-pane.c | 26 ++++----- cmd-refresh-client.c | 70 ++++++++++++++++++----- control-notify.c | 34 ----------- control.c | 47 ++++++++++++++++ input.c | 9 +-- notify.c | 11 ---- server-client.c | 131 ++++++++++++++++++++++++++++++++++++++++++- spawn.c | 1 - tmux.1 | 21 ++++++- tmux.h | 36 +++++++++++- window.c | 66 ++++++++++++++++++---- 11 files changed, 357 insertions(+), 95 deletions(-) diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 185bdc12..d9d9f436 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -56,16 +56,17 @@ const struct cmd_entry cmd_pipe_pane_entry = { static enum cmd_retval cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct cmd_find_state *target = cmdq_get_target(item); - struct client *tc = cmdq_get_target_client(item); - struct window_pane *wp = target->wp; - struct session *s = target->s; - struct winlink *wl = target->wl; - char *cmd; - int old_fd, pipe_fd[2], null_fd, in, out; - struct format_tree *ft; - sigset_t set, oldset; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item); + struct window_pane *wp = target->wp; + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane_offset *wpo = &wp->pipe_offset; + char *cmd; + int old_fd, pipe_fd[2], null_fd, in, out; + struct format_tree *ft; + sigset_t set, oldset; /* Destroy the old pipe. */ old_fd = wp->pipe_fd; @@ -159,10 +160,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) close(pipe_fd[1]); wp->pipe_fd = pipe_fd[0]; - if (wp->fd != -1) - wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); - else - wp->pipe_off = 0; + memcpy(wpo, &wp->offset, sizeof *wpo); setblocking(wp->pipe_fd, 0); wp->pipe_event = bufferevent_new(wp->pipe_fd, diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index d31bd2cc..65537917 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -34,23 +34,55 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "cC:Df:F:lLRSt:U", 0, 1 }, - .usage = "[-cDlLRSU] [-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE - " [adjustment]", + .args = { "A:cC:Df:F:lLRSt:U", 0, 1 }, + .usage = "[-cDlLRSU] [-A pane:state] [-C XxY] [-f flags] " + CMD_TARGET_CLIENT_USAGE " [adjustment]", .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_refresh_client_exec }; +static void +cmd_refresh_client_update_offset(struct client *tc, const char *value) +{ + struct window_pane *wp; + struct client_offset *co; + char *copy, *colon; + u_int pane; + + if (*value != '%') + return; + copy = xstrdup(value); + if ((colon = strchr(copy, ':')) == NULL) + goto out; + *colon++ = '\0'; + + if (sscanf(copy, "%%%u", &pane) != 1) + goto out; + wp = window_pane_find_by_id(pane); + if (wp == NULL) + goto out; + + co = server_client_add_pane_offset(tc, wp); + if (strcmp(colon, "on") == 0) + co->flags &= ~CLIENT_OFFSET_OFF; + else if (strcmp(colon, "off") == 0) + co->flags |= CLIENT_OFFSET_OFF; + +out: + free(copy); +} + static enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct client *tc = cmdq_get_target_client(item); - struct tty *tty = &tc->tty; - struct window *w; - const char *size, *errstr; - u_int x, y, adjust; + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); + struct tty *tty = &tc->tty; + struct window *w; + const char *size, *errstr, *value; + u_int x, y, adjust; + struct args_value *av; if (args_has(args, 'c') || args_has(args, 'L') || @@ -112,11 +144,19 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'f')) server_client_set_flags(tc, args_get(args, 'f')); - if (args_has(args, 'C')) { - if (~tc->flags & CLIENT_CONTROL) { - cmdq_error(item, "not a control client"); - return (CMD_RETURN_ERROR); + if (args_has(args, 'A')) { + if (~tc->flags & CLIENT_CONTROL) + goto not_control_client; + value = args_first_value(args, 'A', &av); + while (value != NULL) { + cmd_refresh_client_update_offset(tc, value); + value = args_next_value(&av); } + return (CMD_RETURN_NORMAL); + } + if (args_has(args, 'C')) { + if (~tc->flags & CLIENT_CONTROL) + goto not_control_client; size = args_get(args, 'C'); if (sscanf(size, "%u,%u", &x, &y) != 2 && sscanf(size, "%ux%u", &x, &y) != 2) { @@ -142,4 +182,8 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) server_redraw_client(tc); } return (CMD_RETURN_NORMAL); + +not_control_client: + cmdq_error(item, "not a control client"); + return (CMD_RETURN_ERROR); } diff --git a/control-notify.c b/control-notify.c index a513c147..a1735d57 100644 --- a/control-notify.c +++ b/control-notify.c @@ -26,40 +26,6 @@ #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \ ((c) != NULL && ((c)->flags & CLIENT_CONTROL)) -void -control_notify_input(struct client *c, struct window_pane *wp, - const u_char *buf, size_t len) -{ - struct evbuffer *message; - u_int i; - - if (c->session == NULL) - return; - - if (c->flags & CLIENT_CONTROL_NOOUTPUT) - return; - - /* - * Only write input if the window pane is linked to a window belonging - * to the client's session. - */ - if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) { - message = evbuffer_new(); - if (message == NULL) - fatalx("out of memory"); - evbuffer_add_printf(message, "%%output %%%u ", wp->id); - for (i = 0; i < len; i++) { - if (buf[i] < ' ' || buf[i] == '\\') - evbuffer_add_printf(message, "\\%03o", buf[i]); - else - evbuffer_add_printf(message, "%c", buf[i]); - } - evbuffer_add(message, "", 1); - control_write(c, "%s", EVBUFFER_DATA(message)); - evbuffer_free(message); - } -} - void control_notify_pane_mode_changed(int pane) { diff --git a/control.c b/control.c index bdc89de4..b1bcd2c1 100644 --- a/control.c +++ b/control.c @@ -38,6 +38,53 @@ control_write(struct client *c, const char *fmt, ...) va_end(ap); } +/* Write output from a pane. */ +void +control_write_output(struct client *c, struct window_pane *wp) +{ + struct client_offset *co; + struct evbuffer *message; + u_char *new_data; + size_t new_size, i; + + if (c->flags & CLIENT_CONTROL_NOOUTPUT) + return; + + /* + * Only write input if the window pane is linked to a window belonging + * to the client's session. + */ + if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) + return; + + co = server_client_add_pane_offset(c, wp); + if (co->flags & CLIENT_OFFSET_OFF) { + window_pane_update_used_data(wp, &co->offset, SIZE_MAX, 1); + return; + } + new_data = window_pane_get_new_data(wp, &co->offset, &new_size); + if (new_size == 0) + return; + + message = evbuffer_new(); + if (message == NULL) + fatalx("out of memory"); + evbuffer_add_printf(message, "%%output %%%u ", wp->id); + + for (i = 0; i < new_size; i++) { + if (new_data[i] < ' ' || new_data[i] == '\\') + evbuffer_add_printf(message, "\\%03o", new_data[i]); + else + evbuffer_add_printf(message, "%c", new_data[i]); + } + evbuffer_add(message, "", 1); + + control_write(c, "%s", EVBUFFER_DATA(message)); + evbuffer_free(message); + + window_pane_update_used_data(wp, &co->offset, new_size, 1); +} + /* Control error callback. */ static enum cmd_retval control_error(struct cmdq_item *item, void *data) diff --git a/input.c b/input.c index 44b1b948..7bb69663 100644 --- a/input.c +++ b/input.c @@ -942,10 +942,12 @@ input_parse(struct input_ctx *ictx, u_char *buf, size_t len) void input_parse_pane(struct window_pane *wp) { - struct evbuffer *evb = wp->event->input; + void *new_data; + size_t new_size; - input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb)); - evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); + new_data = window_pane_get_new_data(wp, &wp->offset, &new_size); + input_parse_buffer(wp, new_data, new_size); + window_pane_update_used_data(wp, &wp->offset, new_size, 1); } /* Parse given input. */ @@ -960,7 +962,6 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) window_update_activity(wp->window); wp->flags |= PANE_CHANGED; - notify_input(wp, buf, len); /* NULL wp if there is a mode set as don't want to update the tty. */ if (TAILQ_EMPTY(&wp->modes)) diff --git a/notify.c b/notify.c index 54254354..a50eb264 100644 --- a/notify.c +++ b/notify.c @@ -209,17 +209,6 @@ notify_hook(struct cmdq_item *item, const char *name) notify_insert_hook(item, &ne); } -void -notify_input(struct window_pane *wp, const u_char *buf, size_t len) -{ - struct client *c; - - TAILQ_FOREACH(c, &clients, entry) { - if (c->flags & CLIENT_CONTROL) - control_notify_input(c, wp, buf, len); - } -} - void notify_client(const char *name, struct client *c) { diff --git a/server-client.c b/server-client.c index 3cd2083d..02abd73a 100644 --- a/server-client.c +++ b/server-client.c @@ -35,6 +35,7 @@ static void server_client_free(int, short, void *); static void server_client_check_pane_focus(struct window_pane *); static void server_client_check_pane_resize(struct window_pane *); +static void server_client_check_pane_buffer(struct window_pane *); static void server_client_check_window_resize(struct window *); static key_code server_client_check_mouse(struct client *, struct key_event *); static void server_client_repeat_timer(int, short, void *); @@ -70,6 +71,43 @@ server_client_window_cmp(struct client_window *cw1, } RB_GENERATE(client_windows, client_window, entry, server_client_window_cmp); +/* Compare client offsets. */ +static int +server_client_offset_cmp(struct client_offset *co1, struct client_offset *co2) +{ + if (co1->pane < co2->pane) + return (-1); + if (co1->pane > co2->pane) + return (1); + return (0); +} +RB_GENERATE(client_offsets, client_offset, entry, server_client_offset_cmp); + +/* Get pane offsets for this client. */ +struct client_offset * +server_client_get_pane_offset(struct client *c, struct window_pane *wp) +{ + struct client_offset co = { .pane = wp->id }; + + return (RB_FIND(client_offsets, &c->offsets, &co)); +} + +/* Add pane offsets for this client. */ +struct client_offset * +server_client_add_pane_offset(struct client *c, struct window_pane *wp) +{ + struct client_offset *co; + + co = server_client_get_pane_offset(c, wp); + if (co != NULL) + return (co); + co = xcalloc(1, sizeof *co); + co->pane = wp->id; + RB_INSERT(client_offsets, &c->offsets, co); + memcpy(&co->offset, &wp->offset, sizeof co->offset); + return (co); +} + /* Number of attached clients. */ u_int server_client_how_many(void) @@ -226,15 +264,14 @@ server_client_create(int fd) c->queue = cmdq_new(); RB_INIT(&c->windows); + RB_INIT(&c->offsets); + RB_INIT(&c->files); c->tty.fd = -1; c->tty.sx = 80; c->tty.sy = 24; status_init(c); - - RB_INIT(&c->files); - c->flags |= CLIENT_FOCUSED; c->keytable = key_bindings_get_table("root", 1); @@ -288,6 +325,7 @@ server_client_lost(struct client *c) { struct client_file *cf, *cf1; struct client_window *cw, *cw1; + struct client_offset *co, *co1; c->flags |= CLIENT_DEAD; @@ -303,6 +341,10 @@ server_client_lost(struct client *c) RB_REMOVE(client_windows, &c->windows, cw); free(cw); } + RB_FOREACH_SAFE(co, client_offsets, &c->offsets, co1) { + RB_REMOVE(client_offsets, &c->offsets, co); + free(co); + } TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); @@ -1368,6 +1410,7 @@ server_client_loop(void) if (focus) server_client_check_pane_focus(wp); server_client_check_pane_resize(wp); + server_client_check_pane_buffer(wp); } wp->flags &= ~PANE_REDRAW; } @@ -1492,6 +1535,88 @@ server_client_check_pane_resize(struct window_pane *wp) log_debug("%s: %%%u timer running", __func__, wp->id); } +/* Check pane buffer size. */ +static void +server_client_check_pane_buffer(struct window_pane *wp) +{ + struct evbuffer *evb = wp->event->input; + size_t minimum; + struct client *c; + struct client_offset *co; + int off = !TAILQ_EMPTY(&clients); + + /* + * Work out the minimum acknowledged size. This is the most that can be + * removed from the buffer. + */ + minimum = wp->offset.acknowledged; + if (wp->pipe_fd != -1 && wp->pipe_offset.acknowledged < minimum) + minimum = wp->pipe_offset.acknowledged; + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL) + continue; + if ((~c->flags & CLIENT_CONTROL) || + (c->flags & CLIENT_CONTROL_NOOUTPUT) || + (co = server_client_get_pane_offset(c, wp)) == NULL) { + off = 0; + continue; + } + if (~co->flags & CLIENT_OFFSET_OFF) + off = 0; + log_debug("%s: %s has %zu bytes used, %zu bytes acknowledged " + "for %%%u", __func__, c->name, co->offset.used, + co->offset.acknowledged, wp->id); + if (co->offset.acknowledged < minimum) + minimum = co->offset.acknowledged; + } + minimum -= wp->base_offset; + if (minimum == 0) + goto out; + + /* Drain the buffer. */ + log_debug("%s: %%%u has %zu minimum (of %zu) bytes acknowledged", + __func__, wp->id, minimum, EVBUFFER_LENGTH(evb)); + evbuffer_drain(evb, minimum); + + /* + * Adjust the base offset. If it would roll over, all the offsets into + * the buffer need to be adjusted. + */ + if (wp->base_offset > SIZE_MAX - minimum) { + log_debug("%s: %%%u base offset has wrapped", __func__, wp->id); + wp->offset.acknowledged -= wp->base_offset; + wp->offset.used -= wp->base_offset; + if (wp->pipe_fd != -1) { + wp->pipe_offset.acknowledged -= wp->base_offset; + wp->pipe_offset.used -= wp->base_offset; + } + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL || (~c->flags & CLIENT_CONTROL)) + continue; + co = server_client_get_pane_offset(c, wp); + if (co != NULL) { + co->offset.acknowledged -= wp->base_offset; + co->offset.used -= wp->base_offset; + } + } + wp->base_offset = minimum; + } else + wp->base_offset += minimum; + +out: + /* + * If there is data remaining, and there are no clients able to consume + * it, do not read any more. This is true when 1) there are attached + * clients 2) all the clients are control clients 3) all of them have + * either the OFF flag set, or are otherwise not able to accept any + * more data for this pane. + */ + if (off) + bufferevent_disable(wp->event, EV_READ); + else + bufferevent_enable(wp->event, EV_READ); +} + /* Check whether pane should be focused. */ static void server_client_check_pane_focus(struct window_pane *wp) diff --git a/spawn.c b/spawn.c index 01f19097..d5b52ffa 100644 --- a/spawn.c +++ b/spawn.c @@ -439,7 +439,6 @@ spawn_pane(struct spawn_context *sc, char **cause) _exit(1); complete: - new_wp->pipe_off = 0; new_wp->flags &= ~PANE_EXITED; sigprocmask(SIG_SETMASK, &oldset, NULL); diff --git a/tmux.1 b/tmux.1 index 9f687bd2..e7131829 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1251,6 +1251,7 @@ and sets an environment variable for the newly created session; it may be specified multiple times. .It Xo Ic refresh-client .Op Fl cDlLRSU +.Op Fl A Ar pane:state .Op Fl C Ar XxY .Op Fl f Ar flags .Op Fl t Ar target-client @@ -1295,7 +1296,25 @@ window, changing the current window in the attached session will reset it. .Pp .Fl C -sets the width and height of a control client. +sets the width and height of a control mode client. +.Fl A +informs +.Nm +of a control mode client's interest in a pane. +The argument is a pane ID (with leading +.Ql % ) , +a colon, then one of +.Ql on +or +.Ql off . +If +.Ql off , +.Nm +will not send output from the pane to the client and if all clients have turned +the pane off, will stop reading from the pane. +.Fl A +may be given multiple times. +.Pp .Fl f sets a comma-separated list of client flags, see .Ic attach-session . diff --git a/tmux.h b/tmux.h index cdc57474..cfb63e36 100644 --- a/tmux.h +++ b/tmux.h @@ -896,6 +896,12 @@ struct window_mode_entry { TAILQ_ENTRY (window_mode_entry) entry; }; +/* Offsets into pane buffer. */ +struct window_pane_offset { + size_t used; + size_t acknowledged; +}; + /* Child window structure. */ struct window_pane { u_int id; @@ -946,6 +952,8 @@ struct window_pane { int fd; struct bufferevent *event; + struct window_pane_offset offset; + size_t base_offset; struct event resize_timer; @@ -957,7 +965,7 @@ struct window_pane { int pipe_fd; struct bufferevent *pipe_event; - size_t pipe_off; + struct window_pane_offset pipe_offset; struct screen *screen; struct screen base; @@ -1541,6 +1549,18 @@ struct client_window { }; RB_HEAD(client_windows, client_window); +/* Client offsets. */ +struct client_offset { + u_int pane; + + struct window_pane_offset offset; + int flags; +#define CLIENT_OFFSET_OFF 0x1 + + RB_ENTRY(client_offset) entry; +}; +RB_HEAD(client_offsets, client_offset); + /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); @@ -1555,6 +1575,7 @@ struct client { struct cmdq_list *queue; struct client_windows windows; + struct client_offsets offsets; pid_t pid; int fd; @@ -1927,7 +1948,6 @@ char *format_trim_right(const char *, u_int); /* notify.c */ void notify_hook(struct cmdq_item *, const char *); -void notify_input(struct window_pane *, const u_char *, size_t); void notify_client(const char *, struct client *); void notify_session(const char *, struct session *); void notify_winlink(const char *, struct winlink *); @@ -2339,6 +2359,11 @@ void printflike(1, 2) server_add_message(const char *, ...); /* server-client.c */ RB_PROTOTYPE(client_windows, client_window, entry, server_client_window_cmp); +RB_PROTOTYPE(client_offsets, client_offset, entry, server_client_offset_cmp); +struct client_offset *server_client_get_pane_offset(struct client *, + struct window_pane *); +struct client_offset *server_client_add_pane_offset(struct client *, + struct window_pane *); u_int server_client_how_many(void); void server_client_set_overlay(struct client *, u_int, overlay_check_cb, overlay_mode_cb, overlay_draw_cb, overlay_key_cb, @@ -2684,6 +2709,12 @@ void winlink_clear_flags(struct winlink *); int winlink_shuffle_up(struct session *, struct winlink *); int window_pane_start_input(struct window_pane *, struct cmdq_item *, char **); +void *window_pane_get_new_data(struct window_pane *, + struct window_pane_offset *, size_t *); +void window_pane_update_used_data(struct window_pane *, + struct window_pane_offset *, size_t, int); +void window_pane_acknowledge_data(struct window_pane *, + struct window_pane_offset *, size_t); /* layout.c */ u_int layout_count_cells(struct layout_cell *); @@ -2800,6 +2831,7 @@ char *parse_window_name(const char *); /* control.c */ void control_start(struct client *); void printflike(2, 3) control_write(struct client *, const char *, ...); +void control_write_output(struct client *, struct window_pane *); /* control-notify.c */ void control_notify_input(struct client *, struct window_pane *, diff --git a/window.c b/window.c index 2427bd6e..c7a17551 100644 --- a/window.c +++ b/window.c @@ -881,7 +881,6 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->sy = wp->osx = sy; wp->pipe_fd = -1; - wp->pipe_off = 0; wp->pipe_event = NULL; screen_init(&wp->base, sx, sy, hlimit); @@ -933,22 +932,28 @@ window_pane_destroy(struct window_pane *wp) static void window_pane_read_callback(__unused struct bufferevent *bufev, void *data) { - struct window_pane *wp = data; - struct evbuffer *evb = wp->event->input; - size_t size = EVBUFFER_LENGTH(evb); - char *new_data; - size_t new_size; + struct window_pane *wp = data; + struct evbuffer *evb = wp->event->input; + struct window_pane_offset *wpo = &wp->pipe_offset; + size_t size = EVBUFFER_LENGTH(evb); + char *new_data; + size_t new_size; + struct client *c; - new_size = size - wp->pipe_off; - if (wp->pipe_fd != -1 && new_size > 0) { - new_data = EVBUFFER_DATA(evb) + wp->pipe_off; - bufferevent_write(wp->pipe_event, new_data, new_size); + if (wp->pipe_fd != -1) { + new_data = window_pane_get_new_data(wp, wpo, &new_size); + if (new_size > 0) { + bufferevent_write(wp->pipe_event, new_data, new_size); + window_pane_update_used_data(wp, wpo, new_size, 1); + } } log_debug("%%%u has %zu bytes", wp->id, size); + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL && c->flags & CLIENT_CONTROL) + control_write_output(c, wp); + } input_parse_pane(wp); - - wp->pipe_off = EVBUFFER_LENGTH(evb); } static void @@ -1541,3 +1546,40 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item, return (0); } + +void * +window_pane_get_new_data(struct window_pane *wp, + struct window_pane_offset *wpo, size_t *size) +{ + size_t used = wpo->used - wp->base_offset; + + *size = EVBUFFER_LENGTH(wp->event->input) - used; + return (EVBUFFER_DATA(wp->event->input) + used); +} + +void +window_pane_update_used_data(struct window_pane *wp, + struct window_pane_offset *wpo, size_t size, int acknowledge) +{ + size_t used = wpo->used - wp->base_offset; + + if (size > EVBUFFER_LENGTH(wp->event->input) - used) + size = EVBUFFER_LENGTH(wp->event->input) - used; + wpo->used += size; + + if (acknowledge) + window_pane_acknowledge_data(wp, wpo, size); +} + +void +window_pane_acknowledge_data(struct window_pane *wp, + struct window_pane_offset *wpo, size_t size) +{ + size_t acknowledged = wpo->acknowledged - wp->base_offset; + + if (size > EVBUFFER_LENGTH(wp->event->input) - acknowledged) + size = EVBUFFER_LENGTH(wp->event->input) - acknowledged; + wpo->acknowledged += size; + if (wpo->acknowledged > wpo->used) + wpo->acknowledged = wpo->used; +} From 2420bd8584e4eea6ccd8dd304ac55ae8ff55b227 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 21 May 2020 09:02:04 +0100 Subject: [PATCH 0440/1006] spawn.c: fix up bad merge --- spawn.c | 1 - 1 file changed, 1 deletion(-) diff --git a/spawn.c b/spawn.c index 5865a881..9f65b329 100644 --- a/spawn.c +++ b/spawn.c @@ -446,7 +446,6 @@ complete: } #endif - new_wp->pipe_off = 0; new_wp->flags &= ~PANE_EXITED; sigprocmask(SIG_SETMASK, &oldset, NULL); From 9a0763c3a06e589bd5d27046655603faea8c667f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 22 May 2020 11:07:04 +0000 Subject: [PATCH 0441/1006] Move client offset stuff into control.c since only control clients will need it. --- cmd-refresh-client.c | 6 +-- control.c | 125 +++++++++++++++++++++++++++++++++++++++++-- server-client.c | 84 +++++++++-------------------- tmux.h | 25 +++------ 4 files changed, 157 insertions(+), 83 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 65537917..4eb8417b 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -46,7 +46,6 @@ static void cmd_refresh_client_update_offset(struct client *tc, const char *value) { struct window_pane *wp; - struct client_offset *co; char *copy, *colon; u_int pane; @@ -63,11 +62,10 @@ cmd_refresh_client_update_offset(struct client *tc, const char *value) if (wp == NULL) goto out; - co = server_client_add_pane_offset(tc, wp); if (strcmp(colon, "on") == 0) - co->flags &= ~CLIENT_OFFSET_OFF; + control_set_pane_on(tc, wp); else if (strcmp(colon, "off") == 0) - co->flags |= CLIENT_OFFSET_OFF; + control_set_pane_off(tc, wp); out: free(copy); diff --git a/control.c b/control.c index b1bcd2c1..24c26fea 100644 --- a/control.c +++ b/control.c @@ -26,6 +26,124 @@ #include "tmux.h" +/* Control offsets. */ +struct control_offset { + u_int pane; + + struct window_pane_offset offset; + int flags; +#define CONTROL_OFFSET_OFF 0x1 + + RB_ENTRY(control_offset) entry; +}; +RB_HEAD(control_offsets, control_offset); + +/* Compare client offsets. */ +static int +control_offset_cmp(struct control_offset *co1, struct control_offset *co2) +{ + if (co1->pane < co2->pane) + return (-1); + if (co1->pane > co2->pane) + return (1); + return (0); +} +RB_GENERATE_STATIC(control_offsets, control_offset, entry, control_offset_cmp); + +/* Get pane offsets for this client. */ +static struct control_offset * +control_get_offset(struct client *c, struct window_pane *wp) +{ + struct control_offset co = { .pane = wp->id }; + + if (c->offsets == NULL) + return (NULL); + return (RB_FIND(control_offsets, c->offsets, &co)); +} + +/* Add pane offsets for this client. */ +static struct control_offset * +control_add_offset(struct client *c, struct window_pane *wp) +{ + struct control_offset *co; + + co = control_get_offset(c, wp); + if (co != NULL) + return (co); + + if (c->offsets == NULL) { + c->offsets = xmalloc(sizeof *c->offsets); + RB_INIT(c->offsets); + } + + co = xcalloc(1, sizeof *co); + co->pane = wp->id; + RB_INSERT(control_offsets, c->offsets, co); + memcpy(&co->offset, &wp->offset, sizeof co->offset); + return (co); +} + +/* Free control offsets. */ +void +control_free_offsets(struct client *c) +{ + struct control_offset *co, *co1; + + if (c->offsets == NULL) + return; + RB_FOREACH_SAFE(co, control_offsets, c->offsets, co1) { + RB_REMOVE(control_offsets, c->offsets, co); + free(co); + } + free(c->offsets); +} + +/* Get offsets for client. */ +struct window_pane_offset * +control_pane_offset(struct client *c, struct window_pane *wp, int *off) +{ + struct control_offset *co; + + if (c->flags & CLIENT_CONTROL_NOOUTPUT) { + *off = 0; + return (NULL); + } + + co = control_get_offset(c, wp); + if (co == NULL) { + *off = 0; + return (NULL); + } + if (co->flags & CONTROL_OFFSET_OFF) { + *off = 1; + return (NULL); + } + return (&co->offset); +} + +/* Set pane as on. */ +void +control_set_pane_on(struct client *c, struct window_pane *wp) +{ + struct control_offset *co; + + co = control_get_offset(c, wp); + if (co != NULL) { + co->flags &= ~CONTROL_OFFSET_OFF; + memcpy(&co->offset, &wp->offset, sizeof co->offset); + } +} + +/* Set pane as off. */ +void +control_set_pane_off(struct client *c, struct window_pane *wp) +{ + struct control_offset *co; + + co = control_add_offset(c, wp); + co->flags |= CONTROL_OFFSET_OFF; +} + /* Write a line. */ void control_write(struct client *c, const char *fmt, ...) @@ -42,7 +160,7 @@ control_write(struct client *c, const char *fmt, ...) void control_write_output(struct client *c, struct window_pane *wp) { - struct client_offset *co; + struct control_offset *co; struct evbuffer *message; u_char *new_data; size_t new_size, i; @@ -57,8 +175,8 @@ control_write_output(struct client *c, struct window_pane *wp) if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) return; - co = server_client_add_pane_offset(c, wp); - if (co->flags & CLIENT_OFFSET_OFF) { + co = control_add_offset(c, wp); + if (co->flags & CONTROL_OFFSET_OFF) { window_pane_update_used_data(wp, &co->offset, SIZE_MAX, 1); return; } @@ -133,6 +251,7 @@ control_callback(__unused struct client *c, __unused const char *path, } } +/* Initialize for control mode. */ void control_start(struct client *c) { diff --git a/server-client.c b/server-client.c index 02abd73a..1f0ac66f 100644 --- a/server-client.c +++ b/server-client.c @@ -71,43 +71,6 @@ server_client_window_cmp(struct client_window *cw1, } RB_GENERATE(client_windows, client_window, entry, server_client_window_cmp); -/* Compare client offsets. */ -static int -server_client_offset_cmp(struct client_offset *co1, struct client_offset *co2) -{ - if (co1->pane < co2->pane) - return (-1); - if (co1->pane > co2->pane) - return (1); - return (0); -} -RB_GENERATE(client_offsets, client_offset, entry, server_client_offset_cmp); - -/* Get pane offsets for this client. */ -struct client_offset * -server_client_get_pane_offset(struct client *c, struct window_pane *wp) -{ - struct client_offset co = { .pane = wp->id }; - - return (RB_FIND(client_offsets, &c->offsets, &co)); -} - -/* Add pane offsets for this client. */ -struct client_offset * -server_client_add_pane_offset(struct client *c, struct window_pane *wp) -{ - struct client_offset *co; - - co = server_client_get_pane_offset(c, wp); - if (co != NULL) - return (co); - co = xcalloc(1, sizeof *co); - co->pane = wp->id; - RB_INSERT(client_offsets, &c->offsets, co); - memcpy(&co->offset, &wp->offset, sizeof co->offset); - return (co); -} - /* Number of attached clients. */ u_int server_client_how_many(void) @@ -264,7 +227,6 @@ server_client_create(int fd) c->queue = cmdq_new(); RB_INIT(&c->windows); - RB_INIT(&c->offsets); RB_INIT(&c->files); c->tty.fd = -1; @@ -325,7 +287,6 @@ server_client_lost(struct client *c) { struct client_file *cf, *cf1; struct client_window *cw, *cw1; - struct client_offset *co, *co1; c->flags |= CLIENT_DEAD; @@ -341,10 +302,7 @@ server_client_lost(struct client *c) RB_REMOVE(client_windows, &c->windows, cw); free(cw); } - RB_FOREACH_SAFE(co, client_offsets, &c->offsets, co1) { - RB_REMOVE(client_offsets, &c->offsets, co); - free(co); - } + control_free_offsets(c); TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); @@ -1542,8 +1500,9 @@ server_client_check_pane_buffer(struct window_pane *wp) struct evbuffer *evb = wp->event->input; size_t minimum; struct client *c; - struct client_offset *co; - int off = !TAILQ_EMPTY(&clients); + struct window_pane_offset *wpo; + int off = 1, flag; + u_int attached_clients = 0; /* * Work out the minimum acknowledged size. This is the most that can be @@ -1555,20 +1514,28 @@ server_client_check_pane_buffer(struct window_pane *wp) TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL) continue; - if ((~c->flags & CLIENT_CONTROL) || - (c->flags & CLIENT_CONTROL_NOOUTPUT) || - (co = server_client_get_pane_offset(c, wp)) == NULL) { + attached_clients++; + + if (~c->flags & CLIENT_CONTROL) { off = 0; continue; } - if (~co->flags & CLIENT_OFFSET_OFF) + wpo = control_pane_offset(c, wp, &flag); + if (wpo == NULL) { off = 0; + continue; + } + if (!flag) + off = 0; + log_debug("%s: %s has %zu bytes used, %zu bytes acknowledged " - "for %%%u", __func__, c->name, co->offset.used, - co->offset.acknowledged, wp->id); - if (co->offset.acknowledged < minimum) - minimum = co->offset.acknowledged; + "for %%%u", __func__, c->name, wpo->used, wpo->acknowledged, + wp->id); + if (wpo->acknowledged < minimum) + minimum = wpo->acknowledged; } + if (attached_clients == 0) + off = 0; minimum -= wp->base_offset; if (minimum == 0) goto out; @@ -1593,10 +1560,10 @@ server_client_check_pane_buffer(struct window_pane *wp) TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL || (~c->flags & CLIENT_CONTROL)) continue; - co = server_client_get_pane_offset(c, wp); - if (co != NULL) { - co->offset.acknowledged -= wp->base_offset; - co->offset.used -= wp->base_offset; + wpo = control_pane_offset(c, wp, &flag); + if (wpo != NULL && !flag) { + wpo->acknowledged -= wp->base_offset; + wpo->used -= wp->base_offset; } } wp->base_offset = minimum; @@ -2394,9 +2361,10 @@ server_client_set_flags(struct client *c, const char *flags) c->flags &= ~flag; else c->flags |= flag; + if (flag == CLIENT_CONTROL_NOOUTPUT) + control_free_offsets(c); } free(copy); - } /* Get client flags. This is only flags useful to show to users. */ diff --git a/tmux.h b/tmux.h index cfb63e36..6965f132 100644 --- a/tmux.h +++ b/tmux.h @@ -45,6 +45,7 @@ struct cmdq_item; struct cmdq_list; struct cmdq_state; struct cmds; +struct control_offsets; struct environ; struct format_job_tree; struct format_tree; @@ -1549,18 +1550,6 @@ struct client_window { }; RB_HEAD(client_windows, client_window); -/* Client offsets. */ -struct client_offset { - u_int pane; - - struct window_pane_offset offset; - int flags; -#define CLIENT_OFFSET_OFF 0x1 - - RB_ENTRY(client_offset) entry; -}; -RB_HEAD(client_offsets, client_offset); - /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); @@ -1575,7 +1564,7 @@ struct client { struct cmdq_list *queue; struct client_windows windows; - struct client_offsets offsets; + struct control_offsets *offsets; pid_t pid; int fd; @@ -2359,11 +2348,6 @@ void printflike(1, 2) server_add_message(const char *, ...); /* server-client.c */ RB_PROTOTYPE(client_windows, client_window, entry, server_client_window_cmp); -RB_PROTOTYPE(client_offsets, client_offset, entry, server_client_offset_cmp); -struct client_offset *server_client_get_pane_offset(struct client *, - struct window_pane *); -struct client_offset *server_client_add_pane_offset(struct client *, - struct window_pane *); u_int server_client_how_many(void); void server_client_set_overlay(struct client *, u_int, overlay_check_cb, overlay_mode_cb, overlay_draw_cb, overlay_key_cb, @@ -2830,6 +2814,11 @@ char *parse_window_name(const char *); /* control.c */ void control_start(struct client *); +void control_set_pane_on(struct client *, struct window_pane *); +void control_set_pane_off(struct client *, struct window_pane *); +struct window_pane_offset *control_pane_offset(struct client *, + struct window_pane *, int *); +void control_free_offsets(struct client *); void printflike(2, 3) control_write(struct client *, const char *, ...); void control_write_output(struct client *, struct window_pane *); From a06a0e13921955aaf7e772ee83a7280720a2f03a Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 22 May 2020 15:08:38 +0000 Subject: [PATCH 0442/1006] xterm* can have focus too. --- options-table.c | 2 +- tty.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/options-table.c b/options-table.c index 87670b12..54bdadba 100644 --- a/options-table.c +++ b/options-table.c @@ -307,7 +307,7 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, - .default_str = "xterm*:clipboard:ccolour:cstyle:title," + .default_str = "xterm*:clipboard:ccolour:cstyle:focus:title," "screen*:title", .separator = ",", .text = "List of terminal features, used if they cannot be " diff --git a/tty.c b/tty.c index 770c8b78..d330cbc0 100644 --- a/tty.c +++ b/tty.c @@ -467,7 +467,7 @@ tty_update_features(struct tty *tty) if (options_get_number(global_options, "extended-keys")) tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS)); if (options_get_number(global_options, "focus-events")) - tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); + tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS)); if (tty->term->flags & TERM_VT100LIKE) tty_puts(tty, "\033[?7727h"); } From 033d6472cb71e82be75aae6682031ef3b711226a Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 22 May 2020 15:43:38 +0000 Subject: [PATCH 0443/1006] FocusIn keys can also update the latest client, like normal keys. --- server-client.c | 2 +- tty-keys.c | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/server-client.c b/server-client.c index 1f0ac66f..d514b11e 100644 --- a/server-client.c +++ b/server-client.c @@ -1286,7 +1286,7 @@ forward_key: window_pane_key(wp, c, s, wl, key, m); out: - if (s != NULL) + if (s != NULL && key != KEYC_FOCUS_OUT) server_client_update_latest(c); free(event); return (CMD_RETURN_NORMAL); diff --git a/tty-keys.c b/tty-keys.c index 5a2b3817..bbd4bb5f 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -800,13 +800,10 @@ complete_key: tty->flags &= ~TTY_TIMER; /* Check for focus events. */ - if (key == KEYC_FOCUS_OUT) { + if (key == KEYC_FOCUS_OUT) tty->client->flags &= ~CLIENT_FOCUSED; - return (1); - } else if (key == KEYC_FOCUS_IN) { + else if (key == KEYC_FOCUS_IN) tty->client->flags |= CLIENT_FOCUSED; - return (1); - } /* Fire the key. */ if (key != KEYC_UNKNOWN) { From 87a59efc9476cc12d3e79b1afb55b9e59984b450 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 22 May 2020 17:14:35 +0100 Subject: [PATCH 0444/1006] Log ncurses and utf8proc versions. --- compat.h | 6 ++++++ proc.c | 15 +++++++++++++-- tty-term.c | 5 ----- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/compat.h b/compat.h index 736a14fc..df8aa562 100644 --- a/compat.h +++ b/compat.h @@ -27,6 +27,12 @@ #include #include +#if defined(HAVE_CURSES_H) +#include +#elif defined(HAVE_NCURSES_H) +#include +#endif + #ifndef __GNUC__ #define __attribute__(a) #endif diff --git a/proc.c b/proc.c index f3d6063d..68de33c6 100644 --- a/proc.c +++ b/proc.c @@ -183,8 +183,19 @@ proc_start(const char *name) log_debug("%s started (%ld): version %s, socket %s, protocol %d", name, (long)getpid(), getversion(), socket_path, PROTOCOL_VERSION); - log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release, - u.version, event_get_version(), event_get_method()); + log_debug("on %s %s %s; libevent %s (%s)" +#ifdef HAVE_UTF8PROC + "; utf8proc %s/%s" +#endif +#ifdef NCURSES_VERSION + "; ncurses " NCURSES_VERSION +#endif + , u.sysname, u.release, u.version + , event_get_version(), event_get_method() +#ifdef HAVE_UTF8PROC + , utf8proc_version (), utf8proc_unicode_version () +#endif + ); tp = xcalloc(1, sizeof *tp); tp->name = xstrdup(name); diff --git a/tty-term.c b/tty-term.c index 36d8a2e6..6b1cde16 100644 --- a/tty-term.c +++ b/tty-term.c @@ -18,11 +18,6 @@ #include -#if defined(HAVE_CURSES_H) -#include -#elif defined(HAVE_NCURSES_H) -#include -#endif #include #include #include From 6ae26a6b54a47f539395dc68de115b8e103a74e6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 22 May 2020 17:20:35 +0100 Subject: [PATCH 0445/1006] Fix utf8proc version logging. --- compat.h | 4 ++++ proc.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compat.h b/compat.h index df8aa562..43a46021 100644 --- a/compat.h +++ b/compat.h @@ -33,6 +33,10 @@ #include #endif +#ifdef HAVE_UTF8PROC +#include +#endif + #ifndef __GNUC__ #define __attribute__(a) #endif diff --git a/proc.c b/proc.c index 68de33c6..823a4a52 100644 --- a/proc.c +++ b/proc.c @@ -183,14 +183,14 @@ proc_start(const char *name) log_debug("%s started (%ld): version %s, socket %s, protocol %d", name, (long)getpid(), getversion(), socket_path, PROTOCOL_VERSION); - log_debug("on %s %s %s; libevent %s (%s)" + log_debug("on %s %s %s", u.sysname, u.release, u.version); + log_debug("using libevent %s (%s)" #ifdef HAVE_UTF8PROC "; utf8proc %s/%s" #endif #ifdef NCURSES_VERSION "; ncurses " NCURSES_VERSION #endif - , u.sysname, u.release, u.version , event_get_version(), event_get_method() #ifdef HAVE_UTF8PROC , utf8proc_version (), utf8proc_unicode_version () From b3e5a99c8fd37b8b1d2921f996bf24f47631e7a2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 22 May 2020 17:22:03 +0100 Subject: [PATCH 0446/1006] And tweak again. --- proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proc.c b/proc.c index 823a4a52..48db6367 100644 --- a/proc.c +++ b/proc.c @@ -186,7 +186,7 @@ proc_start(const char *name) log_debug("on %s %s %s", u.sysname, u.release, u.version); log_debug("using libevent %s (%s)" #ifdef HAVE_UTF8PROC - "; utf8proc %s/%s" + "; utf8proc %s (Unicode %s)" #endif #ifdef NCURSES_VERSION "; ncurses " NCURSES_VERSION From 1fdacba1117daee58fe8c011f0fd55d517f66510 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 22 May 2020 17:24:42 +0100 Subject: [PATCH 0447/1006] Think Solaris needs term.h here. --- compat.h | 1 + tty-term.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/compat.h b/compat.h index 43a46021..19a5e466 100644 --- a/compat.h +++ b/compat.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #if defined(HAVE_CURSES_H) diff --git a/tty-term.c b/tty-term.c index 6b1cde16..66238b38 100644 --- a/tty-term.c +++ b/tty-term.c @@ -21,7 +21,6 @@ #include #include #include -#include #include "tmux.h" From a48cc458a60310480a8ba02da14fbed6f88399f4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 22 May 2020 17:27:07 +0100 Subject: [PATCH 0448/1006] Maybe this is better. --- compat.h | 7 ------- proc.c | 4 ++++ tty-term.c | 7 +++++++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/compat.h b/compat.h index 19a5e466..37f78e7b 100644 --- a/compat.h +++ b/compat.h @@ -25,15 +25,8 @@ #include #include #include -#include #include -#if defined(HAVE_CURSES_H) -#include -#elif defined(HAVE_NCURSES_H) -#include -#endif - #ifdef HAVE_UTF8PROC #include #endif diff --git a/proc.c b/proc.c index 48db6367..5b3fd735 100644 --- a/proc.c +++ b/proc.c @@ -27,6 +27,10 @@ #include #include +#if defined(HAVE_NCURSES_H) +#include +#endif + #include "tmux.h" struct tmuxproc { diff --git a/tty-term.c b/tty-term.c index 66238b38..1906eb23 100644 --- a/tty-term.c +++ b/tty-term.c @@ -21,6 +21,13 @@ #include #include #include +#include + +#if defined(HAVE_CURSES_H) +#include +#elif defined(HAVE_NCURSES_H) +#include +#endif #include "tmux.h" From 2ac6cc2633cf61211b31462f372e16f15d9bf2d6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 22 May 2020 17:34:30 +0100 Subject: [PATCH 0449/1006] Put headers back how they were. --- tty-term.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tty-term.c b/tty-term.c index 1906eb23..36d8a2e6 100644 --- a/tty-term.c +++ b/tty-term.c @@ -18,16 +18,15 @@ #include -#include -#include -#include -#include - #if defined(HAVE_CURSES_H) #include #elif defined(HAVE_NCURSES_H) #include #endif +#include +#include +#include +#include #include "tmux.h" From 6c829827110c6616c8abe8cb3ef9e9ed0a1dbd7d Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 24 May 2020 09:13:06 +0000 Subject: [PATCH 0450/1006] Now the tty has a pointer back to the client there is no point (and a bit confusing) in it keeping a copy of the fd as well. Remove it. --- cmd-new-session.c | 4 +-- server-client.c | 15 +++++------ tmux.h | 3 +-- tty.c | 67 ++++++++++++++++++++++------------------------- 4 files changed, 40 insertions(+), 49 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index a9a0376b..be29122d 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -165,13 +165,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) * the terminal as that calls tcsetattr() to prepare for tmux taking * over. */ - if (!detached && !already_attached && c->tty.fd != -1) { + if (!detached && !already_attached && c->fd != -1) { if (server_client_check_nested(cmdq_get_client(item))) { cmdq_error(item, "sessions should be nested with care, " "unset $TMUX to force"); goto fail; } - if (tcgetattr(c->tty.fd, &tio) != 0) + if (tcgetattr(c->fd, &tio) != 0) fatal("tcgetattr failed"); tiop = &tio; } else diff --git a/server-client.c b/server-client.c index d514b11e..2cf9c387 100644 --- a/server-client.c +++ b/server-client.c @@ -229,7 +229,6 @@ server_client_create(int fd) RB_INIT(&c->windows); RB_INIT(&c->files); - c->tty.fd = -1; c->tty.sx = 80; c->tty.sy = 24; @@ -307,10 +306,6 @@ server_client_lost(struct client *c) TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); - /* - * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called - * and tty_free might close an unrelated fd. - */ if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); @@ -342,6 +337,10 @@ server_client_lost(struct client *c) proc_remove_peer(c->peer); c->peer = NULL; + if (c->fd != -1) { + close(c->fd); + c->fd = -1; + } server_client_unref(c); server_add_accept(0); /* may be more file descriptors now */ @@ -2008,7 +2007,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) break; c->flags &= ~CLIENT_SUSPENDED; - if (c->tty.fd == -1) /* exited in the meantime */ + if (c->fd == -1) /* exited in the meantime */ break; s = c->session; @@ -2210,11 +2209,9 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) if (c->flags & CLIENT_CONTROL) { close(c->fd); c->fd = -1; - control_start(c); - c->tty.fd = -1; } else if (c->fd != -1) { - if (tty_init(&c->tty, c, c->fd) != 0) { + if (tty_init(&c->tty, c) != 0) { close(c->fd); c->fd = -1; } else { diff --git a/tmux.h b/tmux.h index 6965f132..8085b4a7 100644 --- a/tmux.h +++ b/tmux.h @@ -1283,7 +1283,6 @@ struct tty { u_int rleft; u_int rright; - int fd; struct event event_in; struct evbuffer *in; struct event event_out; @@ -2064,7 +2063,7 @@ void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); void tty_cell(struct tty *, const struct grid_cell *, const struct grid_cell *, int *); -int tty_init(struct tty *, struct client *, int); +int tty_init(struct tty *, struct client *); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); void tty_start_tty(struct tty *); diff --git a/tty.c b/tty.c index d330cbc0..ba06d0bb 100644 --- a/tty.c +++ b/tty.c @@ -90,19 +90,19 @@ tty_create_log(void) } int -tty_init(struct tty *tty, struct client *c, int fd) +tty_init(struct tty *tty, struct client *c) { - if (!isatty(fd)) + if (!isatty(c->fd)) return (-1); memset(tty, 0, sizeof *tty); - - tty->fd = fd; tty->client = c; tty->cstyle = 0; tty->ccolour = xstrdup(""); + if (tcgetattr(c->fd, &tty->tio) != 0) + return (-1); return (0); } @@ -113,7 +113,7 @@ tty_resize(struct tty *tty) struct winsize ws; u_int sx, sy, xpixel, ypixel; - if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) { + if (ioctl(c->fd, TIOCGWINSZ, &ws) != -1) { sx = ws.ws_col; if (sx == 0) { sx = 80; @@ -156,7 +156,7 @@ tty_read_callback(__unused int fd, __unused short events, void *data) size_t size = EVBUFFER_LENGTH(tty->in); int nread; - nread = evbuffer_read(tty->in, tty->fd, -1); + nread = evbuffer_read(tty->in, c->fd, -1); if (nread == 0 || nread == -1) { if (nread == 0) log_debug("%s: read closed", name); @@ -225,7 +225,7 @@ tty_write_callback(__unused int fd, __unused short events, void *data) size_t size = EVBUFFER_LENGTH(tty->out); int nwrite; - nwrite = evbuffer_write(tty->out, tty->fd); + nwrite = evbuffer_write(tty->out, c->fd); if (nwrite == -1) return; log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size); @@ -250,7 +250,7 @@ tty_open(struct tty *tty, char **cause) struct client *c = tty->client; tty->term = tty_term_create(tty, c->term_name, &c->term_features, - tty->fd, cause); + c->fd, cause); if (tty->term == NULL) { tty_close(tty); return (-1); @@ -259,13 +259,13 @@ tty_open(struct tty *tty, char **cause) tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER); - event_set(&tty->event_in, tty->fd, EV_PERSIST|EV_READ, + event_set(&tty->event_in, c->fd, EV_PERSIST|EV_READ, tty_read_callback, tty); tty->in = evbuffer_new(); if (tty->in == NULL) fatal("out of memory"); - event_set(&tty->event_out, tty->fd, EV_WRITE, tty_write_callback, tty); + event_set(&tty->event_out, c->fd, EV_WRITE, tty_write_callback, tty); tty->out = evbuffer_new(); if (tty->out == NULL) fatal("out of memory"); @@ -298,21 +298,19 @@ tty_start_tty(struct tty *tty) struct termios tio; struct timeval tv = { .tv_sec = 1 }; - if (tty->fd != -1 && tcgetattr(tty->fd, &tty->tio) == 0) { - setblocking(tty->fd, 0); - event_add(&tty->event_in, NULL); + setblocking(c->fd, 0); + event_add(&tty->event_in, NULL); - memcpy(&tio, &tty->tio, sizeof tio); - tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP); - tio.c_iflag |= IGNBRK; - tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); - tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL| - ECHOPRT|ECHOKE|ISIG); - tio.c_cc[VMIN] = 1; - tio.c_cc[VTIME] = 0; - if (tcsetattr(tty->fd, TCSANOW, &tio) == 0) - tcflush(tty->fd, TCIOFLUSH); - } + memcpy(&tio, &tty->tio, sizeof tio); + tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP); + tio.c_iflag |= IGNBRK; + tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); + tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|ECHOPRT| + ECHOKE|ISIG); + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(c->fd, TCSANOW, &tio) == 0) + tcflush(c->fd, TCIOFLUSH); tty_putcode(tty, TTYC_SMCUP); @@ -363,7 +361,8 @@ tty_send_requests(struct tty *tty) void tty_stop_tty(struct tty *tty) { - struct winsize ws; + struct client *c = tty->client; + struct winsize ws; if (!(tty->flags & TTY_STARTED)) return; @@ -382,9 +381,9 @@ tty_stop_tty(struct tty *tty) * because the fd is invalid. Things like ssh -t can easily leave us * with a dead tty. */ - if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1) + if (ioctl(c->fd, TIOCGWINSZ, &ws) == -1) return; - if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1) + if (tcsetattr(c->fd, TCSANOW, &tty->tio) == -1) return; tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); @@ -419,7 +418,7 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); - setblocking(tty->fd, 1); + setblocking(c->fd, 1); } void @@ -440,11 +439,6 @@ tty_close(struct tty *tty) tty->flags &= ~TTY_OPENED; } - - if (tty->fd != -1) { - close(tty->fd); - tty->fd = -1; - } } void @@ -475,12 +469,13 @@ tty_update_features(struct tty *tty) void tty_raw(struct tty *tty, const char *s) { - ssize_t n, slen; - u_int i; + struct client *c = tty->client; + ssize_t n, slen; + u_int i; slen = strlen(s); for (i = 0; i < 5; i++) { - n = write(tty->fd, s, slen); + n = write(c->fd, s, slen); if (n >= 0) { s += n; slen -= n; From 18aab909593440bfa4fe003a9f683c0fb2993461 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 24 May 2020 09:40:17 +0000 Subject: [PATCH 0451/1006] Give control code its own state struct. --- control.c | 43 ++++++++++++++++++++++++++++--------------- server-client.c | 2 ++ tmux.h | 5 +++-- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/control.c b/control.c index 24c26fea..750f1284 100644 --- a/control.c +++ b/control.c @@ -38,6 +38,11 @@ struct control_offset { }; RB_HEAD(control_offsets, control_offset); +/* Control state. */ +struct control_state { + struct control_offsets offsets; +}; + /* Compare client offsets. */ static int control_offset_cmp(struct control_offset *co1, struct control_offset *co2) @@ -54,31 +59,26 @@ RB_GENERATE_STATIC(control_offsets, control_offset, entry, control_offset_cmp); static struct control_offset * control_get_offset(struct client *c, struct window_pane *wp) { - struct control_offset co = { .pane = wp->id }; + struct control_state *cs = c->control_state; + struct control_offset co = { .pane = wp->id }; - if (c->offsets == NULL) - return (NULL); - return (RB_FIND(control_offsets, c->offsets, &co)); + return (RB_FIND(control_offsets, &cs->offsets, &co)); } /* Add pane offsets for this client. */ static struct control_offset * control_add_offset(struct client *c, struct window_pane *wp) { + struct control_state *cs = c->control_state; struct control_offset *co; co = control_get_offset(c, wp); if (co != NULL) return (co); - if (c->offsets == NULL) { - c->offsets = xmalloc(sizeof *c->offsets); - RB_INIT(c->offsets); - } - co = xcalloc(1, sizeof *co); co->pane = wp->id; - RB_INSERT(control_offsets, c->offsets, co); + RB_INSERT(control_offsets, &cs->offsets, co); memcpy(&co->offset, &wp->offset, sizeof co->offset); return (co); } @@ -87,15 +87,13 @@ control_add_offset(struct client *c, struct window_pane *wp) void control_free_offsets(struct client *c) { + struct control_state *cs = c->control_state; struct control_offset *co, *co1; - if (c->offsets == NULL) - return; - RB_FOREACH_SAFE(co, control_offsets, c->offsets, co1) { - RB_REMOVE(control_offsets, c->offsets, co); + RB_FOREACH_SAFE(co, control_offsets, &cs->offsets, co1) { + RB_REMOVE(control_offsets, &cs->offsets, co); free(co); } - free(c->offsets); } /* Get offsets for client. */ @@ -255,8 +253,23 @@ control_callback(__unused struct client *c, __unused const char *path, void control_start(struct client *c) { + struct control_state *cs; + + cs = c->control_state = xcalloc(1, sizeof *cs); + RB_INIT(&cs->offsets); + file_read(c, "-", control_callback, c); if (c->flags & CLIENT_CONTROLCONTROL) file_print(c, "\033P1000p"); } + +/* Stop control mode. */ +void +control_stop(struct client *c) +{ + struct control_state *cs = c->control_state; + + control_free_offsets(c); + free(cs); +} diff --git a/server-client.c b/server-client.c index 2cf9c387..a1ed16ab 100644 --- a/server-client.c +++ b/server-client.c @@ -306,6 +306,8 @@ server_client_lost(struct client *c) TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); + if (c->flags & CLIENT_CONTROL) + control_stop(c); if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); diff --git a/tmux.h b/tmux.h index 8085b4a7..a94e7611 100644 --- a/tmux.h +++ b/tmux.h @@ -45,7 +45,7 @@ struct cmdq_item; struct cmdq_list; struct cmdq_state; struct cmds; -struct control_offsets; +struct control_state; struct environ; struct format_job_tree; struct format_tree; @@ -1563,7 +1563,7 @@ struct client { struct cmdq_list *queue; struct client_windows windows; - struct control_offsets *offsets; + struct control_state *control_state; pid_t pid; int fd; @@ -2813,6 +2813,7 @@ char *parse_window_name(const char *); /* control.c */ void control_start(struct client *); +void control_stop(struct client *); void control_set_pane_on(struct client *, struct window_pane *); void control_set_pane_off(struct client *, struct window_pane *); struct window_pane_offset *control_pane_offset(struct client *, From 14a9fd58d56211f9ee1ee9347d135fc00e03d4bd Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 24 May 2020 14:45:00 +0000 Subject: [PATCH 0452/1006] Remove leftover call to control_free_offsets and do not use for non-control clients. --- server-client.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/server-client.c b/server-client.c index a1ed16ab..5c8f4940 100644 --- a/server-client.c +++ b/server-client.c @@ -301,7 +301,6 @@ server_client_lost(struct client *c) RB_REMOVE(client_windows, &c->windows, cw); free(cw); } - control_free_offsets(c); TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); @@ -2344,15 +2343,18 @@ server_client_set_flags(struct client *c, const char *flags) if (not) next++; - if (strcmp(next, "no-output") == 0) - flag = CLIENT_CONTROL_NOOUTPUT; - else if (strcmp(next, "read-only") == 0) + flag = 0; + if (c->flags & CLIENT_CONTROL) { + if (strcmp(next, "no-output") == 0) + flag = CLIENT_CONTROL_NOOUTPUT; + } + if (strcmp(next, "read-only") == 0) flag = CLIENT_READONLY; else if (strcmp(next, "ignore-size") == 0) flag = CLIENT_IGNORESIZE; else if (strcmp(next, "active-pane") == 0) flag = CLIENT_ACTIVEPANE; - else + if (flag == 0) continue; log_debug("client %s set flag %s", c->name, next); From 3a5219c6d0c1a85ac3cf7a6b938f724650001a4d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 May 2020 09:32:10 +0000 Subject: [PATCH 0453/1006] Instead of storing all UTF-8 characters in the extended cell which means that 14 bytes are wasted for each character in the BMP, only store characters of three bytes or less in the cell itself and store others (outside the BMP or with combining characters) in a separate global tree. Can reduce grid memory use for heavy Unicode users by around 30%. --- grid.c | 51 +++++++++------ tmux.h | 31 ++++++--- utf8.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++ window-copy.c | 28 ++++++-- 4 files changed, 250 insertions(+), 34 deletions(-) diff --git a/grid.c b/grid.c index 06a82522..74b6b7b8 100644 --- a/grid.c +++ b/grid.c @@ -100,11 +100,11 @@ grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, } /* Set cell as extended. */ -static struct grid_cell * +static struct grid_extd_entry * grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, const struct grid_cell *gc) { - struct grid_cell *gcp; + struct grid_extd_entry *gee; int flags = (gc->flags & ~GRID_FLAG_CLEARED); if (~gce->flags & GRID_FLAG_EXTENDED) @@ -113,10 +113,14 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, fatalx("offset too big"); gl->flags |= GRID_LINE_EXTENDED; - gcp = &gl->extddata[gce->offset]; - memcpy(gcp, gc, sizeof *gcp); - gcp->flags = flags; - return (gcp); + gee = &gl->extddata[gce->offset]; + gee->data = utf8_map_big(&gc->data); + gee->attr = gc->attr; + gee->flags = flags; + gee->fg = gc->fg; + gee->bg = gc->bg; + gee->us = gc->us; + return (gee); } /* Free up unused extended cells. */ @@ -124,9 +128,9 @@ static void grid_compact_line(struct grid_line *gl) { int new_extdsize = 0; - struct grid_cell *new_extddata; + struct grid_extd_entry *new_extddata; struct grid_cell_entry *gce; - struct grid_cell *gc; + struct grid_extd_entry *gee; u_int px, idx; if (gl->extdsize == 0) @@ -150,8 +154,8 @@ grid_compact_line(struct grid_line *gl) for (px = 0; px < gl->cellsize; px++) { gce = &gl->celldata[px]; if (gce->flags & GRID_FLAG_EXTENDED) { - gc = &gl->extddata[gce->offset]; - memcpy(&new_extddata[idx], gc, sizeof *gc); + gee = &gl->extddata[gce->offset]; + memcpy(&new_extddata[idx], gee, sizeof *gee); gce->offset = idx++; } } @@ -181,17 +185,14 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) { struct grid_line *gl = &gd->linedata[py]; struct grid_cell_entry *gce = &gl->celldata[px]; - struct grid_cell *gc; + struct grid_extd_entry *gee; memcpy(gce, &grid_cleared_entry, sizeof *gce); if (bg != 8) { if (bg & COLOUR_FLAG_RGB) { grid_get_extended_cell(gl, gce, gce->flags); - gl->flags |= GRID_LINE_EXTENDED; - - gc = &gl->extddata[gce->offset]; - memcpy(gc, &grid_cleared_cell, sizeof *gc); - gc->bg = bg; + gee = grid_extended_cell(gl, gce, &grid_cleared_cell); + gee->bg = bg; } else { if (bg & COLOUR_FLAG_256) gce->flags |= GRID_FLAG_BG256; @@ -483,12 +484,20 @@ static void grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) { struct grid_cell_entry *gce = &gl->celldata[px]; + struct grid_extd_entry *gee; if (gce->flags & GRID_FLAG_EXTENDED) { if (gce->offset >= gl->extdsize) memcpy(gc, &grid_default_cell, sizeof *gc); - else - memcpy(gc, &gl->extddata[gce->offset], sizeof *gc); + else { + gee = &gl->extddata[gce->offset]; + gc->flags = gee->flags; + gc->attr = gee->attr; + gc->fg = gee->fg; + gc->bg = gee->bg; + gc->us = gee->us; + utf8_get_big(gee->data, &gc->data); + } return; } @@ -545,7 +554,7 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, { struct grid_line *gl; struct grid_cell_entry *gce; - struct grid_cell *gcp; + struct grid_extd_entry *gee; u_int i; if (grid_check_y(gd, __func__, py) != 0) @@ -560,8 +569,8 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, for (i = 0; i < slen; i++) { gce = &gl->celldata[px + i]; if (grid_need_extended_cell(gce, gc)) { - gcp = grid_extended_cell(gl, gce, gc); - utf8_set(&gcp->data, s[i]); + gee = grid_extended_cell(gl, gce, gc); + gee->data = utf8_set_big(s[i], 1); } else grid_store_cell(gce, gc, s[i]); } diff --git a/tmux.h b/tmux.h index a94e7611..118faf9c 100644 --- a/tmux.h +++ b/tmux.h @@ -597,11 +597,11 @@ struct msg_write_close { #define MOTION_MOUSE_MODES (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) /* - * A single UTF-8 character. UTF8_SIZE must be big enough to hold - * combining characters as well, currently at most five (of three - * bytes) are supported. -*/ -#define UTF8_SIZE 18 + * A single UTF-8 character. UTF8_SIZE must be big enough to hold combining + * characters as well. It can't be more than 32 bytes without changes to how + * big characters are stored. + */ +#define UTF8_SIZE 21 struct utf8_data { u_char data[UTF8_SIZE]; @@ -609,7 +609,7 @@ struct utf8_data { u_char size; u_char width; /* 0xff if invalid */ -} __packed; +}; enum utf8_state { UTF8_MORE, UTF8_DONE, @@ -663,13 +663,25 @@ enum utf8_state { /* Grid cell data. */ struct grid_cell { - struct utf8_data data; /* 21 bytes */ + struct utf8_data data; + u_short attr; + u_char flags; + int fg; + int bg; + int us; +}; + +/* Grid extended cell entry. */ +struct grid_extd_entry { + uint32_t data; u_short attr; u_char flags; int fg; int bg; int us; } __packed; + +/* Grid cell entry. */ struct grid_cell_entry { u_char flags; union { @@ -690,7 +702,7 @@ struct grid_line { struct grid_cell_entry *celldata; u_int extdsize; - struct grid_cell *extddata; + struct grid_extd_entry *extddata; int flags; } __packed; @@ -2877,6 +2889,9 @@ u_int session_group_attached_count(struct session_group *); void session_renumber_windows(struct session *); /* utf8.c */ +uint32_t utf8_set_big(char, u_int); +uint32_t utf8_map_big(const struct utf8_data *); +void utf8_get_big(uint32_t, struct utf8_data *); void utf8_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); enum utf8_state utf8_open(struct utf8_data *, u_char); diff --git a/utf8.c b/utf8.c index 3f378fb3..68c970d0 100644 --- a/utf8.c +++ b/utf8.c @@ -29,6 +29,180 @@ static int utf8_width(wchar_t); +struct utf8_big_item { + u_int index; + RB_ENTRY(utf8_big_item) entry; + + char data[UTF8_SIZE]; + u_char size; +}; +RB_HEAD(utf8_big_tree, utf8_big_item); + +static int +utf8_big_cmp(struct utf8_big_item *bi1, struct utf8_big_item *bi2) +{ + if (bi1->size < bi2->size) + return (-1); + if (bi1->size > bi2->size) + return (1); + return (memcmp(bi1->data, bi2->data, bi1->size)); +} +RB_GENERATE_STATIC(utf8_big_tree, utf8_big_item, entry, utf8_big_cmp); +static struct utf8_big_tree utf8_big_tree = RB_INITIALIZER(utf8_big_tree); + +static struct utf8_big_item *utf8_big_list; +static u_int utf8_big_list_size; +static u_int utf8_big_list_used; + +union utf8_big_map { + uint32_t value; + struct { + u_char flags; +#define UTF8_BIG_SIZE 0x1f +#define UTF8_BIG_WIDTH2 0x20 + + u_char data[3]; + }; +} __packed; + +static const union utf8_big_map utf8_big_space1 = { + .flags = 1, + .data = " " +}; +static const union utf8_big_map utf8_big_space2 = { + .flags = UTF8_BIG_WIDTH2|2, + .data = " " +}; + +/* Get a big item by index. */ +static struct utf8_big_item * +utf8_get_big_item(const char *data, size_t size) +{ + struct utf8_big_item bi; + + memcpy(bi.data, data, size); + bi.size = size; + + return (RB_FIND(utf8_big_tree, &utf8_big_tree, &bi)); +} + +/* Add a big item. */ +static int +utf8_put_big_item(const char *data, size_t size, u_int *index) +{ + struct utf8_big_item *bi; + + bi = utf8_get_big_item(data, size); + if (bi != NULL) { + *index = bi->index; + log_debug("%s: have %.*s at %u", __func__, (int)size, data, + *index); + return (0); + } + + if (utf8_big_list_used == utf8_big_list_size) { + if (utf8_big_list_size == 0xffffff) + return (-1); + if (utf8_big_list_size == 0) + utf8_big_list_size = 256; + else if (utf8_big_list_size > 0x7fffff) + utf8_big_list_size = 0xffffff; + else + utf8_big_list_size *= 2; + utf8_big_list = xreallocarray(utf8_big_list, utf8_big_list_size, + sizeof *utf8_big_list); + } + *index = utf8_big_list_used++; + + bi = &utf8_big_list[*index]; + bi->index = *index; + memcpy(bi->data, data, size); + bi->size = size; + RB_INSERT(utf8_big_tree, &utf8_big_tree, bi); + + log_debug("%s: added %.*s at %u", __func__, (int)size, data, *index); + return (0); +} + +/* Get UTF-8 as index into buffer. */ +uint32_t +utf8_map_big(const struct utf8_data *ud) +{ + union utf8_big_map m = { .value = 0 }; + u_int o; + const char *data = ud->data; + size_t size = ud->size; + + if (ud->width != 1 && ud->width != 2) + return (utf8_big_space1.value); + + if (size > UTF8_BIG_SIZE) + goto fail; + if (size == 1) + return (utf8_set_big(data[0], 1)); + + m.flags = size; + if (ud->width == 2) + m.flags |= UTF8_BIG_WIDTH2; + + if (size <= 3) { + memcpy(&m.data, data, size); + return (m.value); + } + + if (utf8_put_big_item(data, size, &o) != 0) + goto fail; + m.data[0] = (o & 0xff); + m.data[1] = (o >> 8) & 0xff; + m.data[2] = (o >> 16); + return (m.value); + +fail: + if (ud->width == 1) + return (utf8_big_space1.value); + return (utf8_big_space2.value); +} + +/* Get UTF-8 from index into buffer. */ +void +utf8_get_big(uint32_t v, struct utf8_data *ud) +{ + union utf8_big_map m = { .value = v }; + struct utf8_big_item *bi; + u_int o; + + memset(ud, 0, sizeof *ud); + ud->size = ud->have = (m.flags & UTF8_BIG_SIZE); + if (m.flags & UTF8_BIG_WIDTH2) + ud->width = 2; + else + ud->width = 1; + + if (ud->size <= 3) { + memcpy(ud->data, m.data, ud->size); + return; + } + + o = ((uint32_t)m.data[2] << 16)|((uint32_t)m.data[1] << 8)|m.data[0]; + if (o >= utf8_big_list_used) + memset(ud->data, ' ', ud->size); + else { + bi = &utf8_big_list[o]; + memcpy(ud->data, bi->data, ud->size); + } +} + +/* Get big value for UTF-8 single character. */ +uint32_t +utf8_set_big(char c, u_int width) +{ + union utf8_big_map m = { .flags = 1, .data[0] = c }; + + if (width == 2) + m.flags |= UTF8_BIG_WIDTH2; + return (m.value); +} + /* Set a single character. */ void utf8_set(struct utf8_data *ud, u_char ch) diff --git a/window-copy.c b/window-copy.c index e572eaa8..1efe01d0 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2551,23 +2551,33 @@ window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, } static const char * -window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size) +window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size, + int *allocated) { + static struct utf8_data ud; struct grid_cell_entry *gce; + char *copy; if (px >= gl->cellsize) { *size = 1; + *allocated = 0; return (" "); } gce = &gl->celldata[px]; if (~gce->flags & GRID_FLAG_EXTENDED) { *size = 1; + *allocated = 0; return (&gce->data.data); } - *size = gl->extddata[gce->offset].data.size; - return (gl->extddata[gce->offset].data.data); + utf8_get_big(gl->extddata[gce->offset].data, &ud); + *size = ud.size; + *allocated = 1; + + copy = xmalloc(ud.size); + memcpy(copy, ud.data, ud.size); + return (copy); } /* Find last match in given range. */ @@ -2630,6 +2640,7 @@ window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, const struct grid_line *gl; const char *d; size_t bufsize = 1024, dlen; + int allocated; while (bufsize < newsize) bufsize *= 2; @@ -2638,7 +2649,7 @@ window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, gl = grid_peek_line(gd, py); bx = *size - 1; for (ax = first; ax < last; ax++) { - d = window_copy_cellstring(gl, ax, &dlen); + d = window_copy_cellstring(gl, ax, &dlen, &allocated); newsize += dlen; while (bufsize < newsize) { bufsize *= 2; @@ -2650,6 +2661,8 @@ window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, memcpy(buf + bx, d, dlen); bx += dlen; } + if (allocated) + free((void *)d); } buf[newsize - 1] = '\0'; @@ -2670,6 +2683,7 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, struct { const char *d; size_t dlen; + int allocated; } *cells; /* Populate the array of cell data. */ @@ -2680,7 +2694,7 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, gl = grid_peek_line(gd, pywrap); while (cell < ncells) { cells[cell].d = window_copy_cellstring(gl, px, - &cells[cell].dlen); + &cells[cell].dlen, &cells[cell].allocated); cell++; px++; if (px == gd->sx) { @@ -2738,6 +2752,10 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, *ppy = pywrap; /* Free cell data. */ + for (cell = 0; cell < ncells; cell++) { + if (cells[cell].allocated) + free((void *)cells[cell].d); + } free(cells); } From 26e8e467e8e4d3e4f4596b9ce50832735cdc4d50 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 May 2020 11:59:50 +0000 Subject: [PATCH 0454/1006] Include title for the width of the menu. --- menu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/menu.c b/menu.c index 7f6933c5..16374115 100644 --- a/menu.c +++ b/menu.c @@ -111,6 +111,7 @@ menu_create(const char *title) menu = xcalloc(1, sizeof *menu); menu->title = xstrdup(title); + menu->width = format_width(title); return (menu); } From 4589297e43ad4b385c9110879aab8338d7c45474 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 May 2020 12:12:58 +0000 Subject: [PATCH 0455/1006] Do not attempt to divide by zero when working out copy position. --- window-copy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index 1efe01d0..b869fc83 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3167,7 +3167,7 @@ window_copy_match_at_cursor(struct window_copy_mode_data *data) */ for (at = start; at <= end; at++) { py = at / sx; - px = at % (py * sx); + px = at - (py * sx); grid_get_cell(gd, px, py, &gc); buf = xrealloc(buf, len + gc.data.size + 1); From bbfb44e9b2e5b2e6560abb0892bb9d5f68e42d0f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 May 2020 15:02:25 +0000 Subject: [PATCH 0456/1006] Make some data types consistent. --- format.c | 35 ++++++++++++++++++++++++++++++----- tmux.h | 8 ++++---- utf8.c | 10 +++++----- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/format.c b/format.c index c7f7b90a..6ff04e72 100644 --- a/format.c +++ b/format.c @@ -761,22 +761,46 @@ format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) struct window_pane *wp = ft->wp; struct grid *gd; struct grid_line *gl; - unsigned long long size; + size_t size = 0; u_int i; if (wp == NULL) return; gd = wp->base.grid; - size = 0; - for (i = 0; i < gd->hsize; i++) { + for (i = 0; i < gd->hsize + gd->sy; i++) { gl = grid_get_line(gd, i); size += gl->cellsize * sizeof *gl->celldata; size += gl->extdsize * sizeof *gl->extddata; } - size += gd->hsize * sizeof *gl; + size += (gd->hsize + gd->sy) * sizeof *gl; - xasprintf(&fe->value, "%llu", size); + xasprintf(&fe->value, "%zu", size); +} + +/* Callback for history_all_bytes. */ +static void +format_cb_history_all_bytes(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + struct grid *gd; + struct grid_line *gl; + u_int i, lines, cells = 0, extended_cells = 0; + + if (wp == NULL) + return; + gd = wp->base.grid; + + lines = gd->hsize + gd->sy; + for (i = 0; i < lines; i++) { + gl = grid_get_line(gd, i); + cells += gl->cellsize; + extended_cells += gl->extdsize; + } + + xasprintf(&fe->value, "%u,%zu,%u,%zu,%u,%zu", lines, + lines * sizeof *gl, cells, cells * sizeof *gl->celldata, + extended_cells, extended_cells * sizeof *gl->extddata); } /* Callback for pane_tabs. */ @@ -2823,6 +2847,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "history_size", "%u", gd->hsize); format_add(ft, "history_limit", "%u", gd->hlimit); format_add_cb(ft, "history_bytes", format_cb_history_bytes); + format_add_cb(ft, "history_all_bytes", format_cb_history_all_bytes); format_add(ft, "pane_written", "%zu", wp->written); format_add(ft, "pane_skipped", "%zu", wp->skipped); diff --git a/tmux.h b/tmux.h index 118faf9c..1eaa1cc1 100644 --- a/tmux.h +++ b/tmux.h @@ -673,7 +673,7 @@ struct grid_cell { /* Grid extended cell entry. */ struct grid_extd_entry { - uint32_t data; + u_int data; u_short attr; u_char flags; int fg; @@ -2889,9 +2889,9 @@ u_int session_group_attached_count(struct session_group *); void session_renumber_windows(struct session *); /* utf8.c */ -uint32_t utf8_set_big(char, u_int); -uint32_t utf8_map_big(const struct utf8_data *); -void utf8_get_big(uint32_t, struct utf8_data *); +u_int utf8_set_big(char, u_int); +u_int utf8_map_big(const struct utf8_data *); +void utf8_get_big(u_int, struct utf8_data *); void utf8_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); enum utf8_state utf8_open(struct utf8_data *, u_char); diff --git a/utf8.c b/utf8.c index 68c970d0..a01540b1 100644 --- a/utf8.c +++ b/utf8.c @@ -55,7 +55,7 @@ static u_int utf8_big_list_size; static u_int utf8_big_list_used; union utf8_big_map { - uint32_t value; + u_int value; struct { u_char flags; #define UTF8_BIG_SIZE 0x1f @@ -125,7 +125,7 @@ utf8_put_big_item(const char *data, size_t size, u_int *index) } /* Get UTF-8 as index into buffer. */ -uint32_t +u_int utf8_map_big(const struct utf8_data *ud) { union utf8_big_map m = { .value = 0 }; @@ -165,7 +165,7 @@ fail: /* Get UTF-8 from index into buffer. */ void -utf8_get_big(uint32_t v, struct utf8_data *ud) +utf8_get_big(u_int v, struct utf8_data *ud) { union utf8_big_map m = { .value = v }; struct utf8_big_item *bi; @@ -183,7 +183,7 @@ utf8_get_big(uint32_t v, struct utf8_data *ud) return; } - o = ((uint32_t)m.data[2] << 16)|((uint32_t)m.data[1] << 8)|m.data[0]; + o = ((u_int)m.data[2] << 16)|((u_int)m.data[1] << 8)|m.data[0]; if (o >= utf8_big_list_used) memset(ud->data, ' ', ud->size); else { @@ -193,7 +193,7 @@ utf8_get_big(uint32_t v, struct utf8_data *ud) } /* Get big value for UTF-8 single character. */ -uint32_t +u_int utf8_set_big(char c, u_int width) { union utf8_big_map m = { .flags = 1, .data[0] = c }; From dc893405e129e67074db15c04b78790618853181 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 May 2020 18:17:14 +0000 Subject: [PATCH 0457/1006] Fix some error strings, from Kris Katterjohn. --- arguments.c | 4 ++-- cmd-resize-pane.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arguments.c b/arguments.c index 712e61c5..ef67107d 100644 --- a/arguments.c +++ b/arguments.c @@ -407,11 +407,11 @@ args_string_percentage(const char *value, long long minval, long long maxval, } ll = (curval * ll) / 100; if (ll < minval) { - *cause = xstrdup("too large"); + *cause = xstrdup("too small"); return (0); } if (ll > maxval) { - *cause = xstrdup("too small"); + *cause = xstrdup("too large"); return (0); } } else { diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 563c95fb..94e060d0 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -117,7 +117,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'y')) { y = args_percentage(args, 'y', 0, INT_MAX, w->sy, &cause); if (cause != NULL) { - cmdq_error(item, "width %s", cause); + cmdq_error(item, "height %s", cause); free(cause); return (CMD_RETURN_ERROR); } From 49ec0742713260f0ba0044701032373409d44984 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 May 2020 18:19:29 +0000 Subject: [PATCH 0458/1006] Tidy up new UTF-8 code and make it more generic. --- grid.c | 6 +- tmux.h | 15 ++- utf8.c | 295 ++++++++++++++++++++++++-------------------------- window-copy.c | 2 +- 4 files changed, 154 insertions(+), 164 deletions(-) diff --git a/grid.c b/grid.c index 74b6b7b8..3f1e3b2e 100644 --- a/grid.c +++ b/grid.c @@ -114,7 +114,7 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, gl->flags |= GRID_LINE_EXTENDED; gee = &gl->extddata[gce->offset]; - gee->data = utf8_map_big(&gc->data); + utf8_from_data(&gc->data, &gee->data); gee->attr = gc->attr; gee->flags = flags; gee->fg = gc->fg; @@ -496,7 +496,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) gc->fg = gee->fg; gc->bg = gee->bg; gc->us = gee->us; - utf8_get_big(gee->data, &gc->data); + utf8_to_data(gee->data, &gc->data); } return; } @@ -570,7 +570,7 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, gce = &gl->celldata[px + i]; if (grid_need_extended_cell(gce, gc)) { gee = grid_extended_cell(gl, gce, gc); - gee->data = utf8_set_big(s[i], 1); + gee->data = utf8_build_one(s[i], 1); } else grid_store_cell(gce, gc, s[i]); } diff --git a/tmux.h b/tmux.h index 1eaa1cc1..efdca430 100644 --- a/tmux.h +++ b/tmux.h @@ -596,10 +596,13 @@ struct msg_write_close { #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) #define MOTION_MOUSE_MODES (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) +/* A single UTF-8 character. */ +typedef u_int utf8_char; + /* - * A single UTF-8 character. UTF8_SIZE must be big enough to hold combining + * An expanded UTF-8 character. UTF8_SIZE must be big enough to hold combining * characters as well. It can't be more than 32 bytes without changes to how - * big characters are stored. + * characters are stored. */ #define UTF8_SIZE 21 struct utf8_data { @@ -673,7 +676,7 @@ struct grid_cell { /* Grid extended cell entry. */ struct grid_extd_entry { - u_int data; + utf8_char data; u_short attr; u_char flags; int fg; @@ -2889,9 +2892,9 @@ u_int session_group_attached_count(struct session_group *); void session_renumber_windows(struct session *); /* utf8.c */ -u_int utf8_set_big(char, u_int); -u_int utf8_map_big(const struct utf8_data *); -void utf8_get_big(u_int, struct utf8_data *); +utf8_char utf8_build_one(char, u_int); +enum utf8_state utf8_from_data(const struct utf8_data *, utf8_char *); +void utf8_to_data(utf8_char, struct utf8_data *); void utf8_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); enum utf8_state utf8_open(struct utf8_data *, u_char); diff --git a/utf8.c b/utf8.c index a01540b1..38827e3d 100644 --- a/utf8.c +++ b/utf8.c @@ -27,153 +27,157 @@ #include "tmux.h" -static int utf8_width(wchar_t); - -struct utf8_big_item { - u_int index; - RB_ENTRY(utf8_big_item) entry; +struct utf8_item { + u_int offset; + RB_ENTRY(utf8_item) entry; char data[UTF8_SIZE]; u_char size; }; -RB_HEAD(utf8_big_tree, utf8_big_item); +RB_HEAD(utf8_tree, utf8_item); static int -utf8_big_cmp(struct utf8_big_item *bi1, struct utf8_big_item *bi2) +utf8_cmp(struct utf8_item *ui1, struct utf8_item *ui2) { - if (bi1->size < bi2->size) + if (ui1->size < ui2->size) return (-1); - if (bi1->size > bi2->size) + if (ui1->size > ui2->size) return (1); - return (memcmp(bi1->data, bi2->data, bi1->size)); + return (memcmp(ui1->data, ui2->data, ui1->size)); } -RB_GENERATE_STATIC(utf8_big_tree, utf8_big_item, entry, utf8_big_cmp); -static struct utf8_big_tree utf8_big_tree = RB_INITIALIZER(utf8_big_tree); +RB_GENERATE_STATIC(utf8_tree, utf8_item, entry, utf8_cmp); +static struct utf8_tree utf8_tree = RB_INITIALIZER(utf8_tree); -static struct utf8_big_item *utf8_big_list; -static u_int utf8_big_list_size; -static u_int utf8_big_list_used; +static struct utf8_item *utf8_list; +static u_int utf8_list_size; +static u_int utf8_list_used; -union utf8_big_map { - u_int value; +union utf8_map { + utf8_char uc; struct { u_char flags; -#define UTF8_BIG_SIZE 0x1f -#define UTF8_BIG_WIDTH2 0x20 +#define UTF8_FLAG_SIZE 0x1f +#define UTF8_FLAG_WIDTH2 0x20 u_char data[3]; }; } __packed; -static const union utf8_big_map utf8_big_space1 = { +static const union utf8_map utf8_space1 = { .flags = 1, .data = " " }; -static const union utf8_big_map utf8_big_space2 = { - .flags = UTF8_BIG_WIDTH2|2, +static const union utf8_map utf8_space2 = { + .flags = UTF8_FLAG_WIDTH2|2, .data = " " }; -/* Get a big item by index. */ -static struct utf8_big_item * -utf8_get_big_item(const char *data, size_t size) +/* Get a UTF-8 item by offset. */ +static struct utf8_item * +utf8_get_item(const char *data, size_t size) { - struct utf8_big_item bi; + struct utf8_item ui; - memcpy(bi.data, data, size); - bi.size = size; + memcpy(ui.data, data, size); + ui.size = size; - return (RB_FIND(utf8_big_tree, &utf8_big_tree, &bi)); + return (RB_FIND(utf8_tree, &utf8_tree, &ui)); } -/* Add a big item. */ +/* Expand UTF-8 list. */ static int -utf8_put_big_item(const char *data, size_t size, u_int *index) +utf8_expand_list(void) { - struct utf8_big_item *bi; - - bi = utf8_get_big_item(data, size); - if (bi != NULL) { - *index = bi->index; - log_debug("%s: have %.*s at %u", __func__, (int)size, data, - *index); - return (0); - } - - if (utf8_big_list_used == utf8_big_list_size) { - if (utf8_big_list_size == 0xffffff) - return (-1); - if (utf8_big_list_size == 0) - utf8_big_list_size = 256; - else if (utf8_big_list_size > 0x7fffff) - utf8_big_list_size = 0xffffff; - else - utf8_big_list_size *= 2; - utf8_big_list = xreallocarray(utf8_big_list, utf8_big_list_size, - sizeof *utf8_big_list); - } - *index = utf8_big_list_used++; - - bi = &utf8_big_list[*index]; - bi->index = *index; - memcpy(bi->data, data, size); - bi->size = size; - RB_INSERT(utf8_big_tree, &utf8_big_tree, bi); - - log_debug("%s: added %.*s at %u", __func__, (int)size, data, *index); + if (utf8_list_size == 0xffffff) + return (-1); + if (utf8_list_size == 0) + utf8_list_size = 256; + else if (utf8_list_size > 0x7fffff) + utf8_list_size = 0xffffff; + else + utf8_list_size *= 2; + utf8_list = xreallocarray(utf8_list, utf8_list_size, sizeof *utf8_list); return (0); } -/* Get UTF-8 as index into buffer. */ -u_int -utf8_map_big(const struct utf8_data *ud) +/* Add a UTF-8 item. */ +static int +utf8_put_item(const char *data, size_t size, u_int *offset) { - union utf8_big_map m = { .value = 0 }; - u_int o; - const char *data = ud->data; - size_t size = ud->size; + struct utf8_item *ui; - if (ud->width != 1 && ud->width != 2) - return (utf8_big_space1.value); - - if (size > UTF8_BIG_SIZE) - goto fail; - if (size == 1) - return (utf8_set_big(data[0], 1)); - - m.flags = size; - if (ud->width == 2) - m.flags |= UTF8_BIG_WIDTH2; - - if (size <= 3) { - memcpy(&m.data, data, size); - return (m.value); + ui = utf8_get_item(data, size); + if (ui != NULL) { + *offset = ui->offset; + log_debug("%s: have %.*s at %u", __func__, (int)size, data, + *offset); + return (0); } - if (utf8_put_big_item(data, size, &o) != 0) + if (utf8_list_used == utf8_list_size && utf8_expand_list() != 0) + return (-1); + *offset = utf8_list_used++; + + ui = &utf8_list[*offset]; + ui->offset = *offset; + memcpy(ui->data, data, size); + ui->size = size; + RB_INSERT(utf8_tree, &utf8_tree, ui); + + log_debug("%s: added %.*s at %u", __func__, (int)size, data, *offset); + return (0); +} + +/* Get UTF-8 character from data. */ +enum utf8_state +utf8_from_data(const struct utf8_data *ud, utf8_char *uc) +{ + union utf8_map m = { .uc = 0 }; + u_int offset; + + if (ud->width != 1 && ud->width != 2) + return (utf8_space1.uc); + + if (ud->size > UTF8_FLAG_SIZE) goto fail; - m.data[0] = (o & 0xff); - m.data[1] = (o >> 8) & 0xff; - m.data[2] = (o >> 16); - return (m.value); + if (ud->size == 1) + return (utf8_build_one(ud->data[0], 1)); + + m.flags = ud->size; + if (ud->width == 2) + m.flags |= UTF8_FLAG_WIDTH2; + + if (ud->size <= 3) + memcpy(m.data, ud->data, ud->size); + else { + if (utf8_put_item(ud->data, ud->size, &offset) != 0) + goto fail; + m.data[0] = (offset & 0xff); + m.data[1] = (offset >> 8) & 0xff; + m.data[2] = (offset >> 16); + } + *uc = m.uc; + return (UTF8_DONE); fail: if (ud->width == 1) - return (utf8_big_space1.value); - return (utf8_big_space2.value); + *uc = utf8_space1.uc; + else + *uc = utf8_space2.uc; + return (UTF8_ERROR); } -/* Get UTF-8 from index into buffer. */ +/* Get UTF-8 data from character. */ void -utf8_get_big(u_int v, struct utf8_data *ud) +utf8_to_data(utf8_char uc, struct utf8_data *ud) { - union utf8_big_map m = { .value = v }; - struct utf8_big_item *bi; - u_int o; + union utf8_map m = { .uc = uc }; + struct utf8_item *ui; + u_int offset; memset(ud, 0, sizeof *ud); - ud->size = ud->have = (m.flags & UTF8_BIG_SIZE); - if (m.flags & UTF8_BIG_WIDTH2) + ud->size = ud->have = (m.flags & UTF8_FLAG_SIZE); + if (m.flags & UTF8_FLAG_WIDTH2) ud->width = 2; else ud->width = 1; @@ -183,24 +187,24 @@ utf8_get_big(u_int v, struct utf8_data *ud) return; } - o = ((u_int)m.data[2] << 16)|((u_int)m.data[1] << 8)|m.data[0]; - if (o >= utf8_big_list_used) + offset = ((u_int)m.data[2] << 16)|((u_int)m.data[1] << 8)|m.data[0]; + if (offset >= utf8_list_used) memset(ud->data, ' ', ud->size); else { - bi = &utf8_big_list[o]; - memcpy(ud->data, bi->data, ud->size); + ui = &utf8_list[offset]; + memcpy(ud->data, ui->data, ud->size); } } -/* Get big value for UTF-8 single character. */ +/* Get UTF-8 character from a single ASCII character. */ u_int -utf8_set_big(char c, u_int width) +utf8_build_one(char c, u_int width) { - union utf8_big_map m = { .flags = 1, .data[0] = c }; + union utf8_map m = { .flags = 1, .data[0] = c }; if (width == 2) - m.flags |= UTF8_BIG_WIDTH2; - return (m.value); + m.flags |= UTF8_FLAG_WIDTH2; + return (m.uc); } /* Set a single character. */ @@ -225,6 +229,20 @@ utf8_copy(struct utf8_data *to, const struct utf8_data *from) to->data[i] = '\0'; } +/* Get width of Unicode character. */ +static int +utf8_width(wchar_t wc) +{ + int width; + + width = wcwidth(wc); + if (width < 0 || width > 0xff) { + log_debug("Unicode %04lx, wcwidth() %d", (long)wc, width); + return (-1); + } + return (width); +} + /* * Open UTF-8 sequence. * @@ -279,20 +297,6 @@ utf8_append(struct utf8_data *ud, u_char ch) return (UTF8_DONE); } -/* Get width of Unicode character. */ -static int -utf8_width(wchar_t wc) -{ - int width; - - width = wcwidth(wc); - if (width < 0 || width > 0xff) { - log_debug("Unicode %04lx, wcwidth() %d", (long)wc, width); - return (-1); - } - return (width); -} - /* Combine UTF-8 into Unicode. */ enum utf8_state utf8_combine(const struct utf8_data *ud, wchar_t *wc) @@ -337,13 +341,10 @@ int utf8_strvis(char *dst, const char *src, size_t len, int flag) { struct utf8_data ud; - const char *start, *end; + const char *start = dst, *end = src + len; enum utf8_state more; size_t i; - start = dst; - end = src + len; - while (src < end) { if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { while (++src < end && more == UTF8_MORE) @@ -369,7 +370,6 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) dst = vis(dst, src[0], flag, '\0'); src++; } - *dst = '\0'; return (dst - start); } @@ -392,9 +392,9 @@ utf8_stravis(char **dst, const char *src, int flag) int utf8_isvalid(const char *s) { - struct utf8_data ud; - const char *end; - enum utf8_state more; + struct utf8_data ud; + const char *end; + enum utf8_state more; end = s + strlen(s); while (s < end) { @@ -420,15 +420,12 @@ utf8_isvalid(const char *s) char * utf8_sanitize(const char *src) { - char *dst; - size_t n; - enum utf8_state more; - struct utf8_data ud; - u_int i; + char *dst = NULL; + size_t n = 0; + enum utf8_state more; + struct utf8_data ud; + u_int i; - dst = NULL; - - n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { @@ -449,7 +446,6 @@ utf8_sanitize(const char *src) dst[n++] = '_'; src++; } - dst = xreallocarray(dst, n + 1, sizeof *dst); dst[n] = '\0'; return (dst); @@ -471,9 +467,8 @@ u_int utf8_strwidth(const struct utf8_data *s, ssize_t n) { ssize_t i; - u_int width; + u_int width = 0; - width = 0; for (i = 0; s[i].size != 0; i++) { if (n != -1 && n == i) break; @@ -489,13 +484,10 @@ utf8_strwidth(const struct utf8_data *s, ssize_t n) struct utf8_data * utf8_fromcstr(const char *src) { - struct utf8_data *dst; - size_t n; + struct utf8_data *dst = NULL; + size_t n = 0; enum utf8_state more; - dst = NULL; - - n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); if ((more = utf8_open(&dst[n], *src)) == UTF8_MORE) { @@ -511,7 +503,6 @@ utf8_fromcstr(const char *src) n++; src++; } - dst = xreallocarray(dst, n + 1, sizeof *dst); dst[n].size = 0; return (dst); @@ -521,18 +512,14 @@ utf8_fromcstr(const char *src) char * utf8_tocstr(struct utf8_data *src) { - char *dst; - size_t n; + char *dst = NULL; + size_t n = 0; - dst = NULL; - - n = 0; for(; src->size != 0; src++) { dst = xreallocarray(dst, n + src->size, 1); memcpy(dst + n, src->data, src->size); n += src->size; } - dst = xreallocarray(dst, n + 1, 1); dst[n] = '\0'; return (dst); @@ -570,7 +557,7 @@ utf8_padcstr(const char *s, u_int width) { size_t slen; char *out; - u_int n, i; + u_int n, i; n = utf8_cstrwidth(s); if (n >= width) @@ -591,7 +578,7 @@ utf8_rpadcstr(const char *s, u_int width) { size_t slen; char *out; - u_int n, i; + u_int n, i; n = utf8_cstrwidth(s); if (n >= width) diff --git a/window-copy.c b/window-copy.c index b869fc83..cfa91df9 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2571,7 +2571,7 @@ window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size, return (&gce->data.data); } - utf8_get_big(gl->extddata[gce->offset].data, &ud); + utf8_to_data(gl->extddata[gce->offset].data, &ud); *size = ud.size; *allocated = 1; From 35779d655d7eec4b904eeb3a670bbef02aba016d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 May 2020 18:55:36 +0000 Subject: [PATCH 0459/1006] Fix definition of padding cells so they are not extended cells. --- screen-write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen-write.c b/screen-write.c index 062a2929..68c3aa76 100644 --- a/screen-write.c +++ b/screen-write.c @@ -39,7 +39,7 @@ static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, const struct utf8_data *, u_int *); static const struct grid_cell screen_write_pad_cell = { - { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 0, 8, 8 + { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0 }; struct screen_write_collect_item { From 6f03e49e68dfe0d9c0c7d49079c4383b26aca916 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 25 May 2020 18:57:24 +0000 Subject: [PATCH 0460/1006] Use the internal representation for UTF-8 keys instead of wchar_t and drop some code only needed for that. --- cmd-parse.y | 13 +++++----- cmd-send-keys.c | 10 ++++---- grid.c | 4 ++- input-keys.c | 3 +-- key-string.c | 17 ++++++------- status.c | 3 +-- tmux.h | 3 --- tty-keys.c | 8 +++--- utf8.c | 67 ++++++++++++++----------------------------------- 9 files changed, 48 insertions(+), 80 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 891f2289..9f36af7e 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -26,6 +26,7 @@ #include #include #include +#include #include "tmux.h" @@ -1251,10 +1252,9 @@ error: static int yylex_token_escape(char **buf, size_t *len) { - int ch, type, o2, o3; - u_int size, i, tmp; - char s[9]; - struct utf8_data ud; + int ch, type, o2, o3, mlen; + u_int size, i, tmp; + char s[9], m[MB_LEN_MAX]; ch = yylex_getc(); @@ -1339,11 +1339,12 @@ unicode: yyerror("invalid \\%c argument", type); return (0); } - if (utf8_split(tmp, &ud) != UTF8_DONE) { + mlen = wctomb(m, tmp); + if (mlen <= 0 || mlen > (int)sizeof m) { yyerror("invalid \\%c argument", type); return (0); } - yylex_append(buf, len, ud.data, ud.size); + yylex_append(buf, len, m, mlen); return (1); } diff --git a/cmd-send-keys.c b/cmd-send-keys.c index a9ecc807..67af718a 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -91,8 +91,8 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, struct args *args, int i) { const char *s = args->argv[i]; - struct utf8_data *ud, *uc; - wchar_t wc; + struct utf8_data *ud, *loop; + utf8_char uc; key_code key; char *endptr; long n; @@ -117,10 +117,10 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, } if (literal) { ud = utf8_fromcstr(s); - for (uc = ud; uc->size != 0; uc++) { - if (utf8_combine(uc, &wc) != UTF8_DONE) + for (loop = ud; loop->size != 0; loop++) { + if (utf8_from_data(loop, &uc) != UTF8_DONE) continue; - after = cmd_send_keys_inject_key(item, after, wc); + after = cmd_send_keys_inject_key(item, after, uc); } free(ud); } diff --git a/grid.c b/grid.c index 3f1e3b2e..acd1f475 100644 --- a/grid.c +++ b/grid.c @@ -76,7 +76,7 @@ grid_need_extended_cell(const struct grid_cell_entry *gce, return (1); if (gc->attr > 0xff) return (1); - if (gc->data.size != 1 || gc->data.width != 1) + if (gc->data.size > 1 || gc->data.width > 1) return (1); if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB)) return (1); @@ -496,6 +496,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) gc->fg = gee->fg; gc->bg = gee->bg; gc->us = gee->us; + log_debug("!!! %x", gc->flags); utf8_to_data(gee->data, &gc->data); } return; @@ -541,6 +542,7 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) gl->cellused = px + 1; gce = &gl->celldata[px]; + if (gc->flags & GRID_FLAG_PADDING) log_debug("!!! padding %d\n", grid_need_extended_cell(gce, gc)); if (grid_need_extended_cell(gce, gc)) grid_extended_cell(gl, gce, gc); else diff --git a/input-keys.c b/input-keys.c index 7a922ab8..d1fbbfcf 100644 --- a/input-keys.c +++ b/input-keys.c @@ -469,10 +469,9 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) return (0); } if (justkey > 0x7f && justkey < KEYC_BASE) { - if (utf8_split(justkey, &ud) != UTF8_DONE) - return (-1); if (key & KEYC_META) bufferevent_write(bev, "\033", 1); + utf8_to_data(justkey, &ud); bufferevent_write(bev, ud.data, ud.size); return (0); } diff --git a/key-string.c b/key-string.c index 1efb6e0b..4ee12145 100644 --- a/key-string.c +++ b/key-string.c @@ -169,7 +169,7 @@ key_string_lookup_string(const char *string) struct utf8_data ud; u_int i; enum utf8_state more; - wchar_t wc; + utf8_char uc; /* Is this no key or any key? */ if (strcasecmp(string, "None") == 0) @@ -210,9 +210,9 @@ key_string_lookup_string(const char *string) more = utf8_append(&ud, (u_char)string[i]); if (more != UTF8_DONE) return (KEYC_UNKNOWN); - if (utf8_combine(&ud, &wc) != UTF8_DONE) + if (utf8_from_data(&ud, &uc) != UTF8_DONE) return (KEYC_UNKNOWN); - return (wc|modifiers); + return (uc|modifiers); } /* Otherwise look the key up in the table. */ @@ -349,12 +349,11 @@ key_string_lookup_key(key_code key, int with_flags) /* Is this a UTF-8 key? */ if (key > 127 && key < KEYC_BASE) { - if (utf8_split(key, &ud) == UTF8_DONE) { - off = strlen(out); - memcpy(out + off, ud.data, ud.size); - out[off + ud.size] = '\0'; - goto out; - } + utf8_to_data(key, &ud); + off = strlen(out); + memcpy(out + off, ud.data, ud.size); + out[off + ud.size] = '\0'; + goto out; } /* Invalid keys are errors. */ diff --git a/status.c b/status.c index 93ac70df..ad4d6cda 100644 --- a/status.c +++ b/status.c @@ -1275,8 +1275,7 @@ process_key: append_key: if (key <= 0x1f || key >= KEYC_BASE) return (0); - if (utf8_split(key, &tmp) != UTF8_DONE) - return (0); + utf8_to_data(key, &tmp); c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2, sizeof *c->prompt_buffer); diff --git a/tmux.h b/tmux.h index efdca430..a0369e34 100644 --- a/tmux.h +++ b/tmux.h @@ -30,7 +30,6 @@ #include #include #include -#include #include "xmalloc.h" @@ -2899,8 +2898,6 @@ void utf8_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); enum utf8_state utf8_open(struct utf8_data *, u_char); enum utf8_state utf8_append(struct utf8_data *, u_char); -enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *); -enum utf8_state utf8_split(wchar_t, struct utf8_data *); int utf8_isvalid(const char *); int utf8_strvis(char *, const char *, size_t, int); int utf8_stravis(char **, const char *, int); diff --git a/tty-keys.c b/tty-keys.c index bbd4bb5f..2e375807 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -578,8 +578,8 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, struct tty_key *tk, *tk1; struct utf8_data ud; enum utf8_state more; + utf8_char uc; u_int i; - wchar_t wc; log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len, (int)len, buf, expired); @@ -611,12 +611,12 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, if (more != UTF8_DONE) return (-1); - if (utf8_combine(&ud, &wc) != UTF8_DONE) + if (utf8_from_data(&ud, &uc) != UTF8_DONE) return (-1); - *key = wc; + *key = uc; log_debug("%s: UTF-8 key %.*s %#llx", c->name, (int)ud.size, - buf, *key); + ud.data, *key); return (0); } diff --git a/utf8.c b/utf8.c index 38827e3d..9df74590 100644 --- a/utf8.c +++ b/utf8.c @@ -230,17 +230,27 @@ utf8_copy(struct utf8_data *to, const struct utf8_data *from) } /* Get width of Unicode character. */ -static int -utf8_width(wchar_t wc) +static enum utf8_state +utf8_width(struct utf8_data *ud, int *width) { - int width; + wchar_t wc; - width = wcwidth(wc); - if (width < 0 || width > 0xff) { - log_debug("Unicode %04lx, wcwidth() %d", (long)wc, width); - return (-1); + switch (mbtowc(&wc, ud->data, ud->size)) { + case -1: + log_debug("UTF-8 %.*s, mbtowc() %d", (int)ud->size, ud->data, + errno); + mbtowc(NULL, NULL, MB_CUR_MAX); + return (UTF8_ERROR); + case 0: + return (UTF8_ERROR); } - return (width); + *width = wcwidth(wc); + if (*width < 0 || *width > 0xff) { + log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, + *width); + return (UTF8_ERROR); + } + return (UTF8_DONE); } /* @@ -270,7 +280,6 @@ utf8_open(struct utf8_data *ud, u_char ch) enum utf8_state utf8_append(struct utf8_data *ud, u_char ch) { - wchar_t wc; int width; if (ud->have >= ud->size) @@ -287,51 +296,13 @@ utf8_append(struct utf8_data *ud, u_char ch) if (ud->width == 0xff) return (UTF8_ERROR); - - if (utf8_combine(ud, &wc) != UTF8_DONE) - return (UTF8_ERROR); - if ((width = utf8_width(wc)) < 0) + if (utf8_width(ud, &width) != UTF8_DONE) return (UTF8_ERROR); ud->width = width; return (UTF8_DONE); } -/* Combine UTF-8 into Unicode. */ -enum utf8_state -utf8_combine(const struct utf8_data *ud, wchar_t *wc) -{ - switch (mbtowc(wc, ud->data, ud->size)) { - case -1: - log_debug("UTF-8 %.*s, mbtowc() %d", (int)ud->size, ud->data, - errno); - mbtowc(NULL, NULL, MB_CUR_MAX); - return (UTF8_ERROR); - case 0: - return (UTF8_ERROR); - default: - return (UTF8_DONE); - } -} - -/* Split Unicode into UTF-8. */ -enum utf8_state -utf8_split(wchar_t wc, struct utf8_data *ud) -{ - char s[MB_LEN_MAX]; - int slen; - - slen = wctomb(s, wc); - if (slen <= 0 || slen > (int)sizeof ud->data) - return (UTF8_ERROR); - - memcpy(ud->data, s, slen); - ud->size = slen; - - ud->width = utf8_width(wc); - return (UTF8_DONE); -} - /* * Encode len characters from src into dst, which is guaranteed to have four * bytes available for each character from src (for \abc or UTF-8) plus space From fd4d3e87938206d8d05509162c3f3a3c274bb478 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 26 May 2020 06:15:13 +0100 Subject: [PATCH 0461/1006] Set IUTF8 again when it exists. --- spawn.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spawn.c b/spawn.c index 9f65b329..7c279eff 100644 --- a/spawn.c +++ b/spawn.c @@ -395,6 +395,9 @@ spawn_pane(struct spawn_context *sc, char **cause) now.c_cc[VERASE] = '\177'; else now.c_cc[VERASE] = key; +#ifdef IUTF8 + now.c_iflag |= IUTF8; +#endif if (tcsetattr(STDIN_FILENO, TCSANOW, &now) != 0) _exit(1); From d73fcfc176b4032dd69d88e0bdef3565bbcb3eb4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 26 May 2020 08:49:36 +0100 Subject: [PATCH 0462/1006] Put the fix back for wcwidth() failing. --- utf8.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/utf8.c b/utf8.c index 353c90ba..9e727baa 100644 --- a/utf8.c +++ b/utf8.c @@ -244,12 +244,25 @@ utf8_width(struct utf8_data *ud, int *width) return (UTF8_ERROR); } *width = wcwidth(wc); - if (*width < 0 || *width > 0xff) { - log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, - *width); - return (UTF8_ERROR); + if (*width >= 0 && *width <= 0xff) + return (UTF8_DONE); + log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, *width); + +#ifndef __OpenBSD__ + /* + * Many platforms (particularly and inevitably OS X) have no width for + * relatively common characters (wcwidth() returns -1); assume width 1 + * in this case. This will be wrong for genuinely nonprintable + * characters, but they should be rare. We may pass through stuff that + * ideally we would block, but this is no worse than sending the same + * to the terminal without tmux. + */ + if (*width < 0) { + *width = 1; + return (UTF8_DONE); } - return (UTF8_DONE); +#endif + return (UTF8_ERROR); } /* From bc2e0cf7ff51c2ab13c7dcc792d25e11ba7a3ef4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 26 May 2020 08:54:05 +0100 Subject: [PATCH 0463/1006] Remove bad merge. --- utf8.c | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/utf8.c b/utf8.c index 9e727baa..6676e9f0 100644 --- a/utf8.c +++ b/utf8.c @@ -315,38 +315,7 @@ utf8_append(struct utf8_data *ud, u_char ch) return (UTF8_DONE); } -/* Get width of Unicode character. */ -static int -utf8_width(wchar_t wc) -{ - int width; - -#ifdef HAVE_UTF8PROC - width = utf8proc_wcwidth(wc); -#else - width = wcwidth(wc); -#endif - if (width < 0 || width > 0xff) { - log_debug("Unicode %04lx, wcwidth() %d", (long)wc, width); - -#ifndef __OpenBSD__ - /* - * Many platforms (particularly and inevitably OS X) have no - * width for relatively common characters (wcwidth() returns - * -1); assume width 1 in this case. This will be wrong for - * genuinely nonprintable characters, but they should be - * rare. We may pass through stuff that ideally we would block, - * but this is no worse than sending the same to the terminal - * without tmux. - */ - if (width < 0) - return (1); -#endif - return (-1); - } - return (width); -} - +/* * Encode len characters from src into dst, which is guaranteed to have four * bytes available for each character from src (for \abc or UTF-8) plus space * for \0. From ea610a311902b56c6466d79332592ab7f3dc501a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 26 May 2020 08:41:47 +0000 Subject: [PATCH 0464/1006] Pass the stdout file descriptor from the client as well as stdin and use them for control clients directly instead of passing everything via the client. --- client.c | 5 +++ control.c | 81 +++++++++++++++++++++++++++++++++++-------------- file.c | 8 +++-- server-client.c | 29 ++++++++++++------ tmux.h | 3 ++ 5 files changed, 91 insertions(+), 35 deletions(-) diff --git a/client.c b/client.c index 841e581e..d1a31de7 100644 --- a/client.c +++ b/client.c @@ -402,6 +402,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat) } else if (client_exitreason != CLIENT_EXIT_NONE) fprintf(stderr, "%s\n", client_exit_message()); setblocking(STDIN_FILENO, 1); + setblocking(STDOUT_FILENO, 1); + setblocking(STDERR_FILENO, 1); return (client_exitval); } @@ -429,6 +431,9 @@ client_send_identify(const char *ttynam, const char *cwd, int feat) if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); + if ((fd = dup(STDOUT_FILENO)) == -1) + fatal("dup failed"); + proc_send(client_peer, MSG_IDENTIFY_STDOUT, fd, NULL, 0); pid = getpid(); proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); diff --git a/control.c b/control.c index 750f1284..7e1a869b 100644 --- a/control.c +++ b/control.c @@ -23,10 +23,11 @@ #include #include #include +#include #include "tmux.h" -/* Control offsets. */ +/* Control client offset. */ struct control_offset { u_int pane; @@ -34,13 +35,16 @@ struct control_offset { int flags; #define CONTROL_OFFSET_OFF 0x1 - RB_ENTRY(control_offset) entry; + RB_ENTRY(control_offset) entry; }; RB_HEAD(control_offsets, control_offset); -/* Control state. */ +/* Control client state. */ struct control_state { - struct control_offsets offsets; + struct control_offsets offsets; + + struct bufferevent *read_event; + struct bufferevent *write_event; }; /* Compare client offsets. */ @@ -146,18 +150,24 @@ control_set_pane_off(struct client *c, struct window_pane *wp) void control_write(struct client *c, const char *fmt, ...) { - va_list ap; + struct control_state *cs = c->control_state; + va_list ap; + char *s; va_start(ap, fmt); - file_vprint(c, fmt, ap); - file_print(c, "\n"); + xvasprintf(&s, fmt, ap); va_end(ap); + + bufferevent_write(cs->write_event, s, strlen(s)); + bufferevent_write(cs->write_event, "\n", 1); + free(s); } /* Write output from a pane. */ void control_write_output(struct client *c, struct window_pane *wp) { + struct control_state *cs = c->control_state; struct control_offset *co; struct evbuffer *message; u_char *new_data; @@ -165,11 +175,6 @@ control_write_output(struct client *c, struct window_pane *wp) if (c->flags & CLIENT_CONTROL_NOOUTPUT) return; - - /* - * Only write input if the window pane is linked to a window belonging - * to the client's session. - */ if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) return; @@ -193,15 +198,15 @@ control_write_output(struct client *c, struct window_pane *wp) else evbuffer_add_printf(message, "%c", new_data[i]); } - evbuffer_add(message, "", 1); + evbuffer_add(message, "\n", 1); - control_write(c, "%s", EVBUFFER_DATA(message)); + bufferevent_write_buffer(cs->write_event, message); evbuffer_free(message); window_pane_update_used_data(wp, &co->offset, new_size, 1); } -/* Control error callback. */ +/* Control client error callback. */ static enum cmd_retval control_error(struct cmdq_item *item, void *data) { @@ -216,18 +221,27 @@ control_error(struct cmdq_item *item, void *data) return (CMD_RETURN_NORMAL); } -/* Control input callback. Read lines and fire commands. */ +/* Control client error callback. */ static void -control_callback(__unused struct client *c, __unused const char *path, - int read_error, int closed, struct evbuffer *buffer, __unused void *data) +control_error_callback(__unused struct bufferevent *bufev, + __unused short what, void *data) { + struct client *c = data; + + c->flags |= CLIENT_EXIT; +} + +/* Control client input callback. Read lines and fire commands. */ +static void +control_read_callback(__unused struct bufferevent *bufev, void *data) +{ + struct client *c = data; + struct control_state *cs = c->control_state; + struct evbuffer *buffer = cs->read_event->input; char *line, *error; struct cmdq_state *state; enum cmd_parse_status status; - if (closed || read_error != 0) - c->flags |= CLIENT_EXIT; - for (;;) { line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF); if (line == NULL) @@ -255,13 +269,30 @@ control_start(struct client *c) { struct control_state *cs; + if (c->flags & CLIENT_CONTROLCONTROL) { + close(c->out_fd); + c->out_fd = -1; + } else + setblocking(c->out_fd, 0); + setblocking(c->fd, 0); + cs = c->control_state = xcalloc(1, sizeof *cs); RB_INIT(&cs->offsets); - file_read(c, "-", control_callback, c); + cs->read_event = bufferevent_new(c->fd, control_read_callback, NULL, + control_error_callback, c); + bufferevent_enable(cs->read_event, EV_READ); if (c->flags & CLIENT_CONTROLCONTROL) - file_print(c, "\033P1000p"); + cs->write_event = cs->read_event; + else { + cs->write_event = bufferevent_new(c->out_fd, NULL, NULL, + control_error_callback, c); + } + bufferevent_enable(cs->write_event, EV_WRITE); + + if (c->flags & CLIENT_CONTROLCONTROL) + control_write(c, "\033P1000p"); } /* Stop control mode. */ @@ -270,6 +301,10 @@ control_stop(struct client *c) { struct control_state *cs = c->control_state; + if (~c->flags & CLIENT_CONTROLCONTROL) + bufferevent_free(cs->write_event); + bufferevent_free(cs->read_event); + control_free_offsets(c); free(cs); } diff --git a/file.c b/file.c index 69dbdee4..0d25db03 100644 --- a/file.c +++ b/file.c @@ -242,7 +242,9 @@ file_write(struct client *c, const char *path, int flags, const void *bdata, cf->path = xstrdup("-"); fd = STDOUT_FILENO; - if (c == NULL || c->flags & CLIENT_ATTACHED) { + if (c == NULL || + (c->flags & CLIENT_ATTACHED) || + (c->flags & CLIENT_CONTROL)) { cf->error = EBADF; goto done; } @@ -311,7 +313,9 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) cf->path = xstrdup("-"); fd = STDIN_FILENO; - if (c == NULL || c->flags & CLIENT_ATTACHED) { + if (c == NULL || + (c->flags & CLIENT_ATTACHED) || + (c->flags & CLIENT_CONTROL)) { cf->error = EBADF; goto done; } diff --git a/server-client.c b/server-client.c index 5c8f4940..d4fa92dc 100644 --- a/server-client.c +++ b/server-client.c @@ -223,7 +223,7 @@ server_client_create(int fd) c->environ = environ_create(); c->fd = -1; - c->cwd = NULL; + c->out_fd = -1; c->queue = cmdq_new(); RB_INIT(&c->windows); @@ -338,6 +338,8 @@ server_client_lost(struct client *c) proc_remove_peer(c->peer); c->peer = NULL; + if (c->out_fd != -1) + close(c->out_fd); if (c->fd != -1) { close(c->fd); c->fd = -1; @@ -1573,10 +1575,9 @@ server_client_check_pane_buffer(struct window_pane *wp) out: /* * If there is data remaining, and there are no clients able to consume - * it, do not read any more. This is true when 1) there are attached - * clients 2) all the clients are control clients 3) all of them have - * either the OFF flag set, or are otherwise not able to accept any - * more data for this pane. + * it, do not read any more. This is true when there are attached + * clients, all of which are control clients which are not able to + * accept any more data. */ if (off) bufferevent_disable(wp->event, EV_READ); @@ -1969,6 +1970,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) case MSG_IDENTIFY_TTYNAME: case MSG_IDENTIFY_CWD: case MSG_IDENTIFY_STDIN: + case MSG_IDENTIFY_STDOUT: case MSG_IDENTIFY_ENVIRON: case MSG_IDENTIFY_CLIENTPID: case MSG_IDENTIFY_DONE: @@ -2179,6 +2181,12 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->fd = imsg->fd; log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd); break; + case MSG_IDENTIFY_STDOUT: + if (datalen != 0) + fatalx("bad MSG_IDENTIFY_STDOUT size"); + c->out_fd = imsg->fd; + log_debug("client %p IDENTIFY_STDOUT %d", c, imsg->fd); + break; case MSG_IDENTIFY_ENVIRON: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); @@ -2207,11 +2215,9 @@ 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) { - close(c->fd); - c->fd = -1; + if (c->flags & CLIENT_CONTROL) control_start(c); - } else if (c->fd != -1) { + else if (c->fd != -1) { if (tty_init(&c->tty, c) != 0) { close(c->fd); c->fd = -1; @@ -2219,6 +2225,8 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) tty_resize(&c->tty); c->flags |= CLIENT_TERMINAL; } + close(c->out_fd); + c->out_fd = -1; } /* @@ -2335,7 +2343,8 @@ void server_client_set_flags(struct client *c, const char *flags) { char *s, *copy, *next; - int flag, not; + uint64_t flag; + int not; s = copy = xstrdup (flags); while ((next = strsep(&s, ",")) != NULL) { diff --git a/tmux.h b/tmux.h index a0369e34..4187ddd8 100644 --- a/tmux.h +++ b/tmux.h @@ -498,6 +498,7 @@ enum msgtype { MSG_IDENTIFY_CLIENTPID, MSG_IDENTIFY_CWD, MSG_IDENTIFY_FEATURES, + MSG_IDENTIFY_STDOUT, MSG_COMMAND = 200, MSG_DETACH, @@ -967,6 +968,7 @@ struct window_pane { int fd; struct bufferevent *event; + struct window_pane_offset offset; size_t base_offset; @@ -1581,6 +1583,7 @@ struct client { pid_t pid; int fd; + int out_fd; struct event event; int retval; From ca0166f26f52e87290fd3d96c10cc660e9ede437 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 26 May 2020 08:47:50 +0000 Subject: [PATCH 0465/1006] Do not try to use the last marked pane if it is invalid. --- cmd-select-pane.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 3b639e06..313deefe 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -129,7 +129,10 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) return (CMD_RETURN_NORMAL); - lastwp = marked_pane.wp; + if (server_check_marked()) + lastwp = marked_pane.wp; + else + lastwp = NULL; if (args_has(args, 'M') || server_is_marked(s, wl, wp)) server_clear_marked(); From 370f0bb98d81133e2d9c5d3d4e1aa2e7c69dfc3e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 26 May 2020 08:56:48 +0000 Subject: [PATCH 0466/1006] Remove leftover debug logging and fix comparison. --- grid.c | 1 - status.c | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/grid.c b/grid.c index acd1f475..a901de56 100644 --- a/grid.c +++ b/grid.c @@ -496,7 +496,6 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) gc->fg = gee->fg; gc->bg = gee->bg; gc->us = gee->us; - log_debug("!!! %x", gc->flags); utf8_to_data(gee->data, &gc->data); } return; diff --git a/status.c b/status.c index ad4d6cda..d143b658 100644 --- a/status.c +++ b/status.c @@ -1275,7 +1275,10 @@ process_key: append_key: if (key <= 0x1f || key >= KEYC_BASE) return (0); - utf8_to_data(key, &tmp); + if (key <= 0x7f) + utf8_set(&tmp, key); + else + utf8_to_data(key, &tmp); c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2, sizeof *c->prompt_buffer); From 392b381d1cec6d63c4baaa709243f760ff6c3403 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 26 May 2020 09:01:03 +0000 Subject: [PATCH 0467/1006] Apply -n when only one pane in the window. --- cmd-break-pane.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 9483aa7e..6d5041e8 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -80,6 +80,10 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } + if (args_has(args, 'n')) { + window_set_name(w, args_get(args, 'n')); + options_set_number(w->options, "automatic-rename", 0); + } server_unlink_window(src_s, wl); return (CMD_RETURN_NORMAL); } From ff6f2ff6d93f05f2edf4b6e63bcf08315f3a3c73 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 26 May 2020 12:50:03 +0000 Subject: [PATCH 0468/1006] Return new character properly when converting to data. --- utf8.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/utf8.c b/utf8.c index 9df74590..2a92003f 100644 --- a/utf8.c +++ b/utf8.c @@ -136,12 +136,16 @@ utf8_from_data(const struct utf8_data *ud, utf8_char *uc) u_int offset; if (ud->width != 1 && ud->width != 2) - return (utf8_space1.uc); + fatalx("invalid UTF-8 width"); + if (ud->size == 0) + fatalx("invalid UTF-8 size"); if (ud->size > UTF8_FLAG_SIZE) goto fail; - if (ud->size == 1) - return (utf8_build_one(ud->data[0], 1)); + if (ud->size == 1) { + *uc = utf8_build_one(ud->data[0], 1); + return (UTF8_DONE); + } m.flags = ud->size; if (ud->width == 2) From 2ced370bee234e00c3ece3b4110dcb71067df846 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 26 May 2020 13:19:21 +0000 Subject: [PATCH 0469/1006] Tweak some out of date bits, reported by bcgraham. --- tmux.1 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tmux.1 b/tmux.1 index e7131829..8fdb9360 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2596,8 +2596,7 @@ spreads the current pane and any panes next to it out evenly. .D1 (alias: Ic selectp ) Make pane .Ar target-pane -the active pane in window -.Ar target-window . +the active pane in its window. If one of .Fl D , .Fl L , @@ -3057,8 +3056,8 @@ User options may have any name, so long as they are prefixed with and be set to any string. For example: .Bd -literal -offset indent -$ tmux setw -q @foo "abc123" -$ tmux showw -v @foo +$ tmux set -wq @foo "abc123" +$ tmux show -wv @foo abc123 .Ed .Pp From f336599a3a0172924174c60c7411eea9b93e5544 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 27 May 2020 06:23:23 +0000 Subject: [PATCH 0470/1006] Make padding cell a valid character. --- grid.c | 1 - screen-write.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/grid.c b/grid.c index a901de56..6ca14cba 100644 --- a/grid.c +++ b/grid.c @@ -541,7 +541,6 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) gl->cellused = px + 1; gce = &gl->celldata[px]; - if (gc->flags & GRID_FLAG_PADDING) log_debug("!!! padding %d\n", grid_need_extended_cell(gce, gc)); if (grid_need_extended_cell(gce, gc)) grid_extended_cell(gl, gce, gc); else diff --git a/screen-write.c b/screen-write.c index 68c3aa76..33e3f975 100644 --- a/screen-write.c +++ b/screen-write.c @@ -39,7 +39,7 @@ static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, const struct utf8_data *, u_int *); static const struct grid_cell screen_write_pad_cell = { - { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0 + { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_PADDING, 8, 8, 0 }; struct screen_write_collect_item { From bda2a0282a6b7b1313d22165b44dded9873dfe89 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 27 May 2020 14:45:35 +0000 Subject: [PATCH 0471/1006] Fix ASCII keys with send-keys -l. --- cmd-send-keys.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 67af718a..b362fab5 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -118,9 +118,14 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, if (literal) { ud = utf8_fromcstr(s); for (loop = ud; loop->size != 0; loop++) { - if (utf8_from_data(loop, &uc) != UTF8_DONE) - continue; - after = cmd_send_keys_inject_key(item, after, uc); + if (loop->size == 1 && loop->data[0] <= 0x7f) + key = loop->data[0]; + else { + if (utf8_from_data(loop, &uc) != UTF8_DONE) + continue; + key = uc; + } + after = cmd_send_keys_inject_key(item, after, key); } free(ud); } From ce6b3a539dffa92ea014009670f6871cd0cf7232 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 28 May 2020 08:41:56 +0100 Subject: [PATCH 0472/1006] utf8proc_unicode_version is too new. --- proc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proc.c b/proc.c index 5b3fd735..ebc39386 100644 --- a/proc.c +++ b/proc.c @@ -190,14 +190,14 @@ proc_start(const char *name) log_debug("on %s %s %s", u.sysname, u.release, u.version); log_debug("using libevent %s (%s)" #ifdef HAVE_UTF8PROC - "; utf8proc %s (Unicode %s)" + "; utf8proc %s" #endif #ifdef NCURSES_VERSION "; ncurses " NCURSES_VERSION #endif , event_get_version(), event_get_method() #ifdef HAVE_UTF8PROC - , utf8proc_version (), utf8proc_unicode_version () + , utf8proc_version () #endif ); From 175e45005f4572b19a4aa34094f9a8c69ced5475 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 May 2020 13:42:13 +0000 Subject: [PATCH 0473/1006] Add -i to find-window to ignore case. --- cmd-find-window.c | 91 ++++++++++++++++++----------------------------- tmux.1 | 4 ++- 2 files changed, 38 insertions(+), 57 deletions(-) diff --git a/cmd-find-window.c b/cmd-find-window.c index e1faeb2f..4cd155e4 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -32,8 +32,8 @@ const struct cmd_entry cmd_find_window_entry = { .name = "find-window", .alias = "findw", - .args = { "CNrt:TZ", 1, 1 }, - .usage = "[-CNrTZ] " CMD_TARGET_PANE_USAGE " match-string", + .args = { "CiNrt:TZ", 1, 1 }, + .usage = "[-CiNrTZ] " CMD_TARGET_PANE_USAGE " match-string", .target = { 't', CMD_FIND_PANE, 0 }, @@ -47,7 +47,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self), *new_args; struct cmd_find_state *target = cmdq_get_target(item); struct window_pane *wp = target->wp; - const char *s = args->argv[0]; + const char *s = args->argv[0], *suffix = ""; char *filter, *argv = { NULL }; int C, N, T; @@ -55,62 +55,41 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) N = args_has(args, 'N'); T = args_has(args, 'T'); + if (args_has(args, 'r') && args_has(args, 'i')) + suffix = "/ri"; + else if (args_has(args, 'r')) + suffix = "/r"; + else if (args_has(args, 'i')) + suffix = "/i"; + if (!C && !N && !T) C = N = T = 1; - if (!args_has(args, 'r')) { - if (C && N && T) { - xasprintf(&filter, - "#{||:" - "#{C:%s},#{||:#{m:*%s*,#{window_name}}," - "#{m:*%s*,#{pane_title}}}}", - s, s, s); - } else if (C && N) { - xasprintf(&filter, - "#{||:#{C:%s},#{m:*%s*,#{window_name}}}", - s, s); - } else if (C && T) { - xasprintf(&filter, - "#{||:#{C:%s},#{m:*%s*,#{pane_title}}}", - s, s); - } else if (N && T) { - xasprintf(&filter, - "#{||:#{m:*%s*,#{window_name}}," - "#{m:*%s*,#{pane_title}}}", - s, s); - } else if (C) - xasprintf(&filter, "#{C:%s}", s); - else if (N) - xasprintf(&filter, "#{m:*%s*,#{window_name}}", s); - else - xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s); - } else { - if (C && N && T) { - xasprintf(&filter, - "#{||:" - "#{C/r:%s},#{||:#{m/r:%s,#{window_name}}," - "#{m/r:%s,#{pane_title}}}}", - s, s, s); - } else if (C && N) { - xasprintf(&filter, - "#{||:#{C/r:%s},#{m/r:%s,#{window_name}}}", - s, s); - } else if (C && T) { - xasprintf(&filter, - "#{||:#{C/r:%s},#{m/r:%s,#{pane_title}}}", - s, s); - } else if (N && T) { - xasprintf(&filter, - "#{||:#{m/r:%s,#{window_name}}," - "#{m/r:%s,#{pane_title}}}", - s, s); - } else if (C) - xasprintf(&filter, "#{C/r:%s}", s); - else if (N) - xasprintf(&filter, "#{m/r:%s,#{window_name}}", s); - else - xasprintf(&filter, "#{m/r:%s,#{pane_title}}", s); - } + if (C && N && T) { + xasprintf(&filter, + "#{||:" + "#{C%s:%s},#{||:#{m%s:*%s*,#{window_name}}," + "#{m%s:*%s*,#{pane_title}}}}", + suffix, s, suffix, s, suffix, s); + } else if (C && N) { + xasprintf(&filter, + "#{||:#{C%s:%s},#{m%s:*%s*,#{window_name}}}", + suffix, s, suffix, s); + } else if (C && T) { + xasprintf(&filter, + "#{||:#{C%s:%s},#{m%s:*%s*,#{pane_title}}}", + suffix, s, suffix, s); + } else if (N && T) { + xasprintf(&filter, + "#{||:#{m%s:*%s*,#{window_name}}," + "#{m%s:*%s*,#{pane_title}}}", + suffix, s, suffix, s); + } else if (C) + xasprintf(&filter, "#{C%s:%s}", suffix, s); + else if (N) + xasprintf(&filter, "#{m%s:*%s*,#{window_name}}", suffix, s); + else + xasprintf(&filter, "#{m%s:*%s*,#{pane_title}}", suffix, s); new_args = args_parse("", 1, &argv); if (args_has(args, 'Z')) diff --git a/tmux.1 b/tmux.1 index 8fdb9360..2ba3362b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2079,7 +2079,7 @@ With .Fl b , other commands are not blocked from running until the indicator is closed. .It Xo Ic find-window -.Op Fl rCNTZ +.Op Fl iCNrTZ .Op Fl t Ar target-pane .Ar match-string .Xc @@ -2098,6 +2098,8 @@ matches only visible window contents, matches only the window name and .Fl T matches only the window title. +.Fl i +makes the search ignore case. The default is .Fl CNT . .Fl Z From a54a88edd6fd893d4370feb9f9136e13096b891c Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Jun 2020 09:43:00 +0000 Subject: [PATCH 0474/1006] Instead of sending all data to control mode clients as fast as possible, add a limit of how much data will be sent to the client and try to use it for panes with some degree of fairness. GitHub issue 2217, with George Nachman. --- client.c | 16 +- cmd-capture-pane.c | 21 +- cmd-new-session.c | 5 +- cmd-queue.c | 9 +- control.c | 474 ++++++++++++++++++++++++++++++++++++--------- input.c | 2 +- server-client.c | 82 +++++--- server.c | 6 +- tmux.h | 25 ++- window-client.c | 2 +- window.c | 24 +-- 11 files changed, 490 insertions(+), 176 deletions(-) diff --git a/client.c b/client.c index d1a31de7..be9dccbf 100644 --- a/client.c +++ b/client.c @@ -384,6 +384,11 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat) client_exec(client_execshell, client_execcmd); } + /* Restore streams to blocking. */ + setblocking(STDIN_FILENO, 1); + setblocking(STDOUT_FILENO, 1); + setblocking(STDERR_FILENO, 1); + /* Print the exit message, if any, and exit. */ if (client_attached) { if (client_exitreason != CLIENT_EXIT_NONE) @@ -392,18 +397,17 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat) ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); - } else if (client_flags & CLIENT_CONTROLCONTROL) { + } else if (client_flags & CLIENT_CONTROL) { if (client_exitreason != CLIENT_EXIT_NONE) printf("%%exit %s\n", client_exit_message()); else printf("%%exit\n"); - printf("\033\\"); - tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); + if (client_flags & CLIENT_CONTROLCONTROL) { + printf("\033\\"); + tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); + } } else if (client_exitreason != CLIENT_EXIT_NONE) fprintf(stderr, "%s\n", client_exit_message()); - setblocking(STDIN_FILENO, 1); - setblocking(STDOUT_FILENO, 1); - setblocking(STDERR_FILENO, 1); return (client_exitval); } diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 588b0fd5..6f37bc8f 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -214,15 +214,20 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); if (args_has(args, 'p')) { - if (!file_can_print(c)) { - cmdq_error(item, "can't write output to client"); - free(buf); - return (CMD_RETURN_ERROR); - } - file_print_buffer(c, buf, len); - if (args_has(args, 'P') && len > 0) + if (len > 0 && buf[len - 1] == '\n') + len--; + if (c->flags & CLIENT_CONTROL) + control_write(c, "%.*s", (int)len, buf); + else { + if (!file_can_print(c)) { + cmdq_error(item, "can't write to client"); + free(buf); + return (CMD_RETURN_ERROR); + } + file_print_buffer(c, buf, len); file_print(c, "\n"); - free(buf); + free(buf); + } } else { bufname = NULL; if (args_has(args, 'b')) diff --git a/cmd-new-session.c b/cmd-new-session.c index be29122d..cc3494de 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -165,7 +165,10 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) * the terminal as that calls tcsetattr() to prepare for tmux taking * over. */ - if (!detached && !already_attached && c->fd != -1) { + if (!detached && + !already_attached && + c->fd != -1 && + (~c->flags & CLIENT_CONTROL)) { if (server_client_check_nested(cmdq_get_client(item))) { cmdq_error(item, "sessions should be nested with care, " "unset $TMUX to force"); diff --git a/cmd-queue.c b/cmd-queue.c index b0c70428..693f7d90 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -780,7 +780,7 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags) u_int number = item->number; if (c != NULL && (c->flags & CLIENT_CONTROL)) - file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags); + control_write(c, "%%%s %ld %u %d", guard, t, number, flags); } /* Show message from command. */ @@ -807,7 +807,10 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) msg = utf8_sanitize(tmp); free(tmp); } - file_print(c, "%s\n", msg); + if (c->flags & CLIENT_CONTROL) + control_write(c, "%s", msg); + else + file_print(c, "%s\n", msg); } else { wp = server_client_get_pane(c); wme = TAILQ_FIRST(&wp->modes); @@ -849,7 +852,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) free(tmp); } if (c->flags & CLIENT_CONTROL) - file_print(c, "%s\n", msg); + control_write(c, "%s", msg); else file_error(c, "%s\n", msg); c->retval = 1; diff --git a/control.c b/control.c index 7e1a869b..c1e0b7eb 100644 --- a/control.c +++ b/control.c @@ -27,112 +27,183 @@ #include "tmux.h" -/* Control client offset. */ -struct control_offset { - u_int pane; +/* + * Block of data to output. Each client has one "all" queue of blocks and + * another queue for each pane (in struct client_offset). %output blocks are + * added to both queues and other output lines (notifications) added only to + * the client queue. + * + * When a client becomes writeable, data from blocks on the pane queue are sent + * up to the maximum size (CLIENT_BUFFER_HIGH). If a block is entirely written, + * it is removed from both pane and client queues and if this means non-%output + * blocks are now at the head of the client queue, they are written. + * + * This means a %output block holds up any subsequent non-%output blocks until + * it is written which enforces ordering even if the client cannot accept the + * entire block in one go. + */ +struct control_block { + size_t size; + char *line; - struct window_pane_offset offset; - int flags; -#define CONTROL_OFFSET_OFF 0x1 + TAILQ_ENTRY(control_block) entry; + TAILQ_ENTRY(control_block) all_entry; + }; - RB_ENTRY(control_offset) entry; +/* Control client pane. */ +struct control_pane { + u_int pane; + + /* + * Offsets into the pane data. The first (offset) is the data we have + * written; the second (queued) the data we have queued (pointed to by + * a block). + */ + struct window_pane_offset offset; + struct window_pane_offset queued; + + int flags; +#define CONTROL_PANE_OFF 0x1 + + int pending_flag; + TAILQ_ENTRY(control_pane) pending_entry; + + TAILQ_HEAD(, control_block) blocks; + + RB_ENTRY(control_pane) entry; }; -RB_HEAD(control_offsets, control_offset); +RB_HEAD(control_panes, control_pane); /* Control client state. */ struct control_state { - struct control_offsets offsets; + struct control_panes panes; - struct bufferevent *read_event; - struct bufferevent *write_event; + TAILQ_HEAD(, control_pane) pending_list; + u_int pending_count; + + TAILQ_HEAD(, control_block) all_blocks; + + struct bufferevent *read_event; + struct bufferevent *write_event; }; -/* Compare client offsets. */ +/* Low watermark. */ +#define CONTROL_BUFFER_LOW 512 +#define CONTROL_BUFFER_HIGH 8192 + +/* Minimum to write to each client. */ +#define CONTROL_WRITE_MINIMUM 32 + +/* Flags to ignore client. */ +#define CONTROL_IGNORE_FLAGS \ + (CLIENT_CONTROL_NOOUTPUT| \ + CLIENT_UNATTACHEDFLAGS) + +/* Compare client panes. */ static int -control_offset_cmp(struct control_offset *co1, struct control_offset *co2) +control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2) { - if (co1->pane < co2->pane) + if (cp1->pane < cp2->pane) return (-1); - if (co1->pane > co2->pane) + if (cp1->pane > cp2->pane) return (1); return (0); } -RB_GENERATE_STATIC(control_offsets, control_offset, entry, control_offset_cmp); +RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp); + +/* Free a block. */ +static void +control_free_block(struct control_state *cs, struct control_block *cb) +{ + free(cb->line); + TAILQ_REMOVE(&cs->all_blocks, cb, all_entry); + free(cb); +} /* Get pane offsets for this client. */ -static struct control_offset * -control_get_offset(struct client *c, struct window_pane *wp) +static struct control_pane * +control_get_pane(struct client *c, struct window_pane *wp) { struct control_state *cs = c->control_state; - struct control_offset co = { .pane = wp->id }; + struct control_pane cp = { .pane = wp->id }; - return (RB_FIND(control_offsets, &cs->offsets, &co)); + return (RB_FIND(control_panes, &cs->panes, &cp)); } /* Add pane offsets for this client. */ -static struct control_offset * -control_add_offset(struct client *c, struct window_pane *wp) +static struct control_pane * +control_add_pane(struct client *c, struct window_pane *wp) { struct control_state *cs = c->control_state; - struct control_offset *co; + struct control_pane *cp; - co = control_get_offset(c, wp); - if (co != NULL) - return (co); + cp = control_get_pane(c, wp); + if (cp != NULL) + return (cp); - co = xcalloc(1, sizeof *co); - co->pane = wp->id; - RB_INSERT(control_offsets, &cs->offsets, co); - memcpy(&co->offset, &wp->offset, sizeof co->offset); - return (co); + cp = xcalloc(1, sizeof *cp); + cp->pane = wp->id; + RB_INSERT(control_panes, &cs->panes, cp); + + memcpy(&cp->offset, &wp->offset, sizeof cp->offset); + memcpy(&cp->queued, &wp->offset, sizeof cp->queued); + TAILQ_INIT(&cp->blocks); + + return (cp); } -/* Free control offsets. */ +/* Reset control offsets. */ void -control_free_offsets(struct client *c) +control_reset_offsets(struct client *c) { struct control_state *cs = c->control_state; - struct control_offset *co, *co1; + struct control_pane *cp, *cp1; - RB_FOREACH_SAFE(co, control_offsets, &cs->offsets, co1) { - RB_REMOVE(control_offsets, &cs->offsets, co); - free(co); + RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) { + RB_REMOVE(control_panes, &cs->panes, cp); + free(cp); } + + TAILQ_INIT(&cs->pending_list); + cs->pending_count = 0; } /* Get offsets for client. */ struct window_pane_offset * control_pane_offset(struct client *c, struct window_pane *wp, int *off) { - struct control_offset *co; + struct control_state *cs = c->control_state; + struct control_pane *cp; if (c->flags & CLIENT_CONTROL_NOOUTPUT) { *off = 0; return (NULL); } - co = control_get_offset(c, wp); - if (co == NULL) { + cp = control_get_pane(c, wp); + if (cp == NULL) { *off = 0; return (NULL); } - if (co->flags & CONTROL_OFFSET_OFF) { + if (cp->flags & CONTROL_PANE_OFF) { *off = 1; return (NULL); } - return (&co->offset); + *off = (EVBUFFER_LENGTH(cs->write_event->output) >= CONTROL_BUFFER_LOW); + return (&cp->offset); } /* Set pane as on. */ void control_set_pane_on(struct client *c, struct window_pane *wp) { - struct control_offset *co; + struct control_pane *cp; - co = control_get_offset(c, wp); - if (co != NULL) { - co->flags &= ~CONTROL_OFFSET_OFF; - memcpy(&co->offset, &wp->offset, sizeof co->offset); + cp = control_get_pane(c, wp); + if (cp != NULL) { + cp->flags &= ~CONTROL_PANE_OFF; + memcpy(&cp->offset, &wp->offset, sizeof cp->offset); + memcpy(&cp->queued, &wp->offset, sizeof cp->queued); } } @@ -140,10 +211,27 @@ control_set_pane_on(struct client *c, struct window_pane *wp) void control_set_pane_off(struct client *c, struct window_pane *wp) { - struct control_offset *co; + struct control_pane *cp; - co = control_add_offset(c, wp); - co->flags |= CONTROL_OFFSET_OFF; + cp = control_add_pane(c, wp); + cp->flags |= CONTROL_PANE_OFF; +} + +/* Write a line. */ +static void +control_vwrite(struct client *c, const char *fmt, va_list ap) +{ + struct control_state *cs = c->control_state; + char *s; + + xvasprintf(&s, fmt, ap); + log_debug("%s: %s: writing line: %s", __func__, c->name, s); + + bufferevent_write(cs->write_event, s, strlen(s)); + bufferevent_write(cs->write_event, "\n", 1); + + bufferevent_enable(cs->write_event, EV_WRITE); + free(s); } /* Write a line. */ @@ -151,16 +239,25 @@ void control_write(struct client *c, const char *fmt, ...) { struct control_state *cs = c->control_state; + struct control_block *cb; va_list ap; - char *s; va_start(ap, fmt); - xvasprintf(&s, fmt, ap); - va_end(ap); - bufferevent_write(cs->write_event, s, strlen(s)); - bufferevent_write(cs->write_event, "\n", 1); - free(s); + if (TAILQ_EMPTY(&cs->all_blocks)) { + control_vwrite(c, fmt, ap); + va_end(ap); + return; + } + + cb = xcalloc(1, sizeof *cb); + xvasprintf(&cb->line, fmt, ap); + TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); + + log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line); + bufferevent_enable(cs->write_event, EV_WRITE); + + va_end(ap); } /* Write output from a pane. */ @@ -168,42 +265,50 @@ void control_write_output(struct client *c, struct window_pane *wp) { struct control_state *cs = c->control_state; - struct control_offset *co; - struct evbuffer *message; - u_char *new_data; - size_t new_size, i; + struct control_pane *cp; + struct control_block *cb; + size_t new_size; - if (c->flags & CLIENT_CONTROL_NOOUTPUT) - return; if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) return; - co = control_add_offset(c, wp); - if (co->flags & CONTROL_OFFSET_OFF) { - window_pane_update_used_data(wp, &co->offset, SIZE_MAX, 1); + if (c->flags & CONTROL_IGNORE_FLAGS) { + cp = control_get_pane(c, wp); + if (cp != NULL) + goto ignore; return; } - new_data = window_pane_get_new_data(wp, &co->offset, &new_size); + cp = control_add_pane(c, wp); + if (cp->flags & CONTROL_PANE_OFF) + goto ignore; + + window_pane_get_new_data(wp, &cp->queued, &new_size); if (new_size == 0) return; + window_pane_update_used_data(wp, &cp->queued, new_size); - message = evbuffer_new(); - if (message == NULL) - fatalx("out of memory"); - evbuffer_add_printf(message, "%%output %%%u ", wp->id); + cb = xcalloc(1, sizeof *cb); + cb->size = new_size; + TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); - for (i = 0; i < new_size; i++) { - if (new_data[i] < ' ' || new_data[i] == '\\') - evbuffer_add_printf(message, "\\%03o", new_data[i]); - else - evbuffer_add_printf(message, "%c", new_data[i]); + TAILQ_INSERT_TAIL(&cp->blocks, cb, entry); + log_debug("%s: %s: new output block of %zu for %%%u", __func__, c->name, + cb->size, wp->id); + + if (!cp->pending_flag) { + log_debug("%s: %s: %%%u now pending", __func__, c->name, + wp->id); + TAILQ_INSERT_TAIL(&cs->pending_list, cp, pending_entry); + cp->pending_flag = 1; + cs->pending_count++; } - evbuffer_add(message, "\n", 1); + bufferevent_enable(cs->write_event, EV_WRITE); + return; - bufferevent_write_buffer(cs->write_event, message); - evbuffer_free(message); - - window_pane_update_used_data(wp, &co->offset, new_size, 1); +ignore: + log_debug("%s: %s: ignoring pane %%%u", __func__, c->name, wp->id); + window_pane_update_used_data(wp, &cp->offset, SIZE_MAX); + window_pane_update_used_data(wp, &cp->queued, SIZE_MAX); } /* Control client error callback. */ @@ -246,8 +351,8 @@ control_read_callback(__unused struct bufferevent *bufev, void *data) line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF); if (line == NULL) break; - log_debug("%s: %s", __func__, line); - if (*line == '\0') { /* empty line exit */ + log_debug("%s: %s: %s", __func__, c->name, line); + if (*line == '\0') { /* empty line detach */ free(line); c->flags |= CLIENT_EXIT; break; @@ -263,6 +368,168 @@ control_read_callback(__unused struct bufferevent *bufev, void *data) } } +/* Does this control client have outstanding data to write? */ +int +control_all_done(struct client *c) +{ + struct control_state *cs = c->control_state; + + if (!TAILQ_EMPTY(&cs->all_blocks)) + return (0); + return (EVBUFFER_LENGTH(cs->write_event->output) == 0); +} + +/* Flush all blocks until output. */ +static void +control_flush_all_blocks(struct client *c) +{ + struct control_state *cs = c->control_state; + struct control_block *cb, *cb1; + + TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) { + if (cb->size != 0) + break; + log_debug("%s: %s: flushing line: %s", __func__, c->name, + cb->line); + + bufferevent_write(cs->write_event, cb->line, strlen(cb->line)); + bufferevent_write(cs->write_event, "\n", 1); + control_free_block(cs, cb); + } +} + +/* Append data to buffer. */ +static struct evbuffer * +control_append_data(struct control_pane *cp, struct evbuffer *message, + struct window_pane *wp, size_t size) +{ + u_char *new_data; + size_t new_size; + u_int i; + + if (message == NULL) { + message = evbuffer_new(); + if (message == NULL) + fatalx("out of memory"); + evbuffer_add_printf(message, "%%output %%%u ", wp->id); + } + + new_data = window_pane_get_new_data(wp, &cp->offset, &new_size); + if (new_size < size) + fatalx("not enough data: %zu < %zu", new_size, size); + for (i = 0; i < size; i++) { + if (new_data[i] < ' ' || new_data[i] == '\\') + evbuffer_add_printf(message, "\\%03o", new_data[i]); + else + evbuffer_add_printf(message, "%c", new_data[i]); + } + window_pane_update_used_data(wp, &cp->offset, size); + return (message); +} + +/* Write buffer. */ +static void +control_write_data(struct client *c, struct evbuffer *message) +{ + struct control_state *cs = c->control_state; + + log_debug("%s: %s: %.*s", __func__, c->name, + (int)EVBUFFER_LENGTH(message), EVBUFFER_DATA(message)); + + evbuffer_add(message, "\n", 1); + bufferevent_write_buffer(cs->write_event, message); + evbuffer_free(message); +} + +/* Write output to client. */ +static int +control_write_pending(struct client *c, struct control_pane *cp, size_t limit) +{ + struct control_state *cs = c->control_state; + struct session *s = c->session; + struct window_pane *wp = NULL; + struct evbuffer *message = NULL; + size_t used = 0, size; + struct control_block *cb, *cb1; + + if (s == NULL || + (wp = window_pane_find_by_id(cp->pane)) == NULL || + winlink_find_by_window(&s->windows, wp->window) == NULL) { + TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) + control_free_block(cs, cb); + control_flush_all_blocks(c); + return (0); + } + + while (used != limit && !TAILQ_EMPTY(&cp->blocks)) { + cb = TAILQ_FIRST(&cp->blocks); + log_debug("%s: %s: output block %zu for %%%u (used %zu/%zu)", + __func__, c->name, cb->size, cp->pane, used, limit); + + size = cb->size; + if (size > limit - used) + size = limit - used; + used += size; + + message = control_append_data(cp, message, wp, size); + + cb->size -= size; + if (cb->size == 0) { + TAILQ_REMOVE(&cp->blocks, cb, entry); + control_free_block(cs, cb); + + cb = TAILQ_FIRST(&cs->all_blocks); + if (cb != NULL && cb->size == 0) { + if (wp != NULL && message != NULL) { + control_write_data(c, message); + message = NULL; + } + control_flush_all_blocks(c); + } + } + } + if (message != NULL) + control_write_data(c, message); + return (!TAILQ_EMPTY(&cp->blocks)); +} + +/* Control client write callback. */ +static void +control_write_callback(__unused struct bufferevent *bufev, void *data) +{ + struct client *c = data; + struct control_state *cs = c->control_state; + struct control_pane *cp, *cp1; + struct evbuffer *evb = cs->write_event->output; + size_t space, limit; + + control_flush_all_blocks(c); + + while (EVBUFFER_LENGTH(evb) < CONTROL_BUFFER_HIGH) { + if (cs->pending_count == 0) + break; + space = CONTROL_BUFFER_HIGH - EVBUFFER_LENGTH(evb); + log_debug("%s: %s: %zu bytes available, %u panes", __func__, + c->name, space, cs->pending_count); + + limit = (space / cs->pending_count / 3); /* 3 bytes for \xxx */ + if (limit < CONTROL_WRITE_MINIMUM) + limit = CONTROL_WRITE_MINIMUM; + + TAILQ_FOREACH_SAFE(cp, &cs->pending_list, pending_entry, cp1) { + if (EVBUFFER_LENGTH(evb) >= CONTROL_BUFFER_HIGH) + break; + if (control_write_pending(c, cp, limit)) + continue; + TAILQ_REMOVE(&cs->pending_list, cp, pending_entry); + cp->pending_flag = 0; + cs->pending_count--; + } + } + if (EVBUFFER_LENGTH(evb) == 0) + bufferevent_disable(cs->write_event, EV_WRITE); +} + /* Initialize for control mode. */ void control_start(struct client *c) @@ -277,22 +544,43 @@ control_start(struct client *c) setblocking(c->fd, 0); cs = c->control_state = xcalloc(1, sizeof *cs); - RB_INIT(&cs->offsets); + RB_INIT(&cs->panes); + TAILQ_INIT(&cs->pending_list); + TAILQ_INIT(&cs->all_blocks); - cs->read_event = bufferevent_new(c->fd, control_read_callback, NULL, - control_error_callback, c); + cs->read_event = bufferevent_new(c->fd, control_read_callback, + control_write_callback, control_error_callback, c); bufferevent_enable(cs->read_event, EV_READ); if (c->flags & CLIENT_CONTROLCONTROL) cs->write_event = cs->read_event; else { - cs->write_event = bufferevent_new(c->out_fd, NULL, NULL, - control_error_callback, c); + cs->write_event = bufferevent_new(c->out_fd, NULL, + control_write_callback, control_error_callback, c); } - bufferevent_enable(cs->write_event, EV_WRITE); + bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW, + 0); - if (c->flags & CLIENT_CONTROLCONTROL) - control_write(c, "\033P1000p"); + if (c->flags & CLIENT_CONTROLCONTROL) { + bufferevent_write(cs->write_event, "\033P1000p", 7); + bufferevent_enable(cs->write_event, EV_WRITE); + } +} + +/* Flush all output for a client that is detaching. */ +void +control_flush(struct client *c) +{ + struct control_state *cs = c->control_state; + struct control_pane *cp; + struct control_block *cb, *cb1; + + RB_FOREACH(cp, control_panes, &cs->panes) { + TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { + TAILQ_REMOVE(&cp->blocks, cb, entry); + control_free_block(cs, cb); + } + } } /* Stop control mode. */ @@ -300,11 +588,15 @@ void control_stop(struct client *c) { struct control_state *cs = c->control_state; + struct control_block *cb, *cb1; if (~c->flags & CLIENT_CONTROLCONTROL) bufferevent_free(cs->write_event); bufferevent_free(cs->read_event); - control_free_offsets(c); + TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) + control_free_block(cs, cb); + control_reset_offsets(c); + free(cs); } diff --git a/input.c b/input.c index 7bb69663..46d8734d 100644 --- a/input.c +++ b/input.c @@ -947,7 +947,7 @@ input_parse_pane(struct window_pane *wp) new_data = window_pane_get_new_data(wp, &wp->offset, &new_size); input_parse_buffer(wp, new_data, new_size); - window_pane_update_used_data(wp, &wp->offset, new_size, 1); + window_pane_update_used_data(wp, &wp->offset, new_size); } /* Parse given input. */ diff --git a/server-client.c b/server-client.c index d4fa92dc..5218340e 100644 --- a/server-client.c +++ b/server-client.c @@ -58,6 +58,9 @@ static void server_client_dispatch_read_data(struct client *, static void server_client_dispatch_read_done(struct client *, struct imsg *); +/* Maximum data allowed to be held for a pane for a control client. */ +#define SERVER_CLIENT_PANE_LIMIT 16777216 + /* Compare client windows. */ static int server_client_window_cmp(struct client_window *cw1, @@ -80,7 +83,7 @@ server_client_how_many(void) n = 0; TAILQ_FOREACH(c, &clients, entry) { - if (c->session != NULL && (~c->flags & CLIENT_DETACHING)) + if (c->session != NULL && (~c->flags & CLIENT_UNATTACHEDFLAGS)) n++; } return (n); @@ -386,7 +389,7 @@ server_client_suspend(struct client *c) { struct session *s = c->session; - if (s == NULL || (c->flags & CLIENT_DETACHING)) + if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) return; tty_stop_tty(&c->tty); @@ -400,12 +403,14 @@ server_client_detach(struct client *c, enum msgtype msgtype) { struct session *s = c->session; - if (s == NULL || (c->flags & CLIENT_DETACHING)) + if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) return; - c->flags |= CLIENT_DETACHING; - notify_client("client-detached", c); - proc_send(c->peer, msgtype, -1, s->name, strlen(s->name) + 1); + c->flags |= CLIENT_EXIT; + + c->exit_type = CLIENT_EXIT_DETACH; + c->exit_msgtype = msgtype; + c->exit_session = xstrdup(s->name); } /* Execute command to replace a client. */ @@ -1507,12 +1512,12 @@ server_client_check_pane_buffer(struct window_pane *wp) u_int attached_clients = 0; /* - * Work out the minimum acknowledged size. This is the most that can be - * removed from the buffer. + * Work out the minimum used size. This is the most that can be removed + * from the buffer. */ - minimum = wp->offset.acknowledged; - if (wp->pipe_fd != -1 && wp->pipe_offset.acknowledged < minimum) - minimum = wp->pipe_offset.acknowledged; + minimum = wp->offset.used; + if (wp->pipe_fd != -1 && wp->pipe_offset.used < minimum) + minimum = wp->pipe_offset.used; TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL) continue; @@ -1530,11 +1535,13 @@ server_client_check_pane_buffer(struct window_pane *wp) if (!flag) off = 0; - log_debug("%s: %s has %zu bytes used, %zu bytes acknowledged " - "for %%%u", __func__, c->name, wpo->used, wpo->acknowledged, - wp->id); - if (wpo->acknowledged < minimum) - minimum = wpo->acknowledged; + log_debug("%s: %s has %zu bytes used for %%%u", __func__, + c->name, wpo->used - wp->base_offset, wp->id); + if (wpo->used - wp->base_offset > SERVER_CLIENT_PANE_LIMIT) { + control_flush(c); + c->flags |= CLIENT_EXIT; + } else if (wpo->used < minimum) + minimum = wpo->used; } if (attached_clients == 0) off = 0; @@ -1543,8 +1550,8 @@ server_client_check_pane_buffer(struct window_pane *wp) goto out; /* Drain the buffer. */ - log_debug("%s: %%%u has %zu minimum (of %zu) bytes acknowledged", - __func__, wp->id, minimum, EVBUFFER_LENGTH(evb)); + log_debug("%s: %%%u has %zu minimum (of %zu) bytes used", __func__, + wp->id, minimum, EVBUFFER_LENGTH(evb)); evbuffer_drain(evb, minimum); /* @@ -1553,20 +1560,15 @@ server_client_check_pane_buffer(struct window_pane *wp) */ if (wp->base_offset > SIZE_MAX - minimum) { log_debug("%s: %%%u base offset has wrapped", __func__, wp->id); - wp->offset.acknowledged -= wp->base_offset; wp->offset.used -= wp->base_offset; - if (wp->pipe_fd != -1) { - wp->pipe_offset.acknowledged -= wp->base_offset; + if (wp->pipe_fd != -1) wp->pipe_offset.used -= wp->base_offset; - } TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL || (~c->flags & CLIENT_CONTROL)) continue; wpo = control_pane_offset(c, wp, &flag); - if (wpo != NULL && !flag) { - wpo->acknowledged -= wp->base_offset; + if (wpo != NULL && !flag) wpo->used -= wp->base_offset; - } } wp->base_offset = minimum; } else @@ -1579,6 +1581,7 @@ out: * clients, all of which are control clients which are not able to * accept any more data. */ + log_debug("%s: pane %%%u is %s", __func__, wp->id, off ? "off" : "on"); if (off) bufferevent_disable(wp->event, EV_READ); else @@ -1770,12 +1773,16 @@ static void server_client_check_exit(struct client *c) { struct client_file *cf; + const char *name = c->exit_session; - if (~c->flags & CLIENT_EXIT) - return; - if (c->flags & CLIENT_EXITED) + if ((c->flags & CLIENT_EXITED) || (~c->flags & CLIENT_EXIT)) return; + if (c->flags & CLIENT_CONTROL) { + control_flush(c); + if (!control_all_done(c)) + return; + } RB_FOREACH(cf, client_files, &c->files) { if (EVBUFFER_LENGTH(cf->buffer) != 0) return; @@ -1783,8 +1790,20 @@ server_client_check_exit(struct client *c) if (c->flags & CLIENT_ATTACHED) notify_client("client-detached", c); - proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); c->flags |= CLIENT_EXITED; + + switch (c->exit_type) { + case CLIENT_EXIT_RETURN: + proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); + break; + case CLIENT_EXIT_SHUTDOWN: + proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); + break; + case CLIENT_EXIT_DETACH: + proc_send(c->peer, c->exit_msgtype, -1, name, strlen(name) + 1); + break; + } + free(c->exit_session); } /* Redraw timer callback. */ @@ -1996,7 +2015,6 @@ server_client_dispatch(struct imsg *imsg, void *arg) case MSG_EXITING: if (datalen != 0) fatalx("bad MSG_EXITING size"); - c->session = NULL; tty_close(&c->tty); proc_send(c->peer, MSG_EXITED, -1, NULL, 0); @@ -2050,7 +2068,7 @@ server_client_command_done(struct cmdq_item *item, __unused void *data) if (~c->flags & CLIENT_ATTACHED) c->flags |= CLIENT_EXIT; - else if (~c->flags & CLIENT_DETACHING) + else if (~c->flags & CLIENT_EXIT) tty_send_requests(&c->tty); return (CMD_RETURN_NORMAL); } @@ -2372,7 +2390,7 @@ server_client_set_flags(struct client *c, const char *flags) else c->flags |= flag; if (flag == CLIENT_CONTROL_NOOUTPUT) - control_free_offsets(c); + control_reset_offsets(c); } free(copy); } diff --git a/server.c b/server.c index f07479ae..67d47f23 100644 --- a/server.c +++ b/server.c @@ -199,6 +199,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, "tty ps", NULL) != 0) fatal("pledge failed"); + input_key_build(); RB_INIT(&windows); RB_INIT(&all_window_panes); TAILQ_INIT(&clients); @@ -294,9 +295,8 @@ server_send_exit(void) if (c->flags & CLIENT_SUSPENDED) server_client_lost(c); else { - if (c->flags & CLIENT_ATTACHED) - notify_client("client-detached", c); - proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); + c->flags |= CLIENT_EXIT; + c->exit_type = CLIENT_EXIT_SHUTDOWN; } c->session = NULL; } diff --git a/tmux.h b/tmux.h index 4187ddd8..a14d4ac2 100644 --- a/tmux.h +++ b/tmux.h @@ -85,9 +85,6 @@ struct winlink; /* Automatic name refresh interval, in microseconds. Must be < 1 second. */ #define NAME_INTERVAL 500000 -/* Maximum size of data to hold from a pane. */ -#define READ_SIZE 8192 - /* Default pixel cell sizes. */ #define DEFAULT_XPIXEL 16 #define DEFAULT_YPIXEL 32 @@ -915,7 +912,6 @@ struct window_mode_entry { /* Offsets into pane buffer. */ struct window_pane_offset { size_t used; - size_t acknowledged; }; /* Child window structure. */ @@ -1627,7 +1623,7 @@ struct client { #define CLIENT_DEAD 0x200 #define CLIENT_REDRAWBORDERS 0x400 #define CLIENT_READONLY 0x800 -#define CLIENT_DETACHING 0x1000 +/* 0x1000 unused */ #define CLIENT_CONTROL 0x2000 #define CLIENT_CONTROLCONTROL 0x4000 #define CLIENT_FOCUSED 0x8000 @@ -1657,12 +1653,21 @@ struct client { #define CLIENT_UNATTACHEDFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ - CLIENT_DETACHING) + CLIENT_EXIT) #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ - CLIENT_DETACHING) + CLIENT_EXIT) uint64_t flags; + + enum { + CLIENT_EXIT_RETURN, + CLIENT_EXIT_SHUTDOWN, + CLIENT_EXIT_DETACH + } exit_type; + enum msgtype exit_msgtype; + char *exit_session; + struct key_table *keytable; uint64_t redraw_panes; @@ -2712,8 +2717,6 @@ int window_pane_start_input(struct window_pane *, void *window_pane_get_new_data(struct window_pane *, struct window_pane_offset *, size_t *); void window_pane_update_used_data(struct window_pane *, - struct window_pane_offset *, size_t, int); -void window_pane_acknowledge_data(struct window_pane *, struct window_pane_offset *, size_t); /* layout.c */ @@ -2829,15 +2832,17 @@ char *default_window_name(struct window *); char *parse_window_name(const char *); /* control.c */ +void control_flush(struct client *); void control_start(struct client *); void control_stop(struct client *); void control_set_pane_on(struct client *, struct window_pane *); void control_set_pane_off(struct client *, struct window_pane *); struct window_pane_offset *control_pane_offset(struct client *, struct window_pane *, int *); -void control_free_offsets(struct client *); +void control_reset_offsets(struct client *); void printflike(2, 3) control_write(struct client *, const char *, ...); void control_write_output(struct client *, struct window_pane *); +int control_all_done(struct client *); /* control-notify.c */ void control_notify_input(struct client *, struct window_pane *, diff --git a/window-client.c b/window-client.c index 159efa5a..5e02462b 100644 --- a/window-client.c +++ b/window-client.c @@ -166,7 +166,7 @@ window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, data->item_size = 0; TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL || (c->flags & (CLIENT_DETACHING))) + if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) continue; item = window_client_add_item(data); diff --git a/window.c b/window.c index c7a17551..020ae4bf 100644 --- a/window.c +++ b/window.c @@ -944,16 +944,17 @@ window_pane_read_callback(__unused struct bufferevent *bufev, void *data) new_data = window_pane_get_new_data(wp, wpo, &new_size); if (new_size > 0) { bufferevent_write(wp->pipe_event, new_data, new_size); - window_pane_update_used_data(wp, wpo, new_size, 1); + window_pane_update_used_data(wp, wpo, new_size); } } log_debug("%%%u has %zu bytes", wp->id, size); TAILQ_FOREACH(c, &clients, entry) { - if (c->session != NULL && c->flags & CLIENT_CONTROL) + if (c->session != NULL && (c->flags & CLIENT_CONTROL)) control_write_output(c, wp); } input_parse_pane(wp); + bufferevent_disable(wp->event, EV_READ); } static void @@ -978,7 +979,6 @@ window_pane_set_event(struct window_pane *wp) NULL, window_pane_error_callback, wp); wp->ictx = input_init(wp, wp->event); - bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); bufferevent_enable(wp->event, EV_READ|EV_WRITE); } @@ -1559,27 +1559,11 @@ window_pane_get_new_data(struct window_pane *wp, void window_pane_update_used_data(struct window_pane *wp, - struct window_pane_offset *wpo, size_t size, int acknowledge) + struct window_pane_offset *wpo, size_t size) { size_t used = wpo->used - wp->base_offset; if (size > EVBUFFER_LENGTH(wp->event->input) - used) size = EVBUFFER_LENGTH(wp->event->input) - used; wpo->used += size; - - if (acknowledge) - window_pane_acknowledge_data(wp, wpo, size); -} - -void -window_pane_acknowledge_data(struct window_pane *wp, - struct window_pane_offset *wpo, size_t size) -{ - size_t acknowledged = wpo->acknowledged - wp->base_offset; - - if (size > EVBUFFER_LENGTH(wp->event->input) - acknowledged) - size = EVBUFFER_LENGTH(wp->event->input) - acknowledged; - wpo->acknowledged += size; - if (wpo->acknowledged > wpo->used) - wpo->acknowledged = wpo->used; } From 674ec410b7ecf14fa24e9c3987469a4d9da599c1 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Jun 2020 16:09:35 +0000 Subject: [PATCH 0475/1006] Try without cursor/keypad flags if a key doesn't exist, and limit ctrl key translation to ASCII keys. Fixes send-keys, GitHub issue 2247. --- input-keys.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/input-keys.c b/input-keys.c index d1fbbfcf..938c530f 100644 --- a/input-keys.c +++ b/input-keys.c @@ -487,9 +487,13 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) ike = input_key_get(key); if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META)) ike = input_key_get(key & ~KEYC_META); + if (ike == NULL && (key & KEYC_CURSOR)) + ike = input_key_get(key & ~KEYC_CURSOR); + if (ike == NULL && (key & KEYC_KEYPAD)) + ike = input_key_get(key & ~KEYC_KEYPAD); if (ike != NULL) { log_debug("found key 0x%llx: \"%s\"", key, ike->data); - if (key & KEYC_META && (~key & KEYC_IMPLIED_META)) + if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META)) bufferevent_write(bev, "\033", 1); bufferevent_write(bev, ike->data, strlen(ike->data)); return (0); @@ -497,13 +501,13 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) /* No builtin key sequence; construct an extended key sequence. */ if (~s->mode & MODE_KEXTENDED) { - justkey = (key & KEYC_MASK_KEY); if ((key & KEYC_MASK_MODIFIERS) != KEYC_CTRL) goto missing; + justkey = (key & KEYC_MASK_KEY); switch (justkey) { case ' ': case '2': - key = 0||(key & ~KEYC_MASK_KEY); + key = 0|(key & ~KEYC_MASK_KEY); break; case '|': key = 28|(key & ~KEYC_MASK_KEY); @@ -523,6 +527,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) key = (justkey - 'A')|(key & ~KEYC_MASK_KEY); else if (justkey >= 'a' && justkey <= '~') key = (justkey - 96)|(key & ~KEYC_MASK_KEY); + else + return (0); break; } return (input_key(s, bev, key & ~KEYC_CTRL)); From 9819470058800953a1c487cc12f923d0a603c3dc Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Jun 2020 19:39:25 +0000 Subject: [PATCH 0476/1006] Change format callback to return value rather than storing it in the entry. --- format.c | 321 +++++++++++++++++++++++++++++-------------------------- tmux.h | 2 + 2 files changed, 170 insertions(+), 153 deletions(-) diff --git a/format.c b/format.c index 6ff04e72..28c423ef 100644 --- a/format.c +++ b/format.c @@ -38,13 +38,9 @@ * string. */ -struct format_entry; -typedef void (*format_cb)(struct format_tree *, struct format_entry *); - static char *format_job_get(struct format_tree *, const char *); static void format_job_timer(int, short, void *); -static void format_add_cb(struct format_tree *, const char *, format_cb); static int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); static void format_defaults_session(struct format_tree *, @@ -421,50 +417,51 @@ format_job_timer(__unused int fd, __unused short events, __unused void *arg) } /* Callback for host. */ -static void -format_cb_host(__unused struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_host(__unused struct format_tree *ft) { char host[HOST_NAME_MAX + 1]; if (gethostname(host, sizeof host) != 0) - fe->value = xstrdup(""); - else - fe->value = xstrdup(host); + return (xstrdup("")); + return (xstrdup(host)); } /* Callback for host_short. */ -static void -format_cb_host_short(__unused struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_host_short(__unused struct format_tree *ft) { char host[HOST_NAME_MAX + 1], *cp; if (gethostname(host, sizeof host) != 0) - fe->value = xstrdup(""); - else { - if ((cp = strchr(host, '.')) != NULL) - *cp = '\0'; - fe->value = xstrdup(host); - } + return (xstrdup("")); + if ((cp = strchr(host, '.')) != NULL) + *cp = '\0'; + return (xstrdup(host)); } /* Callback for pid. */ -static void -format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_pid(__unused struct format_tree *ft) { - xasprintf(&fe->value, "%ld", (long)getpid()); + char *value; + + xasprintf(&value, "%ld", (long)getpid()); + return (value); } /* Callback for session_attached_list. */ -static void -format_cb_session_attached_list(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_session_attached_list(struct format_tree *ft) { struct session *s = ft->s; struct client *loop; struct evbuffer *buffer; int size; + char *value = NULL; if (s == NULL) - return; + return (NULL); buffer = evbuffer_new(); if (buffer == NULL) @@ -479,20 +476,21 @@ format_cb_session_attached_list(struct format_tree *ft, struct format_entry *fe) } if ((size = EVBUFFER_LENGTH(buffer)) != 0) - xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); + return (value); } /* Callback for session_alerts. */ -static void -format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_session_alerts(struct format_tree *ft) { struct session *s = ft->s; struct winlink *wl; char alerts[1024], tmp[16]; if (s == NULL) - return; + return (NULL); *alerts = '\0'; RB_FOREACH(wl, winlinks, &s->windows) { @@ -510,19 +508,19 @@ format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) if (wl->flags & WINLINK_SILENCE) strlcat(alerts, "~", sizeof alerts); } - fe->value = xstrdup(alerts); + return (xstrdup(alerts)); } /* Callback for session_stack. */ -static void -format_cb_session_stack(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_session_stack(struct format_tree *ft) { struct session *s = ft->s; struct winlink *wl; char result[1024], tmp[16]; if (s == NULL) - return; + return (NULL); xsnprintf(result, sizeof result, "%u", s->curw->idx); TAILQ_FOREACH(wl, &s->lastw, sentry) { @@ -532,16 +530,17 @@ format_cb_session_stack(struct format_tree *ft, struct format_entry *fe) strlcat(result, ",", sizeof result); strlcat(result, tmp, sizeof result); } - fe->value = xstrdup(result); + return (xstrdup(result)); } /* Callback for window_stack_index. */ -static void -format_cb_window_stack_index(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_window_stack_index(struct format_tree *ft) { struct session *s = ft->wl->session; struct winlink *wl; u_int idx; + char *value = NULL; idx = 0; TAILQ_FOREACH(wl, &s->lastw, sentry) { @@ -549,21 +548,21 @@ format_cb_window_stack_index(struct format_tree *ft, struct format_entry *fe) if (wl == ft->wl) break; } - if (wl != NULL) - xasprintf(&fe->value, "%u", idx); - else - fe->value = xstrdup("0"); + if (wl == NULL) + return (xstrdup("0")); + xasprintf(&value, "%u", idx); + return (value); } /* Callback for window_linked_sessions_list. */ -static void -format_cb_window_linked_sessions_list(struct format_tree *ft, - struct format_entry *fe) +static char * +format_cb_window_linked_sessions_list(struct format_tree *ft) { struct window *w = ft->wl->window; struct winlink *wl; struct evbuffer *buffer; int size; + char *value = NULL; buffer = evbuffer_new(); if (buffer == NULL) @@ -576,36 +575,38 @@ format_cb_window_linked_sessions_list(struct format_tree *ft, } if ((size = EVBUFFER_LENGTH(buffer)) != 0) - xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); + return (value); } /* Callback for window_active_sessions. */ -static void -format_cb_window_active_sessions(struct format_tree *ft, - struct format_entry *fe) +static char * +format_cb_window_active_sessions(struct format_tree *ft) { struct window *w = ft->wl->window; struct winlink *wl; u_int n = 0; + char *value; TAILQ_FOREACH(wl, &w->winlinks, wentry) { if (wl->session->curw == wl) n++; } - xasprintf(&fe->value, "%u", n); + xasprintf(&value, "%u", n); + return (value); } /* Callback for window_active_sessions_list. */ -static void -format_cb_window_active_sessions_list(struct format_tree *ft, - struct format_entry *fe) +static char * +format_cb_window_active_sessions_list(struct format_tree *ft) { struct window *w = ft->wl->window; struct winlink *wl; struct evbuffer *buffer; int size; + char *value = NULL; buffer = evbuffer_new(); if (buffer == NULL) @@ -620,18 +621,20 @@ format_cb_window_active_sessions_list(struct format_tree *ft, } if ((size = EVBUFFER_LENGTH(buffer)) != 0) - xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); + return (value); } /* Callback for window_active_clients. */ -static void -format_cb_window_active_clients(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_window_active_clients(struct format_tree *ft) { struct window *w = ft->wl->window; struct client *loop; struct session *client_session; u_int n = 0; + char *value; TAILQ_FOREACH(loop, &clients, entry) { client_session = loop->session; @@ -642,19 +645,20 @@ format_cb_window_active_clients(struct format_tree *ft, struct format_entry *fe) n++; } - xasprintf(&fe->value, "%u", n); + xasprintf(&value, "%u", n); + return (value); } /* Callback for window_active_clients_list. */ -static void -format_cb_window_active_clients_list(struct format_tree *ft, - struct format_entry *fe) +static char * +format_cb_window_active_clients_list(struct format_tree *ft) { struct window *w = ft->wl->window; struct client *loop; struct session *client_session; struct evbuffer *buffer; int size; + char *value = NULL; buffer = evbuffer_new(); if (buffer == NULL) @@ -673,58 +677,58 @@ format_cb_window_active_clients_list(struct format_tree *ft, } if ((size = EVBUFFER_LENGTH(buffer)) != 0) - xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); + return (value); } /* Callback for window_layout. */ -static void -format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_window_layout(struct format_tree *ft) { struct window *w = ft->w; if (w == NULL) - return; + return (NULL); if (w->saved_layout_root != NULL) - fe->value = layout_dump(w->saved_layout_root); - else - fe->value = layout_dump(w->layout_root); + return (layout_dump(w->saved_layout_root)); + return (layout_dump(w->layout_root)); } /* Callback for window_visible_layout. */ -static void -format_cb_window_visible_layout(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_window_visible_layout(struct format_tree *ft) { struct window *w = ft->w; if (w == NULL) - return; + return (NULL); - fe->value = layout_dump(w->layout_root); + return (layout_dump(w->layout_root)); } /* Callback for pane_start_command. */ -static void -format_cb_start_command(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_start_command(struct format_tree *ft) { struct window_pane *wp = ft->wp; if (wp == NULL) - return; + return (NULL); - fe->value = cmd_stringify_argv(wp->argc, wp->argv); + return (cmd_stringify_argv(wp->argc, wp->argv)); } /* Callback for pane_current_command. */ -static void -format_cb_current_command(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_current_command(struct format_tree *ft) { struct window_pane *wp = ft->wp; - char *cmd; + char *cmd, *value; if (wp == NULL || wp->shell == NULL) - return; + return (NULL); cmd = get_proc_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') { @@ -735,37 +739,40 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe) cmd = xstrdup(wp->shell); } } - fe->value = parse_window_name(cmd); + value = parse_window_name(cmd); free(cmd); + return (value); } /* Callback for pane_current_path. */ -static void -format_cb_current_path(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_current_path(struct format_tree *ft) { struct window_pane *wp = ft->wp; char *cwd; if (wp == NULL) - return; + return (NULL); cwd = get_proc_cwd(wp->fd); - if (cwd != NULL) - fe->value = xstrdup(cwd); + if (cwd == NULL) + return (NULL); + return (xstrdup(cwd)); } /* Callback for history_bytes. */ -static void -format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_history_bytes(struct format_tree *ft) { struct window_pane *wp = ft->wp; struct grid *gd; struct grid_line *gl; size_t size = 0; u_int i; + char *value; if (wp == NULL) - return; + return (NULL); gd = wp->base.grid; for (i = 0; i < gd->hsize + gd->sy; i++) { @@ -775,20 +782,22 @@ format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) } size += (gd->hsize + gd->sy) * sizeof *gl; - xasprintf(&fe->value, "%zu", size); + xasprintf(&value, "%zu", size); + return (value); } /* Callback for history_all_bytes. */ -static void -format_cb_history_all_bytes(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_history_all_bytes(struct format_tree *ft) { struct window_pane *wp = ft->wp; struct grid *gd; struct grid_line *gl; u_int i, lines, cells = 0, extended_cells = 0; + char *value; if (wp == NULL) - return; + return (NULL); gd = wp->base.grid; lines = gd->hsize + gd->sy; @@ -798,22 +807,24 @@ format_cb_history_all_bytes(struct format_tree *ft, struct format_entry *fe) extended_cells += gl->extdsize; } - xasprintf(&fe->value, "%u,%zu,%u,%zu,%u,%zu", lines, + xasprintf(&value, "%u,%zu,%u,%zu,%u,%zu", lines, lines * sizeof *gl, cells, cells * sizeof *gl->celldata, extended_cells, extended_cells * sizeof *gl->extddata); + return (value); } /* Callback for pane_tabs. */ -static void -format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_pane_tabs(struct format_tree *ft) { struct window_pane *wp = ft->wp; struct evbuffer *buffer; u_int i; int size; + char *value = NULL; if (wp == NULL) - return; + return (NULL); buffer = evbuffer_new(); if (buffer == NULL) @@ -827,25 +838,27 @@ format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) evbuffer_add_printf(buffer, "%u", i); } if ((size = EVBUFFER_LENGTH(buffer)) != 0) - xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); + return (value); } /* Callback for session_group_list. */ -static void -format_cb_session_group_list(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_session_group_list(struct format_tree *ft) { struct session *s = ft->s; struct session_group *sg; struct session *loop; struct evbuffer *buffer; int size; + char *value = NULL; if (s == NULL) - return; + return (NULL); sg = session_group_contains(s); if (sg == NULL) - return; + return (NULL); buffer = evbuffer_new(); if (buffer == NULL) @@ -858,26 +871,27 @@ format_cb_session_group_list(struct format_tree *ft, struct format_entry *fe) } if ((size = EVBUFFER_LENGTH(buffer)) != 0) - xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); + return (value); } /* Callback for session_group_attached_list. */ -static void -format_cb_session_group_attached_list(struct format_tree *ft, - struct format_entry *fe) +static char * +format_cb_session_group_attached_list(struct format_tree *ft) { struct session *s = ft->s, *client_session, *session_loop; struct session_group *sg; struct client *loop; struct evbuffer *buffer; int size; + char *value = NULL; if (s == NULL) - return; + return (NULL); sg = session_group_contains(s); if (sg == NULL) - return; + return (NULL); buffer = evbuffer_new(); if (buffer == NULL) @@ -897,36 +911,40 @@ format_cb_session_group_attached_list(struct format_tree *ft, } if ((size = EVBUFFER_LENGTH(buffer)) != 0) - xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); + return (value); } /* Callback for pane_in_mode. */ -static void -format_cb_pane_in_mode(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_pane_in_mode(struct format_tree *ft) { struct window_pane *wp = ft->wp; u_int n = 0; struct window_mode_entry *wme; + char *value; if (wp == NULL) - return; + return (NULL); TAILQ_FOREACH(wme, &wp->modes, entry) n++; - xasprintf(&fe->value, "%u", n); + xasprintf(&value, "%u", n); + return (value); } /* Callback for pane_at_top. */ -static void -format_cb_pane_at_top(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_pane_at_top(struct format_tree *ft) { struct window_pane *wp = ft->wp; struct window *w; int status, flag; + char *value; if (wp == NULL) - return; + return (NULL); w = wp->window; status = options_get_number(w->options, "pane-border-status"); @@ -934,19 +952,21 @@ format_cb_pane_at_top(struct format_tree *ft, struct format_entry *fe) flag = (wp->yoff == 1); else flag = (wp->yoff == 0); - xasprintf(&fe->value, "%d", flag); + xasprintf(&value, "%d", flag); + return (value); } /* Callback for pane_at_bottom. */ -static void -format_cb_pane_at_bottom(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_pane_at_bottom(struct format_tree *ft) { struct window_pane *wp = ft->wp; struct window *w; int status, flag; + char *value; if (wp == NULL) - return; + return (NULL); w = wp->window; status = options_get_number(w->options, "pane-border-status"); @@ -954,22 +974,25 @@ format_cb_pane_at_bottom(struct format_tree *ft, struct format_entry *fe) flag = (wp->yoff + wp->sy == w->sy - 1); else flag = (wp->yoff + wp->sy == w->sy); - xasprintf(&fe->value, "%d", flag); + xasprintf(&value, "%d", flag); + return (value); } /* Callback for cursor_character. */ -static void -format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_cursor_character(struct format_tree *ft) { struct window_pane *wp = ft->wp; struct grid_cell gc; + char *value = NULL; if (wp == NULL) - return; + return (NULL); grid_view_get_cell(wp->base.grid, wp->base.cx, wp->base.cy, &gc); if (~gc.flags & GRID_FLAG_PADDING) - xasprintf(&fe->value, "%.*s", (int)gc.data.size, gc.data.data); + xasprintf(&value, "%.*s", (int)gc.data.size, gc.data.data); + return (value); } /* Return word at given coordinates. Caller frees. */ @@ -1043,8 +1066,8 @@ format_grid_word(struct grid *gd, u_int x, u_int y) } /* Callback for mouse_word. */ -static void -format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_mouse_word(struct format_tree *ft) { struct window_pane *wp; struct grid *gd; @@ -1052,25 +1075,21 @@ format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) char *s; if (!ft->m.valid) - return; + return (NULL); wp = cmd_mouse_pane(&ft->m, NULL, NULL); if (wp == NULL) - return; + return (NULL); if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) - return; + return (NULL); if (!TAILQ_EMPTY(&wp->modes)) { if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) - s = window_copy_get_word(wp, x, y); - else - s = NULL; - } else { - gd = wp->base.grid; - s = format_grid_word(gd, x, gd->hsize + y); + return (s = window_copy_get_word(wp, x, y)); + return (NULL); } - if (s != NULL) - fe->value = s; + gd = wp->base.grid; + return (format_grid_word(gd, x, gd->hsize + y)); } /* Return line at given coordinates. Caller frees. */ @@ -1100,34 +1119,29 @@ format_grid_line(struct grid *gd, u_int y) } /* Callback for mouse_line. */ -static void -format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) +static char * +format_cb_mouse_line(struct format_tree *ft) { struct window_pane *wp; struct grid *gd; u_int x, y; - char *s; if (!ft->m.valid) - return; + return (NULL); wp = cmd_mouse_pane(&ft->m, NULL, NULL); if (wp == NULL) - return; + return (NULL); if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) - return; + return (NULL); if (!TAILQ_EMPTY(&wp->modes)) { if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) - s = window_copy_get_line(wp, y); - else - s = NULL; - } else { - gd = wp->base.grid; - s = format_grid_line(gd, gd->hsize + y); + return (window_copy_get_line(wp, y)); + return (NULL); } - if (s != NULL) - fe->value = s; + gd = wp->base.grid; + return (format_grid_line(gd, gd->hsize + y)); } /* Merge one format tree into another. */ @@ -1244,7 +1258,7 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *, cb(fe->key, s, arg); } else { if (fe->value == NULL && fe->cb != NULL) { - fe->cb(ft, fe); + fe->value = fe->cb(ft); if (fe->value == NULL) fe->value = xstrdup(""); } @@ -1304,7 +1318,7 @@ format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) } /* Add a key and function. */ -static void +void format_add_cb(struct format_tree *ft, const char *key, format_cb cb) { struct format_entry *fe; @@ -1427,10 +1441,11 @@ format_find(struct format_tree *ft, const char *key, int modifiers, t = fe->t; goto found; } - if (fe->value == NULL && fe->cb != NULL) - fe->cb(ft, fe); - if (fe->value == NULL) - fe->value = xstrdup(""); + if (fe->value == NULL && fe->cb != NULL) { + fe->value = fe->cb(ft); + if (fe->value == NULL) + fe->value = xstrdup(""); + } found = xstrdup(fe->value); goto found; } diff --git a/tmux.h b/tmux.h index a14d4ac2..483fc483 100644 --- a/tmux.h +++ b/tmux.h @@ -1912,6 +1912,7 @@ char *paste_make_sample(struct paste_buffer *); #define FORMAT_WINDOW 0x40000000U struct format_tree; struct format_modifier; +typedef char *(*format_cb)(struct format_tree *); const char *format_skip(const char *, const char *); int format_true(const char *); struct format_tree *format_create(struct client *, struct cmdq_item *, int, @@ -1922,6 +1923,7 @@ void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); void format_add_tv(struct format_tree *, const char *, struct timeval *); +void format_add_cb(struct format_tree *, const char *, format_cb); void format_each(struct format_tree *, void (*)(const char *, const char *, void *), void *); char *format_expand_time(struct format_tree *, const char *); From 8339702d4779794de0fcb85dd620421ae5cf768b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Jun 2020 20:58:42 +0000 Subject: [PATCH 0477/1006] Check the right thing for maximum client buffer size. --- server-client.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/server-client.c b/server-client.c index 5218340e..81af2f07 100644 --- a/server-client.c +++ b/server-client.c @@ -1510,6 +1510,7 @@ server_client_check_pane_buffer(struct window_pane *wp) struct window_pane_offset *wpo; int off = 1, flag; u_int attached_clients = 0; + size_t new_size; /* * Work out the minimum used size. This is the most that can be removed @@ -1535,12 +1536,15 @@ server_client_check_pane_buffer(struct window_pane *wp) if (!flag) off = 0; - log_debug("%s: %s has %zu bytes used for %%%u", __func__, - c->name, wpo->used - wp->base_offset, wp->id); - if (wpo->used - wp->base_offset > SERVER_CLIENT_PANE_LIMIT) { + window_pane_get_new_data(wp, wpo, &new_size); + log_debug("%s: %s has %zu bytes used and %zu left for %%%u", + __func__, c->name, wpo->used - wp->base_offset, new_size, + wp->id); + if (new_size > SERVER_CLIENT_PANE_LIMIT) { control_flush(c); c->flags |= CLIENT_EXIT; - } else if (wpo->used < minimum) + } + if (wpo->used < minimum) minimum = wpo->used; } if (attached_clients == 0) From 563b7331da2d31aca470389817c282a46da7c872 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Jun 2020 21:08:05 +0000 Subject: [PATCH 0478/1006] Remove blocks from queue when pane disappears. --- control.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/control.c b/control.c index c1e0b7eb..94254d7c 100644 --- a/control.c +++ b/control.c @@ -48,7 +48,7 @@ struct control_block { TAILQ_ENTRY(control_block) entry; TAILQ_ENTRY(control_block) all_entry; - }; +}; /* Control client pane. */ struct control_pane { @@ -455,8 +455,10 @@ control_write_pending(struct client *c, struct control_pane *cp, size_t limit) if (s == NULL || (wp = window_pane_find_by_id(cp->pane)) == NULL || winlink_find_by_window(&s->windows, wp->window) == NULL) { - TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) + TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { + TAILQ_REMOVE(&cp->blocks, cb, entry); control_free_block(cs, cb); + } control_flush_all_blocks(c); return (0); } From f3931497f8aee291bce132fb106cedb55c5b3fa9 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Jun 2020 08:17:27 +0000 Subject: [PATCH 0479/1006] Use CLOCK_MONOTONIC for timer measurement and add a timestamp to control mode %output blocks. --- control.c | 24 ++++++++++++++++++++---- server-client.c | 4 +++- tmux.c | 14 ++++++++++++++ tmux.h | 1 + tty-keys.c | 2 -- window-copy.c | 15 +++------------ 6 files changed, 41 insertions(+), 19 deletions(-) diff --git a/control.c b/control.c index 94254d7c..1801bd75 100644 --- a/control.c +++ b/control.c @@ -45,6 +45,7 @@ struct control_block { size_t size; char *line; + uint64_t t; TAILQ_ENTRY(control_block) entry; TAILQ_ENTRY(control_block) all_entry; @@ -152,6 +153,21 @@ control_add_pane(struct client *c, struct window_pane *wp) return (cp); } +/* Get actual pane for this client. */ +static struct window_pane * +control_window_pane(struct client *c, u_int pane) +{ + struct window_pane *wp; + + if (c->session == NULL) + return (NULL); + if ((wp = window_pane_find_by_id(pane)) == NULL) + return (NULL); + if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) + return (NULL); + return (wp); +} + /* Reset control offsets. */ void control_reset_offsets(struct client *c) @@ -253,6 +269,7 @@ control_write(struct client *c, const char *fmt, ...) cb = xcalloc(1, sizeof *cb); xvasprintf(&cb->line, fmt, ap); TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); + cb->t = get_timer(); log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line); bufferevent_enable(cs->write_event, EV_WRITE); @@ -290,6 +307,7 @@ control_write_output(struct client *c, struct window_pane *wp) cb = xcalloc(1, sizeof *cb); cb->size = new_size; TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); + cb->t = get_timer(); TAILQ_INSERT_TAIL(&cp->blocks, cb, entry); log_debug("%s: %s: new output block of %zu for %%%u", __func__, c->name, @@ -446,15 +464,13 @@ static int control_write_pending(struct client *c, struct control_pane *cp, size_t limit) { struct control_state *cs = c->control_state; - struct session *s = c->session; struct window_pane *wp = NULL; struct evbuffer *message = NULL; size_t used = 0, size; struct control_block *cb, *cb1; - if (s == NULL || - (wp = window_pane_find_by_id(cp->pane)) == NULL || - winlink_find_by_window(&s->windows, wp->window) == NULL) { + wp = control_window_pane(c, cp->pane); + if (wp == NULL) { TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { TAILQ_REMOVE(&cp->blocks, cb, entry); control_free_block(cs, cb); diff --git a/server-client.c b/server-client.c index 81af2f07..e3383aab 100644 --- a/server-client.c +++ b/server-client.c @@ -1779,7 +1779,9 @@ server_client_check_exit(struct client *c) struct client_file *cf; const char *name = c->exit_session; - if ((c->flags & CLIENT_EXITED) || (~c->flags & CLIENT_EXIT)) + if (c->flags & (CLIENT_DEAD|CLIENT_EXITED)) + return; + if (~c->flags & CLIENT_EXIT) return; if (c->flags & CLIENT_CONTROL) { diff --git a/tmux.c b/tmux.c index cfc84cd0..a9aec43d 100644 --- a/tmux.c +++ b/tmux.c @@ -240,6 +240,20 @@ setblocking(int fd, int state) } } +uint64_t +get_timer(void) +{ + struct timespec ts; + + /* + * We want a timestamp in milliseconds suitable for time measurement, + * so prefer the monotonic clock. + */ + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + clock_gettime(CLOCK_REALTIME, &ts); + return ((ts.tv_sec * 1000ULL) + (ts.tv_nsec / 1000000ULL)); +} + const char * sig2name(int signo) { diff --git a/tmux.h b/tmux.h index 483fc483..950496f0 100644 --- a/tmux.h +++ b/tmux.h @@ -1852,6 +1852,7 @@ extern int ptm_fd; extern const char *shell_command; int checkshell(const char *); void setblocking(int, int); +uint64_t get_timer(void); const char *sig2name(int); const char *find_cwd(void); const char *find_home(void); diff --git a/tty-keys.c b/tty-keys.c index 2e375807..4904ba35 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -637,8 +637,6 @@ tty_keys_next(struct tty *tty) struct mouse_event m = { 0 }; struct key_event *event; - gettimeofday(&tv, NULL); - /* Get key buffer. */ buf = EVBUFFER_DATA(tty->in); len = EVBUFFER_LENGTH(tty->in); diff --git a/window-copy.c b/window-copy.c index cfa91df9..af80186b 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2934,15 +2934,6 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) return (found); } -static uint64_t -window_copy_get_time(void) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - return ((tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000ULL)); -} - static int window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, int regex) @@ -2985,11 +2976,11 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, return (0); } } - tstart = window_copy_get_time(); + tstart = get_timer(); start = 0; end = gd->hsize + gd->sy; - stop = window_copy_get_time() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; + stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; again: free(data->searchmark); @@ -3027,7 +3018,7 @@ again: px++; } - t = window_copy_get_time(); + t = get_timer(); if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { data->timeout = 1; break; From 822ee4e0a64cd27c4668aed53f1284b257612dcb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Jun 2020 11:29:00 +0000 Subject: [PATCH 0480/1006] Fail rather than fatal on UTF-8 width 0. --- utf8.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utf8.c b/utf8.c index 2a92003f..893cb5ee 100644 --- a/utf8.c +++ b/utf8.c @@ -135,6 +135,8 @@ utf8_from_data(const struct utf8_data *ud, utf8_char *uc) union utf8_map m = { .uc = 0 }; u_int offset; + if (ud->width == 0) + goto fail; if (ud->width != 1 && ud->width != 2) fatalx("invalid UTF-8 width"); if (ud->size == 0) From 7e501f1993cc1e4b8344643bcee449cc8c958d7e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Jun 2020 17:17:44 +0000 Subject: [PATCH 0481/1006] UTF-8 keys need to be big endian so the size bits are at the top. --- utf8.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utf8.c b/utf8.c index 893cb5ee..5c11b7ca 100644 --- a/utf8.c +++ b/utf8.c @@ -162,14 +162,14 @@ utf8_from_data(const struct utf8_data *ud, utf8_char *uc) m.data[1] = (offset >> 8) & 0xff; m.data[2] = (offset >> 16); } - *uc = m.uc; + *uc = htonl(m.uc); return (UTF8_DONE); fail: if (ud->width == 1) - *uc = utf8_space1.uc; + *uc = htonl(utf8_space1.uc); else - *uc = utf8_space2.uc; + *uc = htonl(utf8_space2.uc); return (UTF8_ERROR); } @@ -177,7 +177,7 @@ fail: void utf8_to_data(utf8_char uc, struct utf8_data *ud) { - union utf8_map m = { .uc = uc }; + union utf8_map m = { .uc = ntohl(uc) }; struct utf8_item *ui; u_int offset; @@ -210,7 +210,7 @@ utf8_build_one(char c, u_int width) if (width == 2) m.flags |= UTF8_FLAG_WIDTH2; - return (m.uc); + return (htonl(m.uc)); } /* Set a single character. */ From 5fbae8c8c6fb0750ef940071224a54db62b9f1db Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Jun 2020 19:10:26 +0000 Subject: [PATCH 0482/1006] Fire copy-pipe command even if there is no text, means it works if it has side effects. --- window-copy.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/window-copy.c b/window-copy.c index af80186b..984a4e3d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3628,6 +3628,8 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len) buf = window_copy_match_at_cursor(data); if (buf != NULL) *len = strlen(buf); + else + *len = 0; return (buf); } @@ -3719,6 +3721,7 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len) /* Don't bother if no data. */ if (off == 0) { free(buf); + *len = 0 return (NULL); } if (keys == MODEKEY_EMACS || lastex <= ey_last) @@ -3753,9 +3756,6 @@ window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, struct job *job; buf = window_copy_get_selection(wme, &len); - if (buf == NULL) - return; - if (cmd == NULL || *cmd == '\0') cmd = options_get_string(global_options, "copy-command"); if (cmd != NULL && *cmd != '\0') { @@ -3763,7 +3763,8 @@ window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, -1, -1); bufferevent_write(job_get_event(job), buf, len); } - window_copy_copy_buffer(wme, prefix, buf, len); + if (buf != NULL) + window_copy_copy_buffer(wme, prefix, buf, len); } static void From f5366ff828cde78c140efa2dd9453956b59f5241 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Jun 2020 19:16:46 +0000 Subject: [PATCH 0483/1006] Missing ; in previous. --- window-copy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index 984a4e3d..20f351aa 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3721,7 +3721,7 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len) /* Don't bother if no data. */ if (off == 0) { free(buf); - *len = 0 + *len = 0; return (NULL); } if (keys == MODEKEY_EMACS || lastex <= ey_last) From 2a4d4bda2b94602e9f999ff0b59efa92613f75a9 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Jun 2020 20:10:23 +0000 Subject: [PATCH 0484/1006] Allow UTF-8 characters of width 0 to be stored, it is useful to be able to put padding cells in as width 0. --- grid.c | 4 ++-- screen-write.c | 2 +- tmux.h | 2 +- utf8.c | 54 ++++++++++++++++++++++---------------------------- 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/grid.c b/grid.c index 6ca14cba..7943d3be 100644 --- a/grid.c +++ b/grid.c @@ -76,7 +76,7 @@ grid_need_extended_cell(const struct grid_cell_entry *gce, return (1); if (gc->attr > 0xff) return (1); - if (gc->data.size > 1 || gc->data.width > 1) + if (gc->data.size != 1 || gc->data.width != 1) return (1); if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB)) return (1); @@ -570,7 +570,7 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, gce = &gl->celldata[px + i]; if (grid_need_extended_cell(gce, gc)) { gee = grid_extended_cell(gl, gce, gc); - gee->data = utf8_build_one(s[i], 1); + gee->data = utf8_build_one(s[i]); } else grid_store_cell(gce, gc, s[i]); } diff --git a/screen-write.c b/screen-write.c index 33e3f975..68c3aa76 100644 --- a/screen-write.c +++ b/screen-write.c @@ -39,7 +39,7 @@ static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, const struct utf8_data *, u_int *); static const struct grid_cell screen_write_pad_cell = { - { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_PADDING, 8, 8, 0 + { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0 }; struct screen_write_collect_item { diff --git a/tmux.h b/tmux.h index 950496f0..b02525fa 100644 --- a/tmux.h +++ b/tmux.h @@ -2902,7 +2902,7 @@ u_int session_group_attached_count(struct session_group *); void session_renumber_windows(struct session *); /* utf8.c */ -utf8_char utf8_build_one(char, u_int); +utf8_char utf8_build_one(u_char); enum utf8_state utf8_from_data(const struct utf8_data *, utf8_char *); void utf8_to_data(utf8_char, struct utf8_data *); void utf8_set(struct utf8_data *, u_char); diff --git a/utf8.c b/utf8.c index 5c11b7ca..c33b6690 100644 --- a/utf8.c +++ b/utf8.c @@ -56,19 +56,26 @@ union utf8_map { utf8_char uc; struct { u_char flags; -#define UTF8_FLAG_SIZE 0x1f -#define UTF8_FLAG_WIDTH2 0x20 - u_char data[3]; }; } __packed; +#define UTF8_GET_SIZE(flags) ((flags) & 0x1f) +#define UTF8_GET_WIDTH(flags) (((flags) >> 5) - 1) + +#define UTF8_SET_SIZE(size) (size) +#define UTF8_SET_WIDTH(width) ((width + 1) << 5) + +static const union utf8_map utf8_space0 = { + .flags = UTF8_SET_WIDTH(0)|UTF8_SET_SIZE(0), + .data = "" +}; static const union utf8_map utf8_space1 = { - .flags = 1, + .flags = UTF8_SET_WIDTH(1)|UTF8_SET_SIZE(1), .data = " " }; static const union utf8_map utf8_space2 = { - .flags = UTF8_FLAG_WIDTH2|2, + .flags = UTF8_SET_WIDTH(2)|UTF8_SET_SIZE(2), .data = " " }; @@ -135,24 +142,12 @@ utf8_from_data(const struct utf8_data *ud, utf8_char *uc) union utf8_map m = { .uc = 0 }; u_int offset; - if (ud->width == 0) - goto fail; - if (ud->width != 1 && ud->width != 2) + if (ud->width > 2) fatalx("invalid UTF-8 width"); - if (ud->size == 0) - fatalx("invalid UTF-8 size"); - if (ud->size > UTF8_FLAG_SIZE) + if (ud->size > UTF8_SIZE) goto fail; - if (ud->size == 1) { - *uc = utf8_build_one(ud->data[0], 1); - return (UTF8_DONE); - } - - m.flags = ud->size; - if (ud->width == 2) - m.flags |= UTF8_FLAG_WIDTH2; - + m.flags = UTF8_SET_SIZE(ud->size)|UTF8_SET_WIDTH(ud->width); if (ud->size <= 3) memcpy(m.data, ud->data, ud->size); else { @@ -166,7 +161,9 @@ utf8_from_data(const struct utf8_data *ud, utf8_char *uc) return (UTF8_DONE); fail: - if (ud->width == 1) + if (ud->width == 0) + *uc = htonl(utf8_space0.uc); + else if (ud->width == 1) *uc = htonl(utf8_space1.uc); else *uc = htonl(utf8_space2.uc); @@ -182,11 +179,8 @@ utf8_to_data(utf8_char uc, struct utf8_data *ud) u_int offset; memset(ud, 0, sizeof *ud); - ud->size = ud->have = (m.flags & UTF8_FLAG_SIZE); - if (m.flags & UTF8_FLAG_WIDTH2) - ud->width = 2; - else - ud->width = 1; + ud->size = ud->have = UTF8_GET_SIZE(m.flags); + ud->width = UTF8_GET_WIDTH(m.flags); if (ud->size <= 3) { memcpy(ud->data, m.data, ud->size); @@ -204,12 +198,12 @@ utf8_to_data(utf8_char uc, struct utf8_data *ud) /* Get UTF-8 character from a single ASCII character. */ u_int -utf8_build_one(char c, u_int width) +utf8_build_one(u_char ch) { - union utf8_map m = { .flags = 1, .data[0] = c }; + union utf8_map m; - if (width == 2) - m.flags |= UTF8_FLAG_WIDTH2; + m.flags = UTF8_SET_SIZE(1)|UTF8_SET_WIDTH(1); + m.data[0] = ch; return (htonl(m.uc)); } From 4694e9a2b62c8df0862d80237e42978d65fc824e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Jun 2020 20:51:46 +0000 Subject: [PATCH 0485/1006] Move the code to set up a padding cell into grid.c. --- grid-view.c | 7 +++++++ grid.c | 21 ++++++++++++++++++--- screen-write.c | 6 +----- tmux.h | 2 ++ tty.c | 9 +++++---- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/grid-view.c b/grid-view.c index a4bd5ba2..f230d3c8 100644 --- a/grid-view.c +++ b/grid-view.c @@ -45,6 +45,13 @@ grid_view_set_cell(struct grid *gd, u_int px, u_int py, grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } +/* Set padding. */ +void +grid_view_set_padding(struct grid *gd, u_int px, u_int py) +{ + grid_set_padding(gd, grid_view_x(gd, px), grid_view_y(gd, py)); +} + /* Set cells. */ void grid_view_set_cells(struct grid *gd, u_int px, u_int py, diff --git a/grid.c b/grid.c index 7943d3be..2e3f50b9 100644 --- a/grid.c +++ b/grid.c @@ -40,8 +40,16 @@ const struct grid_cell grid_default_cell = { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 }; +/* + * Padding grid cell data. Padding cells are the only zero width cell that + * appears in the grid - because of this, they are always extended cells. + */ +static const struct grid_cell grid_padding_cell = { + { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0 +}; + /* Cleared grid cell data. */ -const struct grid_cell grid_cleared_cell = { +static const struct grid_cell grid_cleared_cell = { { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0 }; static const struct grid_cell_entry grid_cleared_entry = { @@ -524,7 +532,7 @@ grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) grid_get_cell1(&gd->linedata[py], px, gc); } -/* Set cell at relative position. */ +/* Set cell at position. */ void grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { @@ -547,7 +555,14 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) grid_store_cell(gce, gc, gc->data.data[0]); } -/* Set cells at relative position. */ +/* Set padding at position. */ +void +grid_set_padding(struct grid *gd, u_int px, u_int py) +{ + grid_set_cell(gd, px, py, &grid_padding_cell); +} + +/* Set cells at position. */ void grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, const char *s, size_t slen) diff --git a/screen-write.c b/screen-write.c index 68c3aa76..46ac967e 100644 --- a/screen-write.c +++ b/screen-write.c @@ -38,10 +38,6 @@ static int screen_write_overwrite(struct screen_write_ctx *, static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, const struct utf8_data *, u_int *); -static const struct grid_cell screen_write_pad_cell = { - { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0 -}; - struct screen_write_collect_item { u_int x; int wrapped; @@ -1774,7 +1770,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) */ for (xx = s->cx + 1; xx < s->cx + width; xx++) { log_debug("%s: new padding at %u,%u", __func__, xx, s->cy); - grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); + grid_view_set_padding(gd, xx, s->cy); skip = 0; } diff --git a/tmux.h b/tmux.h index b02525fa..4523fce4 100644 --- a/tmux.h +++ b/tmux.h @@ -2499,6 +2499,7 @@ void grid_clear_history(struct grid *); const struct grid_line *grid_peek_line(struct grid *, u_int); void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); +void grid_set_padding(struct grid *, u_int, u_int); void grid_set_cells(struct grid *, u_int, u_int, const struct grid_cell *, const char *, size_t); struct grid_line *grid_get_line(struct grid *, u_int); @@ -2520,6 +2521,7 @@ u_int grid_line_length(struct grid *, u_int); void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_view_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); +void grid_view_set_padding(struct grid *, u_int, u_int); void grid_view_set_cells(struct grid *, u_int, u_int, const struct grid_cell *, const char *, size_t); void grid_view_clear_history(struct grid *, u_int); diff --git a/tty.c b/tty.c index ba06d0bb..77eb90be 100644 --- a/tty.c +++ b/tty.c @@ -1380,9 +1380,10 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, screen_select_cell(s, &last, gcp); else memcpy(&last, gcp, sizeof last); - if (!tty_check_overlay(tty, atx + ux, aty)) - ux += gcp->data.width; - else if (ux + gcp->data.width > nx) { + if (!tty_check_overlay(tty, atx + ux, aty)) { + if (~gcp->flags & GRID_FLAG_PADDING) + ux += gcp->data.width; + } else if (ux + gcp->data.width > nx) { tty_attributes(tty, &last, defaults, palette); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.width; j++) { @@ -1397,7 +1398,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, for (j = 0; j < gcp->data.size; j++) tty_putc(tty, gcp->data.data[j]); ux += gcp->data.width; - } else { + } else if (~gcp->flags & GRID_FLAG_PADDING) { memcpy(buf + len, gcp->data.data, gcp->data.size); len += gcp->data.size; width += gcp->data.width; From 3f6af4156f451cfbee867babe74cc6675eb3f947 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 3 Jun 2020 16:35:40 +0000 Subject: [PATCH 0486/1006] Make paste -p the default for ], GitHub issue 2248. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index a518fbd8..9e198123 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -311,7 +311,7 @@ key_bindings_init(void) "bind -N 'Switch to the last client' L switch-client -l", "bind -N 'Clear the marked pane' M select-pane -M", "bind -N 'Enter copy mode' [ copy-mode", - "bind -N 'Paste the most recent paste buffer' ] paste-buffer", + "bind -N 'Paste the most recent paste buffer' ] paste-buffer -p", "bind -N 'Create a new window' c new-window", "bind -N 'Detach the current client' d detach-client", "bind -N 'Search for a pane' f command-prompt \"find-window -Z -- '%%'\"", From b3782d2dc8f872c43fcf53b9436c4e881d3f1e2d Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2020 07:12:05 +0000 Subject: [PATCH 0487/1006] Instead of using a custom parse function to process {}, treat it as a set of statements and parse with yacc, then convert back to a string as the last step. This means the rules are consistent inside and outside {}, %if and friends work at the right time, and the final result isn't littered with unnecessary newlines. --- arguments.c | 5 ++ cmd-parse.y | 205 ++++++++++++++++------------------------------------ cmd.c | 16 ++-- tmux.1 | 19 ++--- 4 files changed, 85 insertions(+), 160 deletions(-) diff --git a/arguments.c b/arguments.c index ef67107d..0495aa0e 100644 --- a/arguments.c +++ b/arguments.c @@ -227,6 +227,11 @@ args_escape(const char *s) return (escaped); } + if (strchr(s, ' ') != NULL && strchr(s, '\'') == NULL) { + xasprintf(&escaped, "'%s'", s); + return (escaped); + } + flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; if (s[strcspn(s, quoted)] != '\0') flags |= VIS_DQ; diff --git a/cmd-parse.y b/cmd-parse.y index 9f36af7e..985994aa 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -43,7 +43,6 @@ struct cmd_parse_scope { }; struct cmd_parse_command { - char *name; u_int line; int argc; @@ -78,6 +77,7 @@ static char *cmd_parse_get_error(const char *, u_int, const char *); static void cmd_parse_free_command(struct cmd_parse_command *); static struct cmd_parse_commands *cmd_parse_new_commands(void); static void cmd_parse_free_commands(struct cmd_parse_commands *); +static char *cmd_parse_commands_to_string(struct cmd_parse_commands *); static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, struct cmd_list *); @@ -111,7 +111,8 @@ static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, %type arguments %type if_open if_elif %type elif elif1 -%type statements statement commands condition condition1 +%type argument_statements statements statement +%type commands condition condition1 %type command %% @@ -359,7 +360,7 @@ commands : command struct cmd_parse_state *ps = &parse_state; $$ = cmd_parse_new_commands(); - if ($1->name != NULL && + if ($1->argc != 0 && (ps->scope == NULL || ps->scope->flag)) TAILQ_INSERT_TAIL($$, $1, entry); else @@ -379,7 +380,7 @@ commands : command { struct cmd_parse_state *ps = &parse_state; - if ($3->name != NULL && + if ($3->argc != 0 && (ps->scope == NULL || ps->scope->flag)) { $$ = $1; TAILQ_INSERT_TAIL($$, $3, entry); @@ -399,7 +400,6 @@ command : assignment struct cmd_parse_state *ps = &parse_state; $$ = xcalloc(1, sizeof *$$); - $$->name = NULL; $$->line = ps->input->line; } | optional_assignment TOKEN @@ -407,20 +407,21 @@ command : assignment struct cmd_parse_state *ps = &parse_state; $$ = xcalloc(1, sizeof *$$); - $$->name = $2; $$->line = ps->input->line; + cmd_prepend_argv(&$$->argc, &$$->argv, $2); + } | optional_assignment TOKEN arguments { struct cmd_parse_state *ps = &parse_state; $$ = xcalloc(1, sizeof *$$); - $$->name = $2; $$->line = ps->input->line; $$->argc = $3.argc; $$->argv = $3.argv; + cmd_prepend_argv(&$$->argc, &$$->argv, $2); } condition1 : if_open commands if_close @@ -524,6 +525,20 @@ argument : TOKEN { $$ = $1; } + | '{' argument_statements + { + $$ = cmd_parse_commands_to_string($2); + cmd_parse_free_commands($2); + } + +argument_statements : statement '}' + { + $$ = $1; + } + | statements '}' + { + $$ = $1; + } %% @@ -558,7 +573,6 @@ cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line, static void cmd_parse_free_command(struct cmd_parse_command *cmd) { - free(cmd->name); cmd_free_argv(cmd->argc, cmd->argv); free(cmd); } @@ -585,6 +599,30 @@ cmd_parse_free_commands(struct cmd_parse_commands *cmds) free(cmds); } +static char * +cmd_parse_commands_to_string(struct cmd_parse_commands *cmds) +{ + struct cmd_parse_command *cmd; + char *string = NULL, *s, *line; + + TAILQ_FOREACH(cmd, cmds, entry) { + line = cmd_stringify_argv(cmd->argc, cmd->argv); + if (string == NULL) + s = line; + else { + xasprintf(&s, "%s ; %s", s, line); + free(line); + } + + free(string); + string = s; + } + if (string == NULL) + string = xstrdup(""); + log_debug("%s: %s", __func__, string); + return (string); +} + static struct cmd_parse_commands * cmd_parse_run_parser(char **cause) { @@ -645,7 +683,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, int i; struct cmd_list *cmdlist = NULL, *result; struct cmd *add; - char *alias, *cause, *s; + char *name, *alias, *cause, *s; /* Check for an empty list. */ if (TAILQ_EMPTY(cmds)) { @@ -661,12 +699,14 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, * command list. */ TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) { - alias = cmd_get_alias(cmd->name); + name = cmd->argv[0]; + + alias = cmd_get_alias(name); if (alias == NULL) continue; line = cmd->line; - log_debug("%s: %u %s = %s", __func__, line, cmd->name, alias); + log_debug("%s: %u %s = %s", __func__, line, name, alias); pi->line = line; cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause); @@ -683,7 +723,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, cmd_parse_free_command(cmd); continue; } - for (i = 0; i < cmd->argc; i++) + for (i = 1; i < cmd->argc; i++) cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]); after = cmd; @@ -707,7 +747,8 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, */ result = cmd_list_new(); TAILQ_FOREACH(cmd, cmds, entry) { - log_debug("%s: %u %s", __func__, cmd->line, cmd->name); + name = cmd->argv[0]; + log_debug("%s: %u %s", __func__, cmd->line, name); cmd_log_argv(cmd->argc, cmd->argv, __func__); if (cmdlist == NULL || @@ -721,7 +762,6 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, } line = cmd->line; - cmd_prepend_argv(&cmd->argc, &cmd->argv, cmd->name); add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause); if (add == NULL) { cmd_list_free(result); @@ -921,11 +961,10 @@ cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) i); cmd = xcalloc(1, sizeof *cmd); - cmd->name = xstrdup(new_argv[0]); cmd->line = pi->line; - cmd->argc = new_argc - 1; - cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1); + cmd->argc = new_argc; + cmd->argv = cmd_copy_argv(new_argc, new_argv); TAILQ_INSERT_TAIL(cmds, cmd, entry); } @@ -941,11 +980,10 @@ cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) last); cmd = xcalloc(1, sizeof *cmd); - cmd->name = xstrdup(new_argv[0]); cmd->line = pi->line; - cmd->argc = new_argc - 1; - cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1); + cmd->argc = new_argc; + cmd->argv = cmd_copy_argv(new_argc, new_argv); TAILQ_INSERT_TAIL(cmds, cmd, entry); } @@ -1123,11 +1161,11 @@ yylex(void) return ('\n'); } - if (ch == ';') { + if (ch == ';' || ch == '{' || ch == '}') { /* - * A semicolon is itself. + * A semicolon or { or } is itself. */ - return (';'); + return (ch); } if (ch == '#') { @@ -1442,119 +1480,6 @@ yylex_token_tilde(char **buf, size_t *len) return (1); } -static int -yylex_token_brace(char **buf, size_t *len) -{ - struct cmd_parse_state *ps = &parse_state; - int ch, lines = 0, nesting = 1, escape = 0; - int quote = '\0', token = 0; - - /* - * Extract a string up to the matching unquoted '}', including newlines - * and handling nested braces. - * - * To detect the final and intermediate braces which affect the nesting - * depth, we scan the input as if it was a tmux config file, and ignore - * braces which would be considered quoted, escaped, or in a comment. - * - * We update the token state after every character because '#' begins a - * comment only when it begins a token. For simplicity, we treat an - * unquoted directive format as comment. - * - * The result is verbatim copy of the input excluding the final brace. - */ - - for (ch = yylex_getc1(); ch != EOF; ch = yylex_getc1()) { - yylex_append1(buf, len, ch); - if (ch == '\n') - lines++; - - /* - * If the previous character was a backslash (escape is set), - * escape anything if unquoted or in double quotes, otherwise - * escape only '\n' and '\\'. - */ - if (escape && - (quote == '\0' || - quote == '"' || - ch == '\n' || - ch == '\\')) { - escape = 0; - if (ch != '\n') - token = 1; - continue; - } - - /* - * The character is not escaped. If it is a backslash, set the - * escape flag. - */ - if (ch == '\\') { - escape = 1; - continue; - } - escape = 0; - - /* A newline always resets to unquoted. */ - if (ch == '\n') { - quote = token = 0; - continue; - } - - if (quote) { - /* - * Inside quotes or comment. Check if this is the - * closing quote. - */ - if (ch == quote && quote != '#') - quote = 0; - token = 1; /* token continues regardless */ - } else { - /* Not inside quotes or comment. */ - switch (ch) { - case '"': - case '\'': - case '#': - /* Beginning of quote or maybe comment. */ - if (ch != '#' || !token) - quote = ch; - token = 1; - break; - case ' ': - case '\t': - case ';': - /* Delimiter - token resets. */ - token = 0; - break; - case '{': - nesting++; - token = 0; /* new commands set - token resets */ - break; - case '}': - nesting--; - token = 1; /* same as after quotes */ - if (nesting == 0) { - (*len)--; /* remove closing } */ - ps->input->line += lines; - return (1); - } - break; - default: - token = 1; - break; - } - } - } - - /* - * Update line count after error as reporting the opening line is more - * useful than EOF. - */ - yyerror("unterminated brace string"); - ps->input->line += lines; - return (0); -} - static char * yylex_token(int ch) { @@ -1580,7 +1505,8 @@ yylex_token(int ch) } /* Whitespace or ; ends a token unless inside quotes. */ - if ((ch == ' ' || ch == '\t' || ch == ';') && state == NONE) + if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') && + state == NONE) break; /* @@ -1601,11 +1527,6 @@ yylex_token(int ch) goto error; goto skip; } - if (ch == '{' && state == NONE) { - if (!yylex_token_brace(&buf, &len)) - goto error; - goto skip; - } if (ch == '}' && state == NONE) goto error; /* unmatched (matched ones were handled) */ diff --git a/cmd.c b/cmd.c index 8a977023..f6023c20 100644 --- a/cmd.c +++ b/cmd.c @@ -357,25 +357,27 @@ cmd_free_argv(int argc, char **argv) char * cmd_stringify_argv(int argc, char **argv) { - char *buf; + char *buf = NULL, *s; + size_t len = 0; int i; - size_t len; if (argc == 0) return (xstrdup("")); - len = 0; - buf = NULL; - for (i = 0; i < argc; i++) { - len += strlen(argv[i]) + 1; + s = args_escape(argv[i]); + log_debug("%s: %u %s = %s", __func__, i, argv[i], s); + + len += strlen(s) + 1; buf = xrealloc(buf, len); if (i == 0) *buf = '\0'; else strlcat(buf, " ", len); - strlcat(buf, argv[i], len); + strlcat(buf, s, len); + + free(s); } return (buf); } diff --git a/tmux.1 b/tmux.1 index 2ba3362b..a6c129d3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -552,17 +552,14 @@ is removed) and are not treated as having any special meaning - so for example variable. .El .Pp -Braces are similar to single quotes in that the text inside is taken literally -without any replacements but this also includes line continuation. -Braces can span multiple lines in which case a literal newline is included in the -string. -They are designed to avoid the need for additional escaping when passing a group -of +Braces are parsed as a configuration file (so conditions such as +.Ql %if +are processed) and then converted into a string. +They are designed to avoid the need for additional escaping when passing a +group of .Nm -or shell commands as an argument (for example to -.Ic if-shell -or -.Ic pipe-pane ) . +commands as an argument (for example to +.Ic if-shell ) . These two examples produce an identical command - note that no escaping is needed when using {}: .Bd -literal -offset indent @@ -570,7 +567,7 @@ if-shell true { display -p 'brace-dollar-foo: }$foo' } -if-shell true "\en display -p 'brace-dollar-foo: }\e$foo'\en" +if-shell true "display -p 'brace-dollar-foo: }\e$foo'" .Ed .Pp Braces may be enclosed inside braces, for example: From d3c5202f50c28586a5a4e97b77332b57b798335b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2020 08:30:44 +0000 Subject: [PATCH 0488/1006] Allow strings to span multiple lines - newlines and any leading whitespace are removed, as well as any following comments that couldn't be part of a format. This allows long formats or other strings to be annotated and indented. --- cmd-parse.y | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 985994aa..6ec2eca3 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -1494,24 +1494,33 @@ yylex_token(int ch) buf = xmalloc(1); for (;;) { - /* - * EOF or \n are always the end of the token. If inside quotes - * they are an error. - */ - if (ch == EOF || ch == '\n') { - if (state != NONE) - goto error; + /* EOF or \n are always the end of the token. */ + if (ch == EOF || (state == NONE && ch == '\n')) break; - } - /* Whitespace or ; ends a token unless inside quotes. */ + /* Whitespace or ; or } ends a token unless inside quotes. */ if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') && state == NONE) break; - /* - * \ ~ and $ are expanded except in single quotes. - */ + /* Spaces and comments inside quotes after \n are removed. */ + if (ch == '\n' && state != NONE) { + while ((ch = yylex_getc()) == ' ' || ch == '\t') + /* nothing */; + if (ch != '#') + continue; + ch = yylex_getc(); + if (strchr(",#{}:", ch) != NULL) { + yylex_ungetc(ch); + ch = '#'; + } else { + while ((ch = yylex_getc()) != '\n' && ch != EOF) + /* nothing */; + } + continue; + } + + /* \ ~ and $ are expanded except in single quotes. */ if (ch == '\\' && state != SINGLE_QUOTES) { if (!yylex_token_escape(&buf, &len)) goto error; @@ -1530,9 +1539,7 @@ yylex_token(int ch) if (ch == '}' && state == NONE) goto error; /* unmatched (matched ones were handled) */ - /* - * ' and " starts or end quotes (and is consumed). - */ + /* ' and " starts or end quotes (and is consumed). */ if (ch == '\'') { if (state == NONE) { state = SINGLE_QUOTES; @@ -1554,9 +1561,7 @@ yylex_token(int ch) } } - /* - * Otherwise add the character to the buffer. - */ + /* Otherwise add the character to the buffer. */ yylex_append1(&buf, &len, ch); skip: From dc74d2e0546f86f9745f730c21a286ddc0f3d6c1 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2020 10:24:14 +0000 Subject: [PATCH 0489/1006] Make the -no-clear command variants not clear the search marks either. --- window-copy.c | 154 +++++++++++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 72 deletions(-) diff --git a/window-copy.c b/window-copy.c index 20f351aa..5ffe4dd1 100644 --- a/window-copy.c +++ b/window-copy.c @@ -177,6 +177,12 @@ enum window_copy_cmd_action { WINDOW_COPY_CMD_CANCEL, }; +enum window_copy_cmd_clear { + WINDOW_COPY_CMD_CLEAR_ALWAYS, + WINDOW_COPY_CMD_CLEAR_NEVER, + WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, +}; + struct window_copy_cmd_state { struct window_mode_entry *wme; struct args *args; @@ -2116,144 +2122,144 @@ static const struct { const char *command; int minargs; int maxargs; - int ismotion; + enum window_copy_cmd_clear clear; enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); } window_copy_cmd_table[] = { - { "append-selection", 0, 0, 0, + { "append-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_append_selection }, - { "append-selection-and-cancel", 0, 0, 0, + { "append-selection-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_append_selection_and_cancel }, - { "back-to-indentation", 0, 0, 0, + { "back-to-indentation", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_back_to_indentation }, - { "begin-selection", 0, 0, 0, + { "begin-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_begin_selection }, - { "bottom-line", 0, 0, 1, + { "bottom-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_bottom_line }, - { "cancel", 0, 0, 0, + { "cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_cancel }, - { "clear-selection", 0, 0, 0, + { "clear-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_clear_selection }, - { "copy-end-of-line", 0, 1, 0, + { "copy-end-of-line", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_copy_end_of_line }, - { "copy-line", 0, 1, 0, + { "copy-line", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_copy_line }, - { "copy-pipe-no-clear", 0, 2, 0, + { "copy-pipe-no-clear", 0, 2, WINDOW_COPY_CMD_CLEAR_NEVER, window_copy_cmd_copy_pipe_no_clear }, - { "copy-pipe", 0, 2, 0, + { "copy-pipe", 0, 2, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_copy_pipe }, - { "copy-pipe-and-cancel", 0, 2, 0, + { "copy-pipe-and-cancel", 0, 2, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_copy_pipe_and_cancel }, - { "copy-selection-no-clear", 0, 1, 0, + { "copy-selection-no-clear", 0, 1, WINDOW_COPY_CMD_CLEAR_NEVER, window_copy_cmd_copy_selection_no_clear }, - { "copy-selection", 0, 1, 0, + { "copy-selection", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_copy_selection }, - { "copy-selection-and-cancel", 0, 1, 0, + { "copy-selection-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_copy_selection_and_cancel }, - { "cursor-down", 0, 0, 1, + { "cursor-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_cursor_down }, - { "cursor-down-and-cancel", 0, 0, 0, + { "cursor-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_cursor_down_and_cancel }, - { "cursor-left", 0, 0, 1, + { "cursor-left", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_cursor_left }, - { "cursor-right", 0, 0, 1, + { "cursor-right", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_cursor_right }, - { "cursor-up", 0, 0, 1, + { "cursor-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_cursor_up }, - { "end-of-line", 0, 0, 1, + { "end-of-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_end_of_line }, - { "goto-line", 1, 1, 1, + { "goto-line", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_goto_line }, - { "halfpage-down", 0, 0, 1, + { "halfpage-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_halfpage_down }, - { "halfpage-down-and-cancel", 0, 0, 0, + { "halfpage-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_halfpage_down_and_cancel }, - { "halfpage-up", 0, 0, 1, + { "halfpage-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_halfpage_up }, - { "history-bottom", 0, 0, 1, + { "history-bottom", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_history_bottom }, - { "history-top", 0, 0, 1, + { "history-top", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_history_top }, - { "jump-again", 0, 0, 1, + { "jump-again", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_jump_again }, - { "jump-backward", 1, 1, 1, + { "jump-backward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_jump_backward }, - { "jump-forward", 1, 1, 1, + { "jump-forward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_jump_forward }, - { "jump-reverse", 0, 0, 1, + { "jump-reverse", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_jump_reverse }, - { "jump-to-backward", 1, 1, 1, + { "jump-to-backward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_jump_to_backward }, - { "jump-to-forward", 1, 1, 1, + { "jump-to-forward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_jump_to_forward }, - { "jump-to-mark", 0, 0, 0, + { "jump-to-mark", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_jump_to_mark }, - { "middle-line", 0, 0, 1, + { "middle-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_middle_line }, - { "next-matching-bracket", 0, 0, 0, + { "next-matching-bracket", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_next_matching_bracket }, - { "next-paragraph", 0, 0, 1, + { "next-paragraph", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_next_paragraph }, - { "next-space", 0, 0, 1, + { "next-space", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_next_space }, - { "next-space-end", 0, 0, 1, + { "next-space-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_next_space_end }, - { "next-word", 0, 0, 1, + { "next-word", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_next_word }, - { "next-word-end", 0, 0, 1, + { "next-word-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_next_word_end }, - { "other-end", 0, 0, 1, + { "other-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_other_end }, - { "page-down", 0, 0, 1, + { "page-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_page_down }, - { "page-down-and-cancel", 0, 0, 0, + { "page-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_page_down_and_cancel }, - { "page-up", 0, 0, 1, + { "page-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_page_up }, - { "previous-matching-bracket", 0, 0, 0, + { "previous-matching-bracket", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_previous_matching_bracket }, - { "previous-paragraph", 0, 0, 1, + { "previous-paragraph", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_previous_paragraph }, - { "previous-space", 0, 0, 1, + { "previous-space", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_previous_space }, - { "previous-word", 0, 0, 1, + { "previous-word", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_previous_word }, - { "rectangle-toggle", 0, 0, 0, + { "rectangle-toggle", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_rectangle_toggle }, - { "refresh-from-pane", 0, 0, 0, + { "refresh-from-pane", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_refresh_from_pane }, - { "scroll-down", 0, 0, 1, + { "scroll-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_scroll_down }, - { "scroll-down-and-cancel", 0, 0, 0, + { "scroll-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_scroll_down_and_cancel }, - { "scroll-up", 0, 0, 1, + { "scroll-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_scroll_up }, - { "search-again", 0, 0, 0, + { "search-again", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_search_again }, - { "search-backward", 0, 1, 0, + { "search-backward", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_search_backward }, - { "search-backward-text", 0, 1, 0, + { "search-backward-text", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_search_backward_text }, - { "search-backward-incremental", 1, 1, 0, + { "search-backward-incremental", 1, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_search_backward_incremental }, - { "search-forward", 0, 1, 0, + { "search-forward", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_search_forward }, - { "search-forward-text", 0, 1, 0, + { "search-forward-text", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_search_forward_text }, - { "search-forward-incremental", 1, 1, 0, + { "search-forward-incremental", 1, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_search_forward_incremental }, - { "search-reverse", 0, 0, 0, + { "search-reverse", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_search_reverse }, - { "select-line", 0, 0, 0, + { "select-line", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_select_line }, - { "select-word", 0, 0, 0, + { "select-word", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_select_word }, - { "set-mark", 0, 0, 0, + { "set-mark", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_set_mark }, - { "start-of-line", 0, 0, 1, + { "start-of-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_start_of_line }, - { "stop-selection", 0, 0, 0, + { "stop-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_stop_selection }, - { "top-line", 0, 0, 1, + { "top-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_top_line }, }; @@ -2265,9 +2271,10 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, struct window_copy_mode_data *data = wme->data; struct window_copy_cmd_state cs; enum window_copy_cmd_action action; + enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER; const char *command; u_int i; - int ismotion = 0, keys; + int keys; if (args->argc == 0) return; @@ -2290,7 +2297,7 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, if (args->argc - 1 < window_copy_cmd_table[i].minargs || args->argc - 1 > window_copy_cmd_table[i].maxargs) break; - ismotion = window_copy_cmd_table[i].ismotion; + clear = window_copy_cmd_table[i].clear; action = window_copy_cmd_table[i].f (&cs); break; } @@ -2298,7 +2305,10 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) { keys = options_get_number(wme->wp->window->options, "mode-keys"); - if (keys != MODEKEY_VI || !ismotion) { + if (clear == WINDOW_COPY_CMD_CLEAR_EMACS_ONLY && + keys == MODEKEY_VI) + clear = WINDOW_COPY_CMD_CLEAR_NEVER; + if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) { window_copy_clear_marks(wme); data->searchx = data->searchy = -1; } else if (data->searchthis != -1) { From a9bf5367da4ee133c417ae00dc87e97d71b7f955 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2020 10:34:40 +0000 Subject: [PATCH 0490/1006] Correct respawn-* - they don't always use the creation command. --- tmux.1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index a6c129d3..68d8b3cc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2519,7 +2519,8 @@ Reactivate a pane in which the command has exited (see the window option). If .Ar shell-command -is not given, the command used when the pane was created is executed. +is not given, the command used when the pane was created or last respawned is +executed. The pane must be already inactive, unless .Fl k is given, in which case any existing command is killed. @@ -2543,7 +2544,8 @@ Reactivate a window in which the command has exited (see the window option). If .Ar shell-command -is not given, the command used when the window was created is executed. +is not given, the command used when the window was created or last respawned is +executed. The window must be already inactive, unless .Fl k is given, in which case any existing command is killed. From 4ea3370316671084da16070137c4be6505d723bc Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2020 10:36:28 +0000 Subject: [PATCH 0491/1006] Shorten some long lines. --- tty-features.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tty-features.c b/tty-features.c index 8f712f64..ea662bb4 100644 --- a/tty-features.c +++ b/tty-features.c @@ -332,21 +332,26 @@ tty_default_features(int *feat, const char *name, u_int version) u_int version; const char *features; } table[] = { -#define TTY_FEATURES_BASE_MODERN_XTERM "256,RGB,bpaste,clipboard,strikethrough,title" +#define TTY_FEATURES_BASE_MODERN_XTERM \ + "256,RGB,bpaste,clipboard,strikethrough,title" { .name = "mintty", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,margins,overline" + .features = TTY_FEATURES_BASE_MODERN_XTERM + ",ccolour,cstyle,extkeys,margins,overline" }, { .name = "tmux", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,overline,usstyle" + .features = TTY_FEATURES_BASE_MODERN_XTERM + ",ccolour,cstyle,focus,overline,usstyle" }, { .name = "rxvt-unicode", .features = "256,bpaste,ccolour,cstyle,title" }, { .name = "iTerm2", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,extkeys,margins,sync" + .features = TTY_FEATURES_BASE_MODERN_XTERM + ",cstyle,extkeys,margins,sync" }, { .name = "XTerm", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,focus,margins,rectfill" + .features = TTY_FEATURES_BASE_MODERN_XTERM + ",ccolour,cstyle,extkeys,focus,margins,rectfill" } }; u_int i; From 23a3742dc89131a32de2ae71c697a39e7a2a2956 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2020 11:42:51 +0100 Subject: [PATCH 0492/1006] Update CHANGES. --- CHANGES | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGES b/CHANGES index eb94477f..7ee50367 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,26 @@ CHANGES FROM 3.1b TO 3.2 +* Add support for extended keys - both xterm(1)'s CSI 27 ~ sequence and the + libtickit CSI u sequence are accepted; only the latter is output. tmux will + only attempt to use these if the extended-keys option is on and it can detect + that the terminal outside supports them (or is told it does with the + "extkeys" terminal feature). + +* Add an option to set the pane border lines style from a choice of single + lines (ACS or UTF-8), double or heavy (UTF-8), simple (plain ASCII) or number + (the pane numbers). Lines that won't work on a non-UTF-8 terminal are + translated back into ACS when they are output. + +* Make focus events update the latest client (like a key press). + +* Store UTF-8 characters differently to reduce memory use. + +* Fix break-pane -n when only one pane in the window. + +* Instead of sending all data to control mode clients as fast as possible, add + a limit of how much data will be sent to the client and try to use it for + panes with some degree of fairness. + * Add an active-pane client flag (set with attach-session -f, new-session -f or refresh-client -f). This allows a client to have an independent active pane for interactive use (the window client pane is still used for many From 4403afe29c2106371ff43c28b9f0a3ed50fd61ee Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2020 16:06:01 +0000 Subject: [PATCH 0493/1006] A } can go on the same line as a command. --- cmd-parse.y | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd-parse.y b/cmd-parse.y index 6ec2eca3..0a0b1993 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -535,9 +535,11 @@ argument_statements : statement '}' { $$ = $1; } - | statements '}' + | statements statement '}' { $$ = $1; + TAILQ_CONCAT($$, $2, entry); + free($2); } %% From c4732af0067d8cf8d2e45fa78ded6b8f308ec779 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2020 20:41:57 +0000 Subject: [PATCH 0494/1006] Some improvements to performance of searching: - Do not allow searches to be repeated at intervals of less than 50 milliseconds, to prevent a huge queue of repeat key presses blocking up everything for ages. - If the search text hasn't changed, the match count can't have changed and there is no need to do a full search, so only search the visible text. This includes both scrolling and repeating the search. - Do not redraw twice when jumping to the search location. GitHub issue 2258. --- window-copy.c | 138 +++++++++++++++++++++++++++++++------------------- 1 file changed, 86 insertions(+), 52 deletions(-) diff --git a/window-copy.c b/window-copy.c index 5ffe4dd1..f03cfa8c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -52,7 +52,8 @@ static void window_copy_write_line(struct window_mode_entry *, static void window_copy_write_lines(struct window_mode_entry *, struct screen_write_ctx *, u_int, u_int); -static void window_copy_scroll_to(struct window_mode_entry *, u_int, u_int); +static void window_copy_scroll_to(struct window_mode_entry *, u_int, u_int, + int); static int window_copy_search_compare(struct grid *, u_int, u_int, struct grid *, u_int, int); static int window_copy_search_lr(struct grid *, struct grid *, u_int *, @@ -67,7 +68,7 @@ static char *window_copy_stringify(struct grid *, u_int, u_int, u_int, static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, u_int *, const char *str); static int window_copy_search_marks(struct window_mode_entry *, - struct screen *, int); + 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); @@ -265,6 +266,7 @@ struct window_copy_mode_data { u_int my; int showmark; + uint64_t searchtime; int searchtype; int searchregex; char *searchstr; @@ -280,6 +282,7 @@ struct window_copy_mode_data { int timeout; /* search has timed out */ #define WINDOW_COPY_SEARCH_TIMEOUT 10000 #define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200 +#define WINDOW_COPY_SEARCH_REPEAT 50 int jumptype; char jumpchar; @@ -588,7 +591,7 @@ window_copy_pageup1(struct window_mode_entry *wme, int half_page) } if (data->searchmark != NULL && !data->timeout) - window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -638,7 +641,7 @@ window_copy_pagedown(struct window_mode_entry *wme, int half_page, if (scroll_exit && data->oy == 0) return (1); if (data->searchmark != NULL && !data->timeout) - window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); return (0); @@ -658,7 +661,7 @@ window_copy_previous_paragraph(struct window_mode_entry *wme) while (oy > 0 && window_copy_find_length(wme, oy) > 0) oy--; - window_copy_scroll_to(wme, 0, oy); + window_copy_scroll_to(wme, 0, oy, 0); } static void @@ -678,7 +681,7 @@ window_copy_next_paragraph(struct window_mode_entry *wme) oy++; ox = window_copy_find_length(wme, oy); - window_copy_scroll_to(wme, ox, oy); + window_copy_scroll_to(wme, ox, oy, 0); } char * @@ -754,7 +757,7 @@ window_copy_size_changed(struct window_mode_entry *wme) screen_write_stop(&ctx); if (search && !data->timeout) - window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_search_marks(wme, NULL, data->searchregex, 0); data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; @@ -1150,7 +1153,7 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) data->oy = 0; if (data->searchmark != NULL && !data->timeout) - window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1171,7 +1174,7 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs) data->oy = screen_hsize(data->backing); if (data->searchmark != NULL && !data->timeout) - window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); return (WINDOW_COPY_CMD_REDRAW); } @@ -1325,7 +1328,7 @@ window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) /* Move the cursor to the found location if any. */ if (!failed) - window_copy_scroll_to(wme, px, py); + window_copy_scroll_to(wme, px, py, 0); } return (WINDOW_COPY_CMD_NOTHING); @@ -1376,7 +1379,7 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) sx = data->cx; sy = screen_hsize(s) + data->cy - data->oy; - window_copy_scroll_to(wme, px, py); + window_copy_scroll_to(wme, px, py, 0); window_copy_cmd_previous_matching_bracket(cs); px = data->cx; @@ -1385,7 +1388,7 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) if (gc.data.size == 1 && (~gc.flags & GRID_FLAG_PADDING) && strchr(close, *gc.data.data) != NULL) - window_copy_scroll_to(wme, sx, sy); + window_copy_scroll_to(wme, sx, sy, 0); break; } @@ -1447,7 +1450,7 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) /* Move the cursor to the found location if any. */ if (!failed) - window_copy_scroll_to(wme, px, py); + window_copy_scroll_to(wme, px, py, 0); } return (WINDOW_COPY_CMD_NOTHING); @@ -1659,6 +1662,10 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs) struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; + if (data->searchtime != 0 && + get_timer() - data->searchtime < WINDOW_COPY_SEARCH_REPEAT) + return (WINDOW_COPY_CMD_NOTHING); + if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) window_copy_search_up(wme, data->searchregex); @@ -1666,6 +1673,7 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs) for (; np != 0; np--) window_copy_search_down(wme, data->searchregex); } + data->searchtime = get_timer(); return (WINDOW_COPY_CMD_NOTHING); } @@ -1676,6 +1684,10 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; + if (data->searchtime != 0 && + get_timer() - data->searchtime < WINDOW_COPY_SEARCH_REPEAT) + return (WINDOW_COPY_CMD_NOTHING); + if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) window_copy_search_down(wme, data->searchregex); @@ -1683,6 +1695,7 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) for (; np != 0; np--) window_copy_search_up(wme, data->searchregex); } + data->searchtime = get_timer(); return (WINDOW_COPY_CMD_NOTHING); } @@ -2327,7 +2340,8 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, } static void -window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py) +window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py, + int no_redraw) { struct window_copy_mode_data *data = wme->data; struct grid *gd = data->backing->grid; @@ -2352,10 +2366,11 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py) data->oy = gd->hsize - offset; } - if (data->searchmark != NULL && !data->timeout) - window_copy_search_marks(wme, NULL, data->searchregex); + if (!no_redraw && data->searchmark != NULL && !data->timeout) + window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); - window_copy_redraw_screen(wme); + if (!no_redraw) + window_copy_redraw_screen(wme); } static int @@ -2877,7 +2892,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, } if (found) { - window_copy_scroll_to(wme, px, i); + window_copy_scroll_to(wme, px, i, 1); return (1); } if (wrap) { @@ -2903,7 +2918,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) struct grid *gd = s->grid; const char *str = data->searchstr; u_int fx, fy, endline; - int wrapflag, cis, found; + int wrapflag, cis, found, visible_only; if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') regex = 0; @@ -2911,6 +2926,10 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) if (data->timeout) return (0); + if (wp->searchstr == NULL || wp->searchregex != regex) + visible_only = 0; + else + visible_only = (strcmp(wp->searchstr, str) == 0); free(wp->searchstr); wp->searchstr = xstrdup(str); wp->searchregex = regex; @@ -2936,23 +2955,37 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, wrapflag, direction, regex); - - if (window_copy_search_marks(wme, &ss, regex)) - window_copy_redraw_screen(wme); + if (found) + window_copy_search_marks(wme, &ss, regex, visible_only); + window_copy_redraw_screen(wme); screen_free(&ss); return (found); } +static void +window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start, + u_int *end) +{ + struct grid *gd = data->backing->grid; + const struct grid_line *gl; + + for (*start = gd->hsize - data->oy; *start > 0; (*start)--) { + gl = grid_peek_line(gd, (*start) - 1); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + } + *end = gd->hsize - data->oy + gd->sy; +} + static int window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, - int regex) + int regex, int visible_only) { struct window_copy_mode_data *data = wme->data; struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; - const struct grid_line *gl; int found, cis, which = -1, stopped = 0; int cflags = REG_EXTENDED; u_int px, py, i, b, nfound = 0, width; @@ -2988,9 +3021,13 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, } tstart = get_timer(); - start = 0; - end = gd->hsize + gd->sy; - stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; + if (visible_only) + window_copy_visible_lines(data, &start, &end); + else { + start = 0; + end = gd->hsize + gd->sy; + stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; + } again: free(data->searchmark); @@ -3045,34 +3082,31 @@ again: if (stopped && stop != 0) { /* Try again but just the visible context. */ - for (start = gd->hsize - data->oy; start > 0; start--) { - gl = grid_peek_line(gd, start - 1); - if (~gl->flags & GRID_LINE_WRAPPED) - break; - } - end = gd->hsize - data->oy + gd->sy; + window_copy_visible_lines(data, &start, &end); stop = 0; goto again; } - if (stopped) { - data->searchthis = -1; - if (nfound > 1000) - data->searchcount = 1000; - else if (nfound > 100) - data->searchcount = 100; - else if (nfound > 10) - data->searchcount = 10; - else - data->searchcount = -1; - data->searchmore = 1; - } else { - if (which != -1) - data->searchthis = 1 + nfound - which; - else + if (!visible_only) { + if (stopped) { data->searchthis = -1; - data->searchcount = nfound; - data->searchmore = 0; + if (nfound > 1000) + data->searchcount = 1000; + else if (nfound > 100) + data->searchcount = 100; + else if (nfound > 10) + data->searchcount = 10; + else + data->searchcount = -1; + data->searchmore = 1; + } else { + if (which != -1) + data->searchthis = 1 + nfound - which; + else + data->searchthis = -1; + data->searchcount = nfound; + data->searchmore = 0; + } } out: @@ -4563,7 +4597,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) data->oy -= ny; if (data->searchmark != NULL && !data->timeout) - window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 0, 0); screen_write_start_pane(&ctx, wp, NULL); @@ -4599,7 +4633,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) data->oy += ny; if (data->searchmark != NULL && !data->timeout) - window_copy_search_marks(wme, NULL, data->searchregex); + window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 0, 0); screen_write_start_pane(&ctx, wp, NULL); From 2154e1f4fbc01a43934b892ca3e6d6ea5bf003a1 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2020 21:40:27 +0000 Subject: [PATCH 0495/1006] Search marks outside the visible text are not useful, so there is no point in allocating a big buffer to store them - just allocate the visible text size, and ignore any outside. --- window-copy.c | 50 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/window-copy.c b/window-copy.c index f03cfa8c..9e661245 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2978,6 +2978,21 @@ window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start, *end = gd->hsize - data->oy + gd->sy; } +static int +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; + + if (py < gd->hsize - data->oy) + return (-1); + if (py > gd->hsize - data->oy + gd->sy - 1) + return (-1); + *at = ((py - (gd->hsize - data->oy)) * gd->sx) + px; + return (0); +} + static int window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, int regex, int visible_only) @@ -3031,7 +3046,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, again: free(data->searchmark); - data->searchmark = xcalloc(gd->hsize + gd->sy, gd->sx); + data->searchmark = xcalloc(gd->sx, gd->sy); data->searchgen = 1; for (py = start; py < end; py++) { @@ -3054,13 +3069,16 @@ again: py == gd->hsize + data->cy - data->oy) which = nfound; - b = (py * gd->sx) + px; - for (i = b; i < b + width; i++) - data->searchmark[i] = data->searchgen; - if (data->searchgen == UCHAR_MAX) - data->searchgen = 1; - else - data->searchgen++; + if (window_copy_search_mark_at(data, px, py, &b) == 0) { + if (b + width > gd->sx * gd->sy) + width = (gd->sx * gd->sy) - b; + for (i = b; i < b + width; i++) + data->searchmark[i] = data->searchgen; + if (data->searchgen == UCHAR_MAX) + data->searchgen = 1; + else + data->searchgen++; + } px++; } @@ -3163,7 +3181,7 @@ window_copy_match_start_end(struct window_copy_mode_data *data, u_int at, u_int *start, u_int *end) { struct grid *gd = data->backing->grid; - u_int last = (gd->hsize + gd->sy) * gd->sx - 1; + u_int last = (gd->sy * gd->sx) - 1; u_char mark = data->searchmark[at]; *start = *end = at; @@ -3191,7 +3209,8 @@ window_copy_match_at_cursor(struct window_copy_mode_data *data) return (NULL); cy = screen_hsize(data->backing) - data->oy + data->cy; - at = (cy * sx) + data->cx; + if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0) + return (NULL); if (data->searchmark[at] == 0) return (NULL); window_copy_match_start_end(data, at, &start, &end); @@ -3204,7 +3223,7 @@ window_copy_match_at_cursor(struct window_copy_mode_data *data) py = at / sx; px = at - (py * sx); - grid_get_cell(gd, px, py, &gc); + grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc); buf = xrealloc(buf, len + gc.data.size + 1); memcpy(buf + len, gc.data.data, gc.data.size); len += gc.data.size; @@ -3221,7 +3240,6 @@ 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; - u_int sx = screen_size_x(data->backing); int inv = 0; if (data->showmark && fy == data->my) { @@ -3241,15 +3259,15 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, if (data->searchmark == NULL) return; - current = (fy * sx) + fx; - + if (window_copy_search_mark_at(data, fx, fy, ¤t) != 0) + return; mark = data->searchmark[current]; if (mark == 0) return; cy = screen_hsize(data->backing) - data->oy + data->cy; - cursor = (cy * sx) + data->cx; - if (data->searchmark[cursor] == mark) { + 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; From d9cd493d093f14b934343a2e57998c86fbca2ef7 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2020 21:41:31 +0000 Subject: [PATCH 0496/1006] Reset wrapped flag when clearing or moving lines, GitHub issue 2215. --- grid.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/grid.c b/grid.c index 2e3f50b9..0c837748 100644 --- a/grid.c +++ b/grid.c @@ -649,6 +649,8 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg) grid_free_line(gd, yy); grid_empty_line(gd, yy, bg); } + if (py != 0) + gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; } /* Move a group of lines. */ @@ -675,6 +677,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg) continue; grid_free_line(gd, yy); } + if (dy != 0) + gd->linedata[dy - 1].flags &= ~GRID_LINE_WRAPPED; memmove(&gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata)); @@ -687,8 +691,11 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg) if (yy < dy || yy >= dy + ny) grid_empty_line(gd, yy, bg); } + if (py != 0 && (py < dy || py >= dy + ny)) + gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; } + /* Move a group of cells. */ void grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx, From c586208991e4291450757e3a19739f368aecbe5d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2020 07:33:57 +0000 Subject: [PATCH 0497/1006] Add support for pausing a pane when the output buffered for a control mode client gets too far behind. The pause-after flag with a time is set on the pane with refresh-client -f and a paused pane may be resumed with refresh-client -A. GitHub issue 2217. --- cmd-refresh-client.c | 4 ++- control.c | 63 +++++++++++++++++++++++++++++++++++--------- resize.c | 16 +++++++---- server-client.c | 40 +++++++++++++++++++++------- tmux.1 | 45 ++++++++++++++++++++++++------- tmux.h | 9 +++++-- 6 files changed, 138 insertions(+), 39 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 4eb8417b..bbe0c736 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -66,6 +66,8 @@ cmd_refresh_client_update_offset(struct client *tc, const char *value) control_set_pane_on(tc, wp); else if (strcmp(colon, "off") == 0) control_set_pane_off(tc, wp); + else if (strcmp(colon, "continue") == 0) + control_continue_pane(tc, wp); out: free(copy); @@ -168,7 +170,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) } tty_set_size(&tc->tty, x, y, 0, 0); tc->flags |= CLIENT_SIZECHANGED; - recalculate_sizes(); + recalculate_sizes_now(1); return (CMD_RETURN_NORMAL); } diff --git a/control.c b/control.c index 1801bd75..b9b22567 100644 --- a/control.c +++ b/control.c @@ -65,6 +65,7 @@ struct control_pane { int flags; #define CONTROL_PANE_OFF 0x1 +#define CONTROL_PANE_PAUSED 0x2 int pending_flag; TAILQ_ENTRY(control_pane) pending_entry; @@ -153,6 +154,19 @@ control_add_pane(struct client *c, struct window_pane *wp) return (cp); } +/* Discard output for a pane. */ +static void +control_discard_pane(struct client *c, struct control_pane *cp) +{ + struct control_state *cs = c->control_state; + struct control_block *cb, *cb1; + + TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { + TAILQ_REMOVE(&cp->blocks, cb, entry); + control_free_block(cs, cb); + } +} + /* Get actual pane for this client. */ static struct window_pane * control_window_pane(struct client *c, u_int pane) @@ -197,7 +211,7 @@ control_pane_offset(struct client *c, struct window_pane *wp, int *off) } cp = control_get_pane(c, wp); - if (cp == NULL) { + if (cp == NULL || (cp->flags & CONTROL_PANE_PAUSED)) { *off = 0; return (NULL); } @@ -216,7 +230,7 @@ control_set_pane_on(struct client *c, struct window_pane *wp) struct control_pane *cp; cp = control_get_pane(c, wp); - if (cp != NULL) { + if (cp != NULL && (cp->flags & CONTROL_PANE_OFF)) { cp->flags &= ~CONTROL_PANE_OFF; memcpy(&cp->offset, &wp->offset, sizeof cp->offset); memcpy(&cp->queued, &wp->offset, sizeof cp->queued); @@ -233,6 +247,21 @@ control_set_pane_off(struct client *c, struct window_pane *wp) cp->flags |= CONTROL_PANE_OFF; } +/* Continue a paused pane. */ +void +control_continue_pane(struct client *c, struct window_pane *wp) +{ + struct control_pane *cp; + + cp = control_get_pane(c, wp); + if (cp != NULL && (cp->flags & CONTROL_PANE_PAUSED)) { + cp->flags &= ~CONTROL_PANE_PAUSED; + memcpy(&cp->offset, &wp->offset, sizeof cp->offset); + memcpy(&cp->queued, &wp->offset, sizeof cp->queued); + control_write(c, "%%continue %%%u", wp->id); + } +} + /* Write a line. */ static void control_vwrite(struct client *c, const char *fmt, va_list ap) @@ -285,6 +314,7 @@ control_write_output(struct client *c, struct window_pane *wp) struct control_pane *cp; struct control_block *cb; size_t new_size; + uint64_t t; if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) return; @@ -296,8 +326,22 @@ control_write_output(struct client *c, struct window_pane *wp) return; } cp = control_add_pane(c, wp); - if (cp->flags & CONTROL_PANE_OFF) + if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED)) goto ignore; + if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { + cb = TAILQ_FIRST(&cp->blocks); + if (cb != NULL) { + t = get_timer(); + log_debug("%s: %s: %%%u is %lld behind", __func__, + c->name, wp->id, (long long)t - cb->t); + if (cb->t < t - c->pause_age) { + cp->flags |= CONTROL_PANE_PAUSED; + control_discard_pane(c, cp); + control_write(c, "%%pause %%%u", wp->id); + return; + } + } + } window_pane_get_new_data(wp, &cp->queued, &new_size); if (new_size == 0) @@ -585,20 +629,15 @@ control_start(struct client *c) } } -/* Flush all output for a client that is detaching. */ +/* Discard all output for a client. */ void -control_flush(struct client *c) +control_discard(struct client *c) { struct control_state *cs = c->control_state; struct control_pane *cp; - struct control_block *cb, *cb1; - RB_FOREACH(cp, control_panes, &cs->panes) { - TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { - TAILQ_REMOVE(&cp->blocks, cb, entry); - control_free_block(cs, cb); - } - } + RB_FOREACH(cp, control_panes, &cs->panes) + control_discard_pane(c, cp); } /* Stop control mode. */ diff --git a/resize.c b/resize.c index 68717e35..d6e6dce2 100644 --- a/resize.c +++ b/resize.c @@ -227,7 +227,7 @@ done: } void -recalculate_size(struct window *w) +recalculate_size(struct window *w, int now) { struct session *s; struct client *c; @@ -348,10 +348,10 @@ recalculate_size(struct window *w) break; } if (w->flags & WINDOW_RESIZE) { - if (changed && w->new_sx == sx && w->new_sy == sy) + if (!now && changed && w->new_sx == sx && w->new_sy == sy) changed = 0; } else { - if (changed && w->sx == sx && w->sy == sy) + if (!now && changed && w->sx == sx && w->sy == sy) changed = 0; } @@ -360,7 +360,7 @@ recalculate_size(struct window *w) return; } log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy); - if (type == WINDOW_SIZE_MANUAL) + if (now || type == WINDOW_SIZE_MANUAL) resize_window(w, sx, sy, xpixel, ypixel); else { w->new_sx = sx; @@ -375,6 +375,12 @@ recalculate_size(struct window *w) void recalculate_sizes(void) +{ + recalculate_sizes_now(0); +} + +void +recalculate_sizes_now(int now) { struct session *s; struct client *c; @@ -407,5 +413,5 @@ recalculate_sizes(void) /* Walk each window and adjust the size. */ RB_FOREACH(w, windows, &windows) - recalculate_size(w); + recalculate_size(w, now); } diff --git a/server-client.c b/server-client.c index e3383aab..1a02a240 100644 --- a/server-client.c +++ b/server-client.c @@ -1093,7 +1093,7 @@ server_client_update_latest(struct client *c) w->latest = c; if (options_get_number(w->options, "window-size") == WINDOW_SIZE_LATEST) - recalculate_size(w); + recalculate_size(w, 0); } /* @@ -1541,7 +1541,7 @@ server_client_check_pane_buffer(struct window_pane *wp) __func__, c->name, wpo->used - wp->base_offset, new_size, wp->id); if (new_size > SERVER_CLIENT_PANE_LIMIT) { - control_flush(c); + control_discard(c); c->flags |= CLIENT_EXIT; } if (wpo->used < minimum) @@ -1785,7 +1785,7 @@ server_client_check_exit(struct client *c) return; if (c->flags & CLIENT_CONTROL) { - control_flush(c); + control_discard(c); if (!control_all_done(c)) return; } @@ -2362,6 +2362,23 @@ server_client_get_cwd(struct client *c, struct session *s) return ("/"); } +/* Get control client flags. */ +static uint64_t +server_client_control_flags(struct client *c, const char *next) +{ + if (strcmp(next, "pause-after") == 0) { + c->pause_age = 0; + return (CLIENT_CONTROL_PAUSEAFTER); + } + if (sscanf(next, "pause-after=%u", &c->pause_age) == 1) { + c->pause_age *= 1000; + return (CLIENT_CONTROL_PAUSEAFTER); + } + if (strcmp(next, "no-output") == 0) + return (CLIENT_CONTROL_NOOUTPUT); + return (0); +} + /* Set client flags. */ void server_client_set_flags(struct client *c, const char *flags) @@ -2376,11 +2393,10 @@ server_client_set_flags(struct client *c, const char *flags) if (not) next++; - flag = 0; - if (c->flags & CLIENT_CONTROL) { - if (strcmp(next, "no-output") == 0) - flag = CLIENT_CONTROL_NOOUTPUT; - } + if (c->flags & CLIENT_CONTROL) + flag = server_client_control_flags(c, next); + else + flag = 0; if (strcmp(next, "read-only") == 0) flag = CLIENT_READONLY; else if (strcmp(next, "ignore-size") == 0) @@ -2405,7 +2421,8 @@ server_client_set_flags(struct client *c, const char *flags) const char * server_client_get_flags(struct client *c) { - static char s[256]; + static char s[256]; + char tmp[32]; *s = '\0'; if (c->flags & CLIENT_ATTACHED) @@ -2416,6 +2433,11 @@ server_client_get_flags(struct client *c) strlcat(s, "ignore-size,", sizeof s); if (c->flags & CLIENT_CONTROL_NOOUTPUT) strlcat(s, "no-output,", sizeof s); + if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { + xsnprintf(tmp, sizeof tmp, "pause-after=%u,", + c->pause_age / 1000); + strlcat(s, tmp, sizeof s); + } if (c->flags & CLIENT_READONLY) strlcat(s, "read-only,", sizeof s); if (c->flags & CLIENT_ACTIVEPANE) diff --git a/tmux.1 b/tmux.1 index 68d8b3cc..4489c9ae 100644 --- a/tmux.1 +++ b/tmux.1 @@ -977,14 +977,18 @@ detaching the client, typically causing it to exit. sets a comma-separated list of client flags. The flags are: .Bl -tag -width Ds -.It read-only -the client is read-only +.It active-pane +the client has an independent active pane .It ignore-size the client does not affect the size of other clients .It no-output the client does not receive pane output in control mode -.It active-pane -the client has an independent active pane +.It pause-after=seconds +output is paused once the pane is +.Ar seconds +behind in control mode +.It read-only +the client is read-only .El .Pp A leading @@ -1295,22 +1299,27 @@ it. .Fl C sets the width and height of a control mode client. .Fl A -informs -.Nm -of a control mode client's interest in a pane. +allows a control mode client to trigger actions on a pane. The argument is a pane ID (with leading .Ql % ) , a colon, then one of -.Ql on +.Ql on , +.Ql off or -.Ql off . +.Ql continue . If .Ql off , .Nm will not send output from the pane to the client and if all clients have turned the pane off, will stop reading from the pane. +If +.Ql continue , +.Nm +will return to sending output to a paused pane (see the +.Ar pause-after +flag). .Fl A -may be given multiple times. +may be given multiple times for different panes. .Pp .Fl f sets a comma-separated list of client flags, see @@ -3345,6 +3354,10 @@ Allows setting the system clipboard. Allows setting the cursor colour. .It cstyle Allows setting the cursor style. +.It extkeys +Supports extended keys. +.It focus +Supports focus reporting. .It margins Supports DECSLRM margins. .It overline @@ -3353,6 +3366,8 @@ Supports the overline SGR attribute. Supports the DECFRA rectangle fill escape sequence. .It RGB Supports RGB colour with the SGR escape sequences. +.It strikethrough +Supports the strikethrough SGR escape sequence. .It sync Supports synchronized updates. .It title @@ -5881,6 +5896,12 @@ The client is now attached to the session with ID .Ar session-id , which is named .Ar name . +.It Ic %continue Ar pane-id +The pane has been continued after being paused (if the +.Ar pause-after +flag is set, see +.Ic refresh-client +.Fl A ) . .It Ic %exit Op Ar reason The .Nm @@ -5907,6 +5928,10 @@ escapes non-printable characters and backslash as octal \\xxx. The pane with ID .Ar pane-id has changed mode. +.It Ic %pause Ar pane-id +The pane has been paused (if the +.Ar pause-after +flag is set). .It Ic %session-changed Ar session-id Ar name The client is now attached to the session with ID .Ar session-id , diff --git a/tmux.h b/tmux.h index 4523fce4..fa11708c 100644 --- a/tmux.h +++ b/tmux.h @@ -1575,7 +1575,9 @@ struct client { struct cmdq_list *queue; struct client_windows windows; + struct control_state *control_state; + u_int pause_age; pid_t pid; int fd; @@ -1643,6 +1645,7 @@ struct client { #define CLIENT_REDRAWPANES 0x20000000 #define CLIENT_NOFORK 0x40000000 #define CLIENT_ACTIVEPANE 0x80000000ULL +#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -2449,8 +2452,9 @@ void status_prompt_save_history(void); void resize_window(struct window *, u_int, u_int, int, int); void default_window_size(struct client *, struct session *, struct window *, u_int *, u_int *, u_int *, u_int *, int); -void recalculate_size(struct window *); +void recalculate_size(struct window *, int); void recalculate_sizes(void); +void recalculate_sizes_now(int); /* input.c */ struct input_ctx *input_init(struct window_pane *, struct bufferevent *); @@ -2837,11 +2841,12 @@ char *default_window_name(struct window *); char *parse_window_name(const char *); /* control.c */ -void control_flush(struct client *); +void control_discard(struct client *); void control_start(struct client *); void control_stop(struct client *); void control_set_pane_on(struct client *, struct window_pane *); void control_set_pane_off(struct client *, struct window_pane *); +void control_continue_pane(struct client *, struct window_pane *); struct window_pane_offset *control_pane_offset(struct client *, struct window_pane *, int *); void control_reset_offsets(struct client *); From 4e5e2c19d0bb9aaad12ddd6dd0fc50a150a6c0cb Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2020 07:44:42 +0000 Subject: [PATCH 0498/1006] Now that we mostly only search visible text, the rate limit on repeating search does not seem to be necessary, remove it for the moment. --- window-copy.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/window-copy.c b/window-copy.c index 9e661245..889306d2 100644 --- a/window-copy.c +++ b/window-copy.c @@ -266,7 +266,6 @@ struct window_copy_mode_data { u_int my; int showmark; - uint64_t searchtime; int searchtype; int searchregex; char *searchstr; @@ -282,7 +281,6 @@ struct window_copy_mode_data { int timeout; /* search has timed out */ #define WINDOW_COPY_SEARCH_TIMEOUT 10000 #define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200 -#define WINDOW_COPY_SEARCH_REPEAT 50 int jumptype; char jumpchar; @@ -1662,10 +1660,6 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs) struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - if (data->searchtime != 0 && - get_timer() - data->searchtime < WINDOW_COPY_SEARCH_REPEAT) - return (WINDOW_COPY_CMD_NOTHING); - if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) window_copy_search_up(wme, data->searchregex); @@ -1673,7 +1667,6 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs) for (; np != 0; np--) window_copy_search_down(wme, data->searchregex); } - data->searchtime = get_timer(); return (WINDOW_COPY_CMD_NOTHING); } @@ -1684,10 +1677,6 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - if (data->searchtime != 0 && - get_timer() - data->searchtime < WINDOW_COPY_SEARCH_REPEAT) - return (WINDOW_COPY_CMD_NOTHING); - if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) window_copy_search_down(wme, data->searchregex); @@ -1695,7 +1684,6 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) for (; np != 0; np--) window_copy_search_up(wme, data->searchregex); } - data->searchtime = get_timer(); return (WINDOW_COPY_CMD_NOTHING); } From c908d2039f97a593fb25da22843c70517495da9b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2020 09:32:15 +0000 Subject: [PATCH 0499/1006] Fix various confusion about am vs xenl. --- tmux.h | 4 ++-- tty-term.c | 17 +++++++++++------ tty.c | 18 +++++++++--------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/tmux.h b/tmux.h index fa11708c..0681100f 100644 --- a/tmux.h +++ b/tmux.h @@ -257,6 +257,7 @@ enum { /* Termcap codes. */ enum tty_code_code { TTYC_ACSC, + TTYC_AM, TTYC_AX, TTYC_BCE, TTYC_BEL, @@ -477,7 +478,6 @@ enum tty_code_code { TTYC_TSL, TTYC_U8, TTYC_VPA, - TTYC_XENL, TTYC_XT }; @@ -1256,7 +1256,7 @@ struct tty_term { struct tty_code *codes; #define TERM_256COLOURS 0x1 -#define TERM_NOXENL 0x2 +#define TERM_NOAM 0x2 #define TERM_DECSLRM 0x4 #define TERM_DECFRA 0x8 #define TERM_RGBCOLOURS 0x10 diff --git a/tty-term.c b/tty-term.c index 76c4fb57..3ccff2ff 100644 --- a/tty-term.c +++ b/tty-term.c @@ -54,6 +54,7 @@ struct tty_term_code_entry { static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_ACSC] = { TTYCODE_STRING, "acsc" }, + [TTYC_AM] = { TTYCODE_FLAG, "am" }, [TTYC_AX] = { TTYCODE_FLAG, "AX" }, [TTYC_BCE] = { TTYCODE_FLAG, "bce" }, [TTYC_BEL] = { TTYCODE_STRING, "bel" }, @@ -274,7 +275,6 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, [TTYC_U8] = { TTYCODE_NUMBER, "U8" }, [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, - [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, [TTYC_XT] = { TTYCODE_FLAG, "XT" } }; @@ -580,17 +580,22 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) tty_term_apply_overrides(term); /* - * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 + * Terminals without am (auto right margin) wrap at at $COLUMNS - 1 * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). * - * This is irritating, most notably because it is impossible to write - * to the very bottom-right of the screen without scrolling. + * Terminals without xenl (eat newline glitch) ignore a newline beyond + * the right edge of the terminal, but tmux doesn't care about this - + * it always uses absolute only moves the cursor with a newline when + * also sending a linefeed. + * + * This is irritating, most notably because it is painful to write to + * the very bottom-right of the screen without scrolling. * * Flag the terminal here and apply some workarounds in other places to * do the best possible. */ - if (!tty_term_flag(term, TTYC_XENL)) - term->flags |= TERM_NOXENL; + if (!tty_term_flag(term, TTYC_AM)) + term->flags |= TERM_NOAM; /* Generate ACS table. If none is present, use nearest ASCII. */ memset(term->acs, 0, sizeof term->acs); diff --git a/tty.c b/tty.c index 77eb90be..14b770e2 100644 --- a/tty.c +++ b/tty.c @@ -564,7 +564,7 @@ tty_putc(struct tty *tty, u_char ch) { const char *acs; - if ((tty->term->flags & TERM_NOXENL) && + if ((tty->term->flags & TERM_NOAM) && ch >= 0x20 && ch != 0x7f && tty->cy == tty->sy - 1 && tty->cx + 1 >= tty->sx) @@ -586,11 +586,11 @@ tty_putc(struct tty *tty, u_char ch) tty->cy++; /* - * On !xenl terminals, force the cursor position to - * where we think it should be after a line wrap - this - * means it works on sensible terminals as well. + * On !am terminals, force the cursor position to where + * we think it should be after a line wrap - this means + * it works on sensible terminals as well. */ - if (tty->term->flags & TERM_NOXENL) + if (tty->term->flags & TERM_NOAM) tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx); } else tty->cx++; @@ -600,7 +600,7 @@ tty_putc(struct tty *tty, u_char ch) void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { - if ((tty->term->flags & TERM_NOXENL) && + if ((tty->term->flags & TERM_NOAM) && tty->cy == tty->sy - 1 && tty->cx + len >= tty->sx) len = tty->sx - tty->cx - 1; @@ -1875,7 +1875,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) ctx->xoff + ctx->ocx + ctx->num > ctx->wox + ctx->wsx)) { if (!ctx->wrapped || !tty_full_width(tty, ctx) || - (tty->term->flags & TERM_NOXENL) || + (tty->term->flags & TERM_NOAM) || ctx->xoff + ctx->ocx != 0 || ctx->yoff + ctx->ocy != tty->cy + 1 || tty->cx < tty->sx || @@ -1931,7 +1931,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, const struct grid_cell *gcp; /* Skip last character if terminal is stupid. */ - if ((tty->term->flags & TERM_NOXENL) && + if ((tty->term->flags & TERM_NOAM) && tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1) return; @@ -2087,7 +2087,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, { if (!ctx->wrapped || !tty_full_width(tty, ctx) || - (tty->term->flags & TERM_NOXENL) || + (tty->term->flags & TERM_NOAM) || ctx->xoff + cx != 0 || ctx->yoff + cy != tty->cy + 1 || tty->cx < tty->sx || From 03b2998abe7712324a1a2ca254167dcc1ce28e4d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2020 09:35:41 +0000 Subject: [PATCH 0500/1006] Do not take the address of a potentially unaligned member. --- grid.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 0c837748..5ea5bf62 100644 --- a/grid.c +++ b/grid.c @@ -114,6 +114,7 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, { struct grid_extd_entry *gee; int flags = (gc->flags & ~GRID_FLAG_CLEARED); + utf8_char uc; if (~gce->flags & GRID_FLAG_EXTENDED) grid_get_extended_cell(gl, gce, flags); @@ -121,8 +122,10 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, fatalx("offset too big"); gl->flags |= GRID_LINE_EXTENDED; + utf8_from_data(&gc->data, &uc); + gee = &gl->extddata[gce->offset]; - utf8_from_data(&gc->data, &gee->data); + gee->data = uc; gee->attr = gc->attr; gee->flags = flags; gee->fg = gc->fg; From d919fa1ed0ea3b167ffc811abba26a2dbcd20631 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2020 11:20:51 +0000 Subject: [PATCH 0501/1006] Change how panes are resized so that the code is clearer and if the pane is resized multiple times during one event loop, it is forced to resize at the end. Also don't zoom/unzoom in switch-client if the pane hasn't changed. GitHub issue 2260. --- cmd-switch-client.c | 2 +- server-client.c | 122 ++++++++++++++++++++------------------------ tmux.h | 6 +-- window.c | 26 ++++++++-- 4 files changed, 79 insertions(+), 77 deletions(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index d062b946..fc7f9d75 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -116,7 +116,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) } else { if (cmdq_get_client(item) == NULL) return (CMD_RETURN_NORMAL); - if (wl != NULL && wp != NULL) { + if (wl != NULL && wp != NULL && wp != wl->window->active) { w = wl->window; if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); diff --git a/server-client.c b/server-client.c index 1a02a240..89275caf 100644 --- a/server-client.c +++ b/server-client.c @@ -45,7 +45,6 @@ static void server_client_check_redraw(struct client *); static void server_client_set_title(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); -static void server_client_resize_event(int, short, void *); static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch_command(struct client *, struct imsg *); @@ -1403,50 +1402,14 @@ server_client_check_window_resize(struct window *w) resize_window(w, w->new_sx, w->new_sy, w->new_xpixel, w->new_ypixel); } -/* Check if we need to force a resize. */ -static int -server_client_resize_force(struct window_pane *wp) -{ - struct timeval tv = { .tv_usec = 100000 }; - - /* - * If we are resizing to the same size as when we entered the loop - * (that is, to the same size the application currently thinks it is), - * tmux may have gone through several resizes internally and thrown - * away parts of the screen. So we need the application to actually - * redraw even though its final size has not changed. - */ - - if (wp->flags & PANE_RESIZEFORCE) { - wp->flags &= ~PANE_RESIZEFORCE; - return (0); - } - - if (wp->sx != wp->osx || - wp->sy != wp->osy || - wp->sx <= 1 || - wp->sy <= 1) - return (0); - - log_debug("%s: %%%u forcing resize", __func__, wp->id); - window_pane_send_resize(wp, -1); - - evtimer_add(&wp->resize_timer, &tv); - wp->flags |= PANE_RESIZEFORCE; - return (1); -} - -/* Resize a pane. */ +/* Resize timer event. */ static void -server_client_resize_pane(struct window_pane *wp) +server_client_resize_timer(__unused int fd, __unused short events, void *data) { - log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); - window_pane_send_resize(wp, 0); + struct window_pane *wp = data; - wp->flags &= ~PANE_RESIZE; - - wp->osx = wp->sx; - wp->osy = wp->sy; + log_debug("%s: %%%u resize timer expired", __func__, wp->id); + evtimer_del(&wp->resize_timer); } /* Start the resize timer. */ @@ -1455,49 +1418,74 @@ server_client_start_resize_timer(struct window_pane *wp) { struct timeval tv = { .tv_usec = 250000 }; - if (!evtimer_pending(&wp->resize_timer, NULL)) - evtimer_add(&wp->resize_timer, &tv); + log_debug("%s: %%%u resize timer started", __func__, wp->id); + evtimer_add(&wp->resize_timer, &tv); } -/* Resize timer event. */ +/* Force timer event. */ static void -server_client_resize_event(__unused int fd, __unused short events, void *data) +server_client_force_timer(__unused int fd, __unused short events, void *data) { struct window_pane *wp = data; - evtimer_del(&wp->resize_timer); + log_debug("%s: %%%u force timer expired", __func__, wp->id); + evtimer_del(&wp->force_timer); + wp->flags |= PANE_RESIZENOW; +} - if (~wp->flags & PANE_RESIZE) - return; - log_debug("%s: %%%u timer fired (was%s resized)", __func__, wp->id, - (wp->flags & PANE_RESIZED) ? "" : " not"); +/* Start the force timer. */ +static void +server_client_start_force_timer(struct window_pane *wp) +{ + struct timeval tv = { .tv_usec = 10000 }; - if (wp->base.saved_grid == NULL && (wp->flags & PANE_RESIZED)) { - log_debug("%s: %%%u deferring timer", __func__, wp->id); - server_client_start_resize_timer(wp); - } else if (!server_client_resize_force(wp)) { - log_debug("%s: %%%u resizing pane", __func__, wp->id); - server_client_resize_pane(wp); - } - wp->flags &= ~PANE_RESIZED; + log_debug("%s: %%%u force timer started", __func__, wp->id); + evtimer_add(&wp->force_timer, &tv); } /* Check if pane should be resized. */ static void server_client_check_pane_resize(struct window_pane *wp) { + if (!event_initialized(&wp->resize_timer)) + evtimer_set(&wp->resize_timer, server_client_resize_timer, wp); + if (!event_initialized(&wp->force_timer)) + evtimer_set(&wp->force_timer, server_client_force_timer, wp); + if (~wp->flags & PANE_RESIZE) return; + log_debug("%s: %%%u needs to be resized", __func__, wp->id); - if (!event_initialized(&wp->resize_timer)) - evtimer_set(&wp->resize_timer, server_client_resize_event, wp); + if (evtimer_pending(&wp->resize_timer, NULL)) { + log_debug("%s: %%%u resize timer is running", __func__, wp->id); + return; + } + server_client_start_resize_timer(wp); - if (!evtimer_pending(&wp->resize_timer, NULL)) { - log_debug("%s: %%%u starting timer", __func__, wp->id); - server_client_resize_pane(wp); - server_client_start_resize_timer(wp); - } else - log_debug("%s: %%%u timer running", __func__, wp->id); + if (~wp->flags & PANE_RESIZEFORCE) { + /* + * The timer is not running and we don't need to force a + * resize, so just resize immediately. + */ + log_debug("%s: resizing %%%u now", __func__, wp->id); + window_pane_send_resize(wp, 0); + wp->flags &= ~PANE_RESIZE; + } else { + /* + * The timer is not running, but we need to force a resize. If + * the force timer has expired, resize to the real size now. + * Otherwise resize to the force size and start the timer. + */ + if (wp->flags & PANE_RESIZENOW) { + log_debug("%s: resizing %%%u after forced resize", __func__, wp->id); + window_pane_send_resize(wp, 0); + wp->flags &= ~(PANE_RESIZE|PANE_RESIZEFORCE|PANE_RESIZENOW); + } else if (!evtimer_pending(&wp->force_timer, NULL)) { + log_debug("%s: forcing resize of %%%u", __func__, wp->id); + window_pane_send_resize(wp, 1); + server_client_start_force_timer(wp); + } + } } /* Check pane buffer size. */ diff --git a/tmux.h b/tmux.h index 0681100f..df63321d 100644 --- a/tmux.h +++ b/tmux.h @@ -928,9 +928,6 @@ struct window_pane { u_int sx; u_int sy; - u_int osx; - u_int osy; - u_int xoff; u_int yoff; @@ -951,7 +948,7 @@ struct window_pane { #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 #define PANE_STYLECHANGED 0x1000 -#define PANE_RESIZED 0x2000 +#define PANE_RESIZENOW 0x2000 int argc; char **argv; @@ -969,6 +966,7 @@ struct window_pane { size_t base_offset; struct event resize_timer; + struct event force_timer; struct input_ctx *ictx; diff --git a/window.c b/window.c index 020ae4bf..523e9913 100644 --- a/window.c +++ b/window.c @@ -436,14 +436,21 @@ window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) } void -window_pane_send_resize(struct window_pane *wp, int yadjust) +window_pane_send_resize(struct window_pane *wp, int force) { struct window *w = wp->window; struct winsize ws; - u_int sy = wp->sy + yadjust; + u_int sy; if (wp->fd == -1) return; + + if (!force) + sy = wp->sy; + else if (wp->sy <= 1) + sy = wp->sy + 1; + else + sy = wp->sy - 1; log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, sy); memset(&ws, 0, sizeof ws); @@ -877,8 +884,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->xoff = 0; wp->yoff = 0; - wp->sx = wp->osx = sx; - wp->sy = wp->osx = sy; + wp->sx = sx; + wp->sy = sy; wp->pipe_fd = -1; wp->pipe_event = NULL; @@ -918,6 +925,8 @@ window_pane_destroy(struct window_pane *wp) if (event_initialized(&wp->resize_timer)) event_del(&wp->resize_timer); + if (event_initialized(&wp->force_timer)) + event_del(&wp->force_timer); RB_REMOVE(window_pane_tree, &all_window_panes, wp); @@ -998,7 +1007,14 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); - wp->flags |= (PANE_RESIZE|PANE_RESIZED); + + /* + * If the pane has already been resized, set the force flag and make + * the application resize twice to force it to redraw. + */ + if (wp->flags & PANE_RESIZE) + wp->flags |= PANE_RESIZEFORCE; + wp->flags |= PANE_RESIZE; } void From 0d8ba2e57ff88582bf1ffeddd523e113f0138001 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jun 2020 14:56:55 +0100 Subject: [PATCH 0502/1006] Add to CHANGES. --- CHANGES | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGES b/CHANGES index 7ee50367..ec6473f1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,21 @@ CHANGES FROM 3.1b TO 3.2 +* Add support for pausing a pane when the output buffered for a control mode + client gets too far behind. The pause-after flag with a time is set on the + pane with refresh-client -f and a paused pane may be resumed with + refresh-client -A. + +* Allow strings in configuration files to span multiple lines - newlines and + any leading whitespace are removed, as well as any following comments that + couldn't be part of a format. This allows long formats or other strings to be + annotated and indented. + +* Instead of using a custom parse function to process {} in configuration + files, treat as a set of statements the same as outside {} and convert back + to a string as the last step. This means the rules are consistent inside and + outside {}, %if and friends work at the right time, and the final result + isn't littered with unnecessary newlines. + * Add support for extended keys - both xterm(1)'s CSI 27 ~ sequence and the libtickit CSI u sequence are accepted; only the latter is output. tmux will only attempt to use these if the extended-keys option is on and it can detect From a4a3d8959890395b040ff49a12f72abc90a5da74 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 6 Jun 2020 12:38:32 +0000 Subject: [PATCH 0503/1006] Use bitshifts instead of a union for encoding UTF-8 into 32 bits, which is more friendly to GCC3. Reported by and ok aoyama@. --- utf8.c | 91 +++++++++++++++++++++------------------------------------- 1 file changed, 33 insertions(+), 58 deletions(-) diff --git a/utf8.c b/utf8.c index c33b6690..d88415f7 100644 --- a/utf8.c +++ b/utf8.c @@ -52,32 +52,11 @@ static struct utf8_item *utf8_list; static u_int utf8_list_size; static u_int utf8_list_used; -union utf8_map { - utf8_char uc; - struct { - u_char flags; - u_char data[3]; - }; -} __packed; +#define UTF8_GET_SIZE(uc) (((uc) >> 24) & 0x1f) +#define UTF8_GET_WIDTH(flags) (((uc) >> 29) - 1) -#define UTF8_GET_SIZE(flags) ((flags) & 0x1f) -#define UTF8_GET_WIDTH(flags) (((flags) >> 5) - 1) - -#define UTF8_SET_SIZE(size) (size) -#define UTF8_SET_WIDTH(width) ((width + 1) << 5) - -static const union utf8_map utf8_space0 = { - .flags = UTF8_SET_WIDTH(0)|UTF8_SET_SIZE(0), - .data = "" -}; -static const union utf8_map utf8_space1 = { - .flags = UTF8_SET_WIDTH(1)|UTF8_SET_SIZE(1), - .data = " " -}; -static const union utf8_map utf8_space2 = { - .flags = UTF8_SET_WIDTH(2)|UTF8_SET_SIZE(2), - .data = " " -}; +#define UTF8_SET_SIZE(size) (((utf8_char)(size)) << 24) +#define UTF8_SET_WIDTH(width) ((((utf8_char)(width)) + 1) << 29) /* Get a UTF-8 item by offset. */ static struct utf8_item * @@ -139,34 +118,31 @@ utf8_put_item(const char *data, size_t size, u_int *offset) enum utf8_state utf8_from_data(const struct utf8_data *ud, utf8_char *uc) { - union utf8_map m = { .uc = 0 }; - u_int offset; + u_int offset; if (ud->width > 2) fatalx("invalid UTF-8 width"); if (ud->size > UTF8_SIZE) goto fail; - m.flags = UTF8_SET_SIZE(ud->size)|UTF8_SET_WIDTH(ud->width); - if (ud->size <= 3) - memcpy(m.data, ud->data, ud->size); - else { - if (utf8_put_item(ud->data, ud->size, &offset) != 0) - goto fail; - m.data[0] = (offset & 0xff); - m.data[1] = (offset >> 8) & 0xff; - m.data[2] = (offset >> 16); - } - *uc = htonl(m.uc); + if (ud->size <= 3) { + offset = (((utf8_char)ud->data[2] << 16)| + ((utf8_char)ud->data[1] << 8)| + ((utf8_char)ud->data[0])); + } else if (utf8_put_item(ud->data, ud->size, &offset) != 0) + goto fail; + *uc = UTF8_SET_SIZE(ud->size)|UTF8_SET_WIDTH(ud->width)|offset; + log_debug("%s: (%d %d %.*s) -> %08x", __func__, ud->width, ud->size, + (int)ud->size, ud->data, *uc); return (UTF8_DONE); fail: if (ud->width == 0) - *uc = htonl(utf8_space0.uc); + *uc = UTF8_SET_SIZE(0)|UTF8_SET_WIDTH(0); else if (ud->width == 1) - *uc = htonl(utf8_space1.uc); + *uc = UTF8_SET_SIZE(1)|UTF8_SET_WIDTH(1)|0x20; else - *uc = htonl(utf8_space2.uc); + *uc = UTF8_SET_SIZE(1)|UTF8_SET_WIDTH(1)|0x2020; return (UTF8_ERROR); } @@ -174,37 +150,36 @@ fail: void utf8_to_data(utf8_char uc, struct utf8_data *ud) { - union utf8_map m = { .uc = ntohl(uc) }; struct utf8_item *ui; u_int offset; memset(ud, 0, sizeof *ud); - ud->size = ud->have = UTF8_GET_SIZE(m.flags); - ud->width = UTF8_GET_WIDTH(m.flags); + ud->size = ud->have = UTF8_GET_SIZE(uc); + ud->width = UTF8_GET_WIDTH(uc); if (ud->size <= 3) { - memcpy(ud->data, m.data, ud->size); - return; + ud->data[2] = (uc >> 16); + ud->data[1] = ((uc >> 8) & 0xff); + ud->data[0] = (uc & 0xff); + } else { + offset = (uc & 0xffffff); + if (offset >= utf8_list_used) + memset(ud->data, ' ', ud->size); + else { + ui = &utf8_list[offset]; + memcpy(ud->data, ui->data, ud->size); + } } - offset = ((u_int)m.data[2] << 16)|((u_int)m.data[1] << 8)|m.data[0]; - if (offset >= utf8_list_used) - memset(ud->data, ' ', ud->size); - else { - ui = &utf8_list[offset]; - memcpy(ud->data, ui->data, ud->size); - } + log_debug("%s: %08x -> (%d %d %.*s)", __func__, uc, ud->width, ud->size, + (int)ud->size, ud->data); } /* Get UTF-8 character from a single ASCII character. */ u_int utf8_build_one(u_char ch) { - union utf8_map m; - - m.flags = UTF8_SET_SIZE(1)|UTF8_SET_WIDTH(1); - m.data[0] = ch; - return (htonl(m.uc)); + return (UTF8_SET_SIZE(1)|UTF8_SET_WIDTH(1)|ch); } /* Set a single character. */ From c60389acbfbacade1e2822ef9099dabaca0ad08e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Jun 2020 08:34:33 +0000 Subject: [PATCH 0504/1006] It is not sensible to store pointers into an array we are going to realloc (duh), use two trees instead. --- utf8.c | 99 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/utf8.c b/utf8.c index d88415f7..00fd5f09 100644 --- a/utf8.c +++ b/utf8.c @@ -28,16 +28,16 @@ #include "tmux.h" struct utf8_item { - u_int offset; - RB_ENTRY(utf8_item) entry; + RB_ENTRY(utf8_item) index_entry; + u_int index; + RB_ENTRY(utf8_item) data_entry; char data[UTF8_SIZE]; u_char size; }; -RB_HEAD(utf8_tree, utf8_item); static int -utf8_cmp(struct utf8_item *ui1, struct utf8_item *ui2) +utf8_data_cmp(struct utf8_item *ui1, struct utf8_item *ui2) { if (ui1->size < ui2->size) return (-1); @@ -45,12 +45,24 @@ utf8_cmp(struct utf8_item *ui1, struct utf8_item *ui2) return (1); return (memcmp(ui1->data, ui2->data, ui1->size)); } -RB_GENERATE_STATIC(utf8_tree, utf8_item, entry, utf8_cmp); -static struct utf8_tree utf8_tree = RB_INITIALIZER(utf8_tree); +RB_HEAD(utf8_data_tree, utf8_item); +RB_GENERATE_STATIC(utf8_data_tree, utf8_item, data_entry, utf8_data_cmp); +static struct utf8_data_tree utf8_data_tree = RB_INITIALIZER(utf8_data_tree); -static struct utf8_item *utf8_list; -static u_int utf8_list_size; -static u_int utf8_list_used; +static int +utf8_index_cmp(struct utf8_item *ui1, struct utf8_item *ui2) +{ + if (ui1->index < ui2->index) + return (-1); + if (ui1->index > ui2->index) + return (1); + return (0); +} +RB_HEAD(utf8_index_tree, utf8_item); +RB_GENERATE_STATIC(utf8_index_tree, utf8_item, index_entry, utf8_index_cmp); +static struct utf8_index_tree utf8_index_tree = RB_INITIALIZER(utf8_index_tree); + +static u_int utf8_next_index; #define UTF8_GET_SIZE(uc) (((uc) >> 24) & 0x1f) #define UTF8_GET_WIDTH(flags) (((uc) >> 29) - 1) @@ -58,59 +70,56 @@ static u_int utf8_list_used; #define UTF8_SET_SIZE(size) (((utf8_char)(size)) << 24) #define UTF8_SET_WIDTH(width) ((((utf8_char)(width)) + 1) << 29) -/* Get a UTF-8 item by offset. */ +/* Get a UTF-8 item from data. */ static struct utf8_item * -utf8_get_item(const char *data, size_t size) +utf8_item_by_data(const char *data, size_t size) { struct utf8_item ui; memcpy(ui.data, data, size); ui.size = size; - return (RB_FIND(utf8_tree, &utf8_tree, &ui)); + return (RB_FIND(utf8_data_tree, &utf8_data_tree, &ui)); } -/* Expand UTF-8 list. */ -static int -utf8_expand_list(void) +/* Get a UTF-8 item from data. */ +static struct utf8_item * +utf8_item_by_index(u_int index) { - if (utf8_list_size == 0xffffff) - return (-1); - if (utf8_list_size == 0) - utf8_list_size = 256; - else if (utf8_list_size > 0x7fffff) - utf8_list_size = 0xffffff; - else - utf8_list_size *= 2; - utf8_list = xreallocarray(utf8_list, utf8_list_size, sizeof *utf8_list); - return (0); + struct utf8_item ui; + + ui.index = index; + + return (RB_FIND(utf8_index_tree, &utf8_index_tree, &ui)); } /* Add a UTF-8 item. */ static int -utf8_put_item(const char *data, size_t size, u_int *offset) +utf8_put_item(const char *data, size_t size, u_int *index) { struct utf8_item *ui; - ui = utf8_get_item(data, size); + ui = utf8_item_by_data(data, size); if (ui != NULL) { - *offset = ui->offset; - log_debug("%s: have %.*s at %u", __func__, (int)size, data, - *offset); + log_debug("%s: found %.*s = %u", __func__, (int)size, data, + *index); + *index = ui->index; return (0); } - if (utf8_list_used == utf8_list_size && utf8_expand_list() != 0) + if (utf8_next_index == 0xffffff + 1) return (-1); - *offset = utf8_list_used++; - ui = &utf8_list[*offset]; - ui->offset = *offset; + ui = xcalloc(1, sizeof *ui); + ui->index = utf8_next_index++; + RB_INSERT(utf8_index_tree, &utf8_index_tree, ui); + memcpy(ui->data, data, size); ui->size = size; - RB_INSERT(utf8_tree, &utf8_tree, ui); + RB_INSERT(utf8_data_tree, &utf8_data_tree, ui); - log_debug("%s: added %.*s at %u", __func__, (int)size, data, *offset); + log_debug("%s: added %.*s = %u", __func__, (int)size, data, *index); + *index = ui->index; return (0); } @@ -118,7 +127,7 @@ utf8_put_item(const char *data, size_t size, u_int *offset) enum utf8_state utf8_from_data(const struct utf8_data *ud, utf8_char *uc) { - u_int offset; + u_int index; if (ud->width > 2) fatalx("invalid UTF-8 width"); @@ -126,12 +135,12 @@ utf8_from_data(const struct utf8_data *ud, utf8_char *uc) if (ud->size > UTF8_SIZE) goto fail; if (ud->size <= 3) { - offset = (((utf8_char)ud->data[2] << 16)| + index = (((utf8_char)ud->data[2] << 16)| ((utf8_char)ud->data[1] << 8)| ((utf8_char)ud->data[0])); - } else if (utf8_put_item(ud->data, ud->size, &offset) != 0) + } else if (utf8_put_item(ud->data, ud->size, &index) != 0) goto fail; - *uc = UTF8_SET_SIZE(ud->size)|UTF8_SET_WIDTH(ud->width)|offset; + *uc = UTF8_SET_SIZE(ud->size)|UTF8_SET_WIDTH(ud->width)|index; log_debug("%s: (%d %d %.*s) -> %08x", __func__, ud->width, ud->size, (int)ud->size, ud->data, *uc); return (UTF8_DONE); @@ -151,7 +160,7 @@ void utf8_to_data(utf8_char uc, struct utf8_data *ud) { struct utf8_item *ui; - u_int offset; + u_int index; memset(ud, 0, sizeof *ud); ud->size = ud->have = UTF8_GET_SIZE(uc); @@ -162,13 +171,11 @@ utf8_to_data(utf8_char uc, struct utf8_data *ud) ud->data[1] = ((uc >> 8) & 0xff); ud->data[0] = (uc & 0xff); } else { - offset = (uc & 0xffffff); - if (offset >= utf8_list_used) + index = (uc & 0xffffff); + if ((ui = utf8_item_by_index(index)) == NULL) memset(ud->data, ' ', ud->size); - else { - ui = &utf8_list[offset]; + else memcpy(ud->data, ui->data, ud->size); - } } log_debug("%s: %08x -> (%d %d %.*s)", __func__, uc, ud->width, ud->size, From fee585ea1429672922e65a4d3b95a5407143c737 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Jun 2020 10:37:00 +0000 Subject: [PATCH 0505/1006] Include width in error message. --- utf8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utf8.c b/utf8.c index 00fd5f09..e3fe570d 100644 --- a/utf8.c +++ b/utf8.c @@ -130,7 +130,7 @@ utf8_from_data(const struct utf8_data *ud, utf8_char *uc) u_int index; if (ud->width > 2) - fatalx("invalid UTF-8 width"); + fatalx("invalid UTF-8 width: %u", ud->width); if (ud->size > UTF8_SIZE) goto fail; From fddcad6957056a32d8894f8c9c7cd44a9c56610b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 10 Jun 2020 06:23:43 +0000 Subject: [PATCH 0506/1006] When the pause-after flag is set, send an alternative %extended-output form instead of %output with the age of the output. --- control.c | 23 +++++++++++++++++------ tmux.1 | 11 +++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/control.c b/control.c index b9b22567..8ebb615e 100644 --- a/control.c +++ b/control.c @@ -462,8 +462,8 @@ control_flush_all_blocks(struct client *c) /* Append data to buffer. */ static struct evbuffer * -control_append_data(struct control_pane *cp, struct evbuffer *message, - struct window_pane *wp, size_t size) +control_append_data(struct client *c, struct control_pane *cp, uint64_t age, + struct evbuffer *message, struct window_pane *wp, size_t size) { u_char *new_data; size_t new_size; @@ -473,7 +473,12 @@ control_append_data(struct control_pane *cp, struct evbuffer *message, message = evbuffer_new(); if (message == NULL) fatalx("out of memory"); - evbuffer_add_printf(message, "%%output %%%u ", wp->id); + if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { + evbuffer_add_printf(message, + "%%extended-output %%%u %llu : ", wp->id, + (unsigned long long)age); + } else + evbuffer_add_printf(message, "%%output %%%u ", wp->id); } new_data = window_pane_get_new_data(wp, &cp->offset, &new_size); @@ -512,6 +517,7 @@ control_write_pending(struct client *c, struct control_pane *cp, size_t limit) struct evbuffer *message = NULL; size_t used = 0, size; struct control_block *cb, *cb1; + uint64_t age, t = get_timer(); wp = control_window_pane(c, cp->pane); if (wp == NULL) { @@ -525,15 +531,20 @@ control_write_pending(struct client *c, struct control_pane *cp, size_t limit) while (used != limit && !TAILQ_EMPTY(&cp->blocks)) { cb = TAILQ_FIRST(&cp->blocks); - log_debug("%s: %s: output block %zu for %%%u (used %zu/%zu)", - __func__, c->name, cb->size, cp->pane, used, limit); + if (cb->t < t) + age = t - cb->t; + 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); size = cb->size; if (size > limit - used) size = limit - used; used += size; - message = control_append_data(cp, message, wp, size); + message = control_append_data(c, cp, age, message, wp, size); cb->size -= size; if (cb->size == 0) { diff --git a/tmux.1 b/tmux.1 index 4489c9ae..2200f193 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5910,6 +5910,17 @@ or an error occurred. If present, .Ar reason describes why the client exited. +.It Ic %extended-output Ar pane-id Ar age Ar ... : Ar value +New form of +.Ic %output +sent when the +.Ar pause-after +flag is set. +.Ar age +is the time in milliseconds for which tmux had buffered the output before it was sent. +Any subsequent arguments up until a single +.Ql \&: +are for future use and should be ignored. .It Ic %layout-change Ar window-id Ar window-layout Ar window-visible-layout Ar window-flags The layout of a window with ID .Ar window-id From 23d79cfda87f822c7440fd572ce5fc440c079ac2 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 10 Jun 2020 07:27:10 +0000 Subject: [PATCH 0507/1006] Instead of a buffer size limit on each pane, set a limit of 300 seconds of data for each client in control mode. --- client.c | 45 ++++++++++++++++++++++++++++---------- control.c | 57 +++++++++++++++++++++++++++++++++++-------------- server-client.c | 22 ++++++++++++------- tmux.1 | 17 +++++++++------ tmux.h | 1 + 5 files changed, 100 insertions(+), 42 deletions(-) diff --git a/client.c b/client.c index be9dccbf..c97df491 100644 --- a/client.c +++ b/client.c @@ -45,11 +45,13 @@ static enum { CLIENT_EXIT_LOST_SERVER, CLIENT_EXIT_EXITED, CLIENT_EXIT_SERVER_EXITED, + CLIENT_EXIT_MESSAGE_PROVIDED } client_exitreason = CLIENT_EXIT_NONE; static int client_exitflag; static int client_exitval; static enum msgtype client_exittype; static const char *client_exitsession; +static char *client_exitmessage; static const char *client_execshell; static const char *client_execcmd; static int client_attached; @@ -207,6 +209,8 @@ client_exit_message(void) return ("exited"); case CLIENT_EXIT_SERVER_EXITED: return ("server exited"); + case CLIENT_EXIT_MESSAGE_PROVIDED: + return (client_exitmessage); } return ("unknown reason"); } @@ -791,13 +795,38 @@ client_dispatch(struct imsg *imsg, __unused void *arg) client_dispatch_wait(imsg); } +/* Process an exit message. */ +static void +client_dispatch_exit_message(char *data, size_t datalen) +{ + int retval; + + if (datalen < sizeof retval && datalen != 0) + fatalx("bad MSG_EXIT size"); + + if (datalen >= sizeof retval) { + memcpy(&retval, data, sizeof retval); + client_exitval = retval; + } + + if (datalen > sizeof retval) { + datalen -= sizeof retval; + data += sizeof retval; + + client_exitmessage = xmalloc(datalen); + memcpy(client_exitmessage, data, datalen); + client_exitmessage[datalen - 1] = '\0'; + + client_exitreason = CLIENT_EXIT_MESSAGE_PROVIDED; + } +} + /* Dispatch imsgs when in wait state (before MSG_READY). */ static void client_dispatch_wait(struct imsg *imsg) { char *data; ssize_t datalen; - int retval; static int pledge_applied; /* @@ -820,12 +849,7 @@ client_dispatch_wait(struct imsg *imsg) switch (imsg->hdr.type) { case MSG_EXIT: case MSG_SHUTDOWN: - if (datalen != sizeof retval && datalen != 0) - fatalx("bad MSG_EXIT size"); - if (datalen == sizeof retval) { - memcpy(&retval, data, sizeof retval); - client_exitval = retval; - } + client_dispatch_exit_message(data, datalen); client_exitflag = 1; client_exit(); break; @@ -916,11 +940,10 @@ client_dispatch_attached(struct imsg *imsg) proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case MSG_EXIT: - if (datalen != 0 && datalen != sizeof (int)) - fatalx("bad MSG_EXIT size"); - + client_dispatch_exit_message(data, datalen); + if (client_exitreason == CLIENT_EXIT_NONE) + client_exitreason = CLIENT_EXIT_EXITED; proc_send(client_peer, MSG_EXITING, -1, NULL, 0); - client_exitreason = CLIENT_EXIT_EXITED; break; case MSG_EXITED: if (datalen != 0) diff --git a/control.c b/control.c index 8ebb615e..140849e1 100644 --- a/control.c +++ b/control.c @@ -89,13 +89,16 @@ struct control_state { struct bufferevent *write_event; }; -/* Low watermark. */ +/* Low and high watermarks. */ #define CONTROL_BUFFER_LOW 512 #define CONTROL_BUFFER_HIGH 8192 /* Minimum to write to each client. */ #define CONTROL_WRITE_MINIMUM 32 +/* Maximum age for clients that are not using pause mode. */ +#define CONTROL_MAXIMUM_AGE 300000 + /* Flags to ignore client. */ #define CONTROL_IGNORE_FLAGS \ (CLIENT_CONTROL_NOOUTPUT| \ @@ -306,6 +309,41 @@ control_write(struct client *c, const char *fmt, ...) va_end(ap); } +/* Check age for this pane. */ +static int +control_check_age(struct client *c, struct window_pane *wp, + struct control_pane *cp) +{ + struct control_block *cb; + uint64_t t, age; + + cb = TAILQ_FIRST(&cp->blocks); + if (cb == NULL) + return (0); + t = get_timer(); + if (cb->t >= t) + return (0); + + age = t - cb->t; + log_debug("%s: %s: %%%u is %llu behind", __func__, c->name, wp->id, + (unsigned long long)age); + + if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { + if (age < c->pause_age) + return (0); + cp->flags |= CONTROL_PANE_PAUSED; + control_discard_pane(c, cp); + control_write(c, "%%pause %%%u", wp->id); + } else { + if (age < CONTROL_MAXIMUM_AGE) + return (0); + c->exit_message = xstrdup("too far behind"); + c->flags |= CLIENT_EXIT; + control_discard(c); + } + return (1); +} + /* Write output from a pane. */ void control_write_output(struct client *c, struct window_pane *wp) @@ -314,7 +352,6 @@ control_write_output(struct client *c, struct window_pane *wp) struct control_pane *cp; struct control_block *cb; size_t new_size; - uint64_t t; if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) return; @@ -328,20 +365,8 @@ control_write_output(struct client *c, struct window_pane *wp) cp = control_add_pane(c, wp); if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED)) goto ignore; - if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { - cb = TAILQ_FIRST(&cp->blocks); - if (cb != NULL) { - t = get_timer(); - log_debug("%s: %s: %%%u is %lld behind", __func__, - c->name, wp->id, (long long)t - cb->t); - if (cb->t < t - c->pause_age) { - cp->flags |= CONTROL_PANE_PAUSED; - control_discard_pane(c, cp); - control_write(c, "%%pause %%%u", wp->id); - return; - } - } - } + if (control_check_age(c, wp, cp)) + return; window_pane_get_new_data(wp, &cp->queued, &new_size); if (new_size == 0) diff --git a/server-client.c b/server-client.c index 89275caf..9e7adf55 100644 --- a/server-client.c +++ b/server-client.c @@ -57,9 +57,6 @@ static void server_client_dispatch_read_data(struct client *, static void server_client_dispatch_read_done(struct client *, struct imsg *); -/* Maximum data allowed to be held for a pane for a control client. */ -#define SERVER_CLIENT_PANE_LIMIT 16777216 - /* Compare client windows. */ static int server_client_window_cmp(struct client_window *cw1, @@ -1528,10 +1525,6 @@ server_client_check_pane_buffer(struct window_pane *wp) log_debug("%s: %s has %zu bytes used and %zu left for %%%u", __func__, c->name, wpo->used - wp->base_offset, new_size, wp->id); - if (new_size > SERVER_CLIENT_PANE_LIMIT) { - control_discard(c); - c->flags |= CLIENT_EXIT; - } if (wpo->used < minimum) minimum = wpo->used; } @@ -1766,6 +1759,8 @@ server_client_check_exit(struct client *c) { struct client_file *cf; const char *name = c->exit_session; + char *data; + size_t size, msize; if (c->flags & (CLIENT_DEAD|CLIENT_EXITED)) return; @@ -1788,7 +1783,17 @@ server_client_check_exit(struct client *c) switch (c->exit_type) { case CLIENT_EXIT_RETURN: - proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); + if (c->exit_message != NULL) { + msize = strlen(c->exit_message) + 1; + size = (sizeof c->retval) + msize; + } else + size = (sizeof c->retval); + data = xmalloc(size); + memcpy(data, &c->retval, sizeof c->retval); + if (c->exit_message != NULL) + memcpy(data + sizeof c->retval, c->exit_message, msize); + proc_send(c->peer, MSG_EXIT, -1, data, size); + free(data); break; case CLIENT_EXIT_SHUTDOWN: proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); @@ -1798,6 +1803,7 @@ server_client_check_exit(struct client *c) break; } free(c->exit_session); + free(c->exit_message); } /* Redraw timer callback. */ diff --git a/tmux.1 b/tmux.1 index 2200f193..1a3cde5d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5727,29 +5727,32 @@ When a client detaches, it prints a message. This may be one of: .Bl -tag -width Ds -.It [detached (from session ...)] +.It detached (from session ...) The client was detached normally. -.It [detached and SIGHUP] +.It detached and SIGHUP The client was detached and its parent sent the .Dv SIGHUP signal (for example with .Ic detach-client .Fl P ) . -.It [lost tty] +.It lost tty The client's .Xr tty 4 or .Xr pty 4 was unexpectedly destroyed. -.It [terminated] +.It terminated The client was killed with .Dv SIGTERM . -.It [exited] +.It too far behind +The client is in control mode and became unable to keep up with the data from +.Nm . +.It exited The server exited when it had no sessions. -.It [server exited] +.It server exited The server exited when it received .Dv SIGTERM . -.It [server exited unexpectedly] +.It server exited unexpectedly The server crashed or otherwise exited without telling the client the reason. .El .Sh TERMINFO EXTENSIONS diff --git a/tmux.h b/tmux.h index df63321d..ee59b4b3 100644 --- a/tmux.h +++ b/tmux.h @@ -1668,6 +1668,7 @@ struct client { } exit_type; enum msgtype exit_msgtype; char *exit_session; + char *exit_message; struct key_table *keytable; From 50ee41423f6f7b4c0bc83ecf9468a37fc8bf9e99 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Jun 2020 09:55:47 +0000 Subject: [PATCH 0508/1006] Add a -A option to pause a pane manually. --- cmd-refresh-client.c | 2 ++ control.c | 14 ++++++++++++++ tmux.1 | 11 ++++++++--- tmux.h | 1 + 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index bbe0c736..f7b6269b 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -68,6 +68,8 @@ cmd_refresh_client_update_offset(struct client *tc, const char *value) control_set_pane_off(tc, wp); else if (strcmp(colon, "continue") == 0) control_continue_pane(tc, wp); + else if (strcmp(colon, "pause") == 0) + control_pause_pane(tc, wp); out: free(copy); diff --git a/control.c b/control.c index 140849e1..8ff7736f 100644 --- a/control.c +++ b/control.c @@ -265,6 +265,20 @@ control_continue_pane(struct client *c, struct window_pane *wp) } } +/* Pause a pane. */ +void +control_pause_pane(struct client *c, struct window_pane *wp) +{ + struct control_pane *cp; + + cp = control_add_pane(c, wp); + if (~cp->flags & CONTROL_PANE_PAUSED) { + cp->flags |= CONTROL_PANE_PAUSED; + control_discard_pane(c, cp); + control_write(c, "%%pause %%%u", wp->id); + } +} + /* Write a line. */ static void control_vwrite(struct client *c, const char *fmt, va_list ap) diff --git a/tmux.1 b/tmux.1 index 1a3cde5d..44806c34 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1304,9 +1304,10 @@ The argument is a pane ID (with leading .Ql % ) , a colon, then one of .Ql on , -.Ql off +.Ql off , +.Ql continue or -.Ql continue . +.Ql pause . If .Ql off , .Nm @@ -1315,9 +1316,13 @@ the pane off, will stop reading from the pane. If .Ql continue , .Nm -will return to sending output to a paused pane (see the +will return to sending output to the pane if it was paused (manually or with the .Ar pause-after flag). +If +.Ql pause , +.Nm +will pause the pane. .Fl A may be given multiple times for different panes. .Pp diff --git a/tmux.h b/tmux.h index ee59b4b3..c6c84e86 100644 --- a/tmux.h +++ b/tmux.h @@ -2846,6 +2846,7 @@ void control_stop(struct client *); void control_set_pane_on(struct client *, struct window_pane *); void control_set_pane_off(struct client *, struct window_pane *); void control_continue_pane(struct client *, struct window_pane *); +void control_pause_pane(struct client *, struct window_pane *); struct window_pane_offset *control_pane_offset(struct client *, struct window_pane *, int *); void control_reset_offsets(struct client *); From cf13d1e1106b26ea93c0f159ddaee03f38b2e2fa Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Jun 2020 10:56:19 +0000 Subject: [PATCH 0509/1006] Fix a crash when completing sessions, from Anindya Mukherjee. --- status.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/status.c b/status.c index d143b658..7313d2a0 100644 --- a/status.c +++ b/status.c @@ -1441,6 +1441,8 @@ status_prompt_complete_prefix(char **list, u_int size) u_int i; size_t j; + if (list == NULL || size == 0) + return (NULL); out = xstrdup(list[0]); for (i = 1; i < size; i++) { j = strlen(list[i]); @@ -1649,13 +1651,22 @@ status_prompt_complete_session(char ***list, u_int *size, const char *s, char flag) { struct session *loop; - char *out, *tmp; + char *out, *tmp, n[11]; RB_FOREACH(loop, sessions, &sessions) { - if (*s != '\0' && strncmp(loop->name, s, strlen(s)) != 0) - continue; - *list = xreallocarray(*list, (*size) + 2, sizeof **list); - xasprintf(&(*list)[(*size)++], "%s:", loop->name); + if (*s == '\0' || strncmp(loop->name, s, strlen(s)) == 0) { + *list = xreallocarray(*list, (*size) + 2, + sizeof **list); + xasprintf(&(*list)[(*size)++], "%s:", loop->name); + } else if (*s == '$') { + xsnprintf(n, sizeof n, "%u", loop->id); + if (s[1] == '\0' || + strncmp(n, s + 1, strlen(s) - 1) == 0) { + *list = xreallocarray(*list, (*size) + 2, + sizeof **list); + xasprintf(&(*list)[(*size)++], "$%s:", n); + } + } } out = status_prompt_complete_prefix(*list, *size); if (out != NULL && flag != '\0') { @@ -1670,7 +1681,7 @@ status_prompt_complete_session(char ***list, u_int *size, const char *s, static char * status_prompt_complete(struct client *c, const char *word, u_int offset) { - struct session *session; + struct session *session; const char *s, *colon; char **list = NULL, *copy = NULL, *out = NULL; char flag = '\0'; From 63c2ed14833bc543254b8dbd7d244e143a89b736 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Jun 2020 19:43:34 +0000 Subject: [PATCH 0510/1006] Add some formats for search in copy mode (search_present, search_match). GitHub issue 2268. --- format.c | 7 ++++++ tmux.1 | 2 ++ tmux.h | 1 + window-copy.c | 61 +++++++++++++++++++++++++++++++++++---------------- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/format.c b/format.c index 28c423ef..b657b81c 100644 --- a/format.c +++ b/format.c @@ -1156,6 +1156,13 @@ format_merge(struct format_tree *ft, struct format_tree *from) } } +/* Get format pane. */ +struct window_pane * +format_get_pane(struct format_tree *ft) +{ + return (ft->wp); +} + /* Add item bits to tree. */ static void format_create_add_item(struct format_tree *ft, struct cmdq_item *item) diff --git a/tmux.1 b/tmux.1 index 44806c34..d3e0d0b3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4749,6 +4749,8 @@ The following variables are available, where appropriate: .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" +.It Li "search_present" Ta "" Ta "1 if search started in copy mode" +.It Li "search_match" Ta "" Ta "Search match if any" .It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode" .It Li "selection_end_x" Ta "" Ta "X position of the end of the selection" .It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection" diff --git a/tmux.h b/tmux.h index c6c84e86..092e0b0e 100644 --- a/tmux.h +++ b/tmux.h @@ -1922,6 +1922,7 @@ struct format_tree *format_create(struct client *, struct cmdq_item *, int, int); void format_free(struct format_tree *); void format_merge(struct format_tree *, struct format_tree *); +struct window_pane *format_get_pane(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); void format_add_tv(struct format_tree *, const char *, diff --git a/window-copy.c b/window-copy.c index 889306d2..f30d241e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -26,6 +26,8 @@ #include "tmux.h" +struct window_copy_mode_data; + static const char *window_copy_key_table(struct window_mode_entry *); static void window_copy_command(struct window_mode_entry *, struct client *, struct session *, struct winlink *, struct args *, @@ -42,7 +44,6 @@ static void window_copy_pageup1(struct window_mode_entry *, int); static int window_copy_pagedown(struct window_mode_entry *, int, int); static void window_copy_next_paragraph(struct window_mode_entry *); static void window_copy_previous_paragraph(struct window_mode_entry *); - static void window_copy_redraw_selection(struct window_mode_entry *, u_int); static void window_copy_redraw_lines(struct window_mode_entry *, u_int, u_int); @@ -51,7 +52,7 @@ static void window_copy_write_line(struct window_mode_entry *, struct screen_write_ctx *, u_int); static void window_copy_write_lines(struct window_mode_entry *, struct screen_write_ctx *, u_int, u_int); - +static char *window_copy_match_at_cursor(struct window_copy_mode_data *); static void window_copy_scroll_to(struct window_mode_entry *, u_int, u_int, int); static int window_copy_search_compare(struct grid *, u_int, u_int, @@ -60,13 +61,13 @@ static int window_copy_search_lr(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); static int window_copy_search_rl(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); -static int window_copy_last_regex(struct grid *gd, u_int py, u_int first, - u_int last, u_int len, u_int *ppx, u_int *psx, - const char *buf, const regex_t *preg, int eflags); +static int window_copy_last_regex(struct grid *, u_int, u_int, u_int, + u_int, u_int *, u_int *, const char *, const regex_t *, + int); static char *window_copy_stringify(struct grid *, u_int, u_int, u_int, char *, u_int *); -static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, u_int *, - const char *str); +static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, + u_int *, const char *); static int window_copy_search_marks(struct window_mode_entry *, struct screen *, int, int); static void window_copy_clear_marks(struct window_mode_entry *); @@ -702,12 +703,40 @@ window_copy_get_line(struct window_pane *wp, u_int y) return (format_grid_line(gd, gd->hsize + y)); } +static char * +window_copy_cursor_word_cb(struct format_tree *ft) +{ + struct window_pane *wp = format_get_pane(ft); + struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct window_copy_mode_data *data = wme->data; + + return (window_copy_get_word(wp, data->cx, data->cy)); +} + +static char * +window_copy_cursor_line_cb(struct format_tree *ft) +{ + struct window_pane *wp = format_get_pane(ft); + struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct window_copy_mode_data *data = wme->data; + + return (window_copy_get_line(wp, data->cy)); +} + +static char * +window_copy_search_match_cb(struct format_tree *ft) +{ + struct window_pane *wp = format_get_pane(ft); + struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct window_copy_mode_data *data = wme->data; + + return (window_copy_match_at_cursor(data)); +} + static void window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) { struct window_copy_mode_data *data = wme->data; - struct grid *gd = data->screen.grid; - char *s; format_add(ft, "scroll_position", "%d", data->oy); format_add(ft, "rectangle_toggle", "%d", data->rectflag); @@ -726,17 +755,11 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) } else format_add(ft, "selection_active", "%d", 0); - s = format_grid_word(gd, data->cx, gd->hsize + data->cy); - if (s != NULL) { - format_add(ft, "copy_cursor_word", "%s", s); - free(s); - } + format_add(ft, "search_present", "%d", data->searchmark != NULL); + format_add_cb(ft, "search_match", window_copy_search_match_cb); - s = format_grid_line(gd, gd->hsize + data->cy); - if (s != NULL) { - format_add(ft, "copy_cursor_line", "%s", s); - free(s); - } + format_add_cb(ft, "copy_cursor_word", window_copy_cursor_word_cb); + format_add_cb(ft, "copy_cursor_line", window_copy_cursor_line_cb); } static void From cf63465eb08a658d1e7ec5fd1e1f70869117d3e9 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 12 Jun 2020 07:10:43 +0000 Subject: [PATCH 0511/1006] Fix quoting with newlines and single quotes. --- arguments.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/arguments.c b/arguments.c index 0495aa0e..dd92a952 100644 --- a/arguments.c +++ b/arguments.c @@ -212,32 +212,35 @@ args_print(struct args *args) char * args_escape(const char *s) { - static const char quoted[] = " #\"';${}"; + static const char dquoted[] = " #';${}"; + static const char squoted[] = " \""; char *escaped, *result; - int flags; + int flags, quotes = 0; if (*s == '\0') { xasprintf(&result, "''"); return (result); } + if (s[strcspn(s, dquoted)] != '\0') + quotes = '"'; + else if (s[strcspn(s, squoted)] != '\0') + quotes = '\''; + if (s[0] != ' ' && - (strchr(quoted, s[0]) != NULL || s[0] == '~') && - s[1] == '\0') { + s[1] == '\0' && + (quotes != 0 || s[0] == '~')) { xasprintf(&escaped, "\\%c", s[0]); return (escaped); } - if (strchr(s, ' ') != NULL && strchr(s, '\'') == NULL) { - xasprintf(&escaped, "'%s'", s); - return (escaped); - } - flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; - if (s[strcspn(s, quoted)] != '\0') + if (quotes == '"') flags |= VIS_DQ; utf8_stravis(&escaped, s, flags); - if (flags & VIS_DQ) { + if (quotes == '\'') + xasprintf(&result, "'%s'", escaped); + else if (quotes == '"') { if (*escaped == '~') xasprintf(&result, "\"\\%s\"", escaped); else From 4c3bdc5a3619c2ea7d9b130efeebf12bf08a1fb2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 12 Jun 2020 07:52:38 +0000 Subject: [PATCH 0512/1006] move-pane also defaults to marked pane now, reported by Ben Challenor. --- tmux.1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.1 b/tmux.1 index d3e0d0b3..207fa47c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2643,6 +2643,7 @@ The marked pane is the default target for .Fl s to .Ic join-pane , +.Ic move-pane , .Ic swap-pane and .Ic swap-window . From d8d77691043a5ecd504fb2a82e4e312d947ab19f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 12 Jun 2020 08:35:01 +0000 Subject: [PATCH 0513/1006] Check if a pane needs to be paused when output is written rather than just when it is queued. --- control.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/control.c b/control.c index 8ff7736f..05093d94 100644 --- a/control.c +++ b/control.c @@ -569,6 +569,13 @@ control_write_pending(struct client *c, struct control_pane *cp, size_t limit) } while (used != limit && !TAILQ_EMPTY(&cp->blocks)) { + if (control_check_age(c, wp, cp)) { + if (message != NULL) + evbuffer_free(message); + message = NULL; + break; + } + cb = TAILQ_FIRST(&cp->blocks); if (cb->t < t) age = t - cb->t; From d52ac7d027e40b34e7089addc4f905d03d9c1b16 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 12 Jun 2020 10:31:12 +0000 Subject: [PATCH 0514/1006] Do not wait on shutdown for commands started with run -b. --- cmd-run-shell.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 3792aaae..647f533f 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -56,6 +56,7 @@ struct cmd_run_shell_data { struct session *s; int wp_id; struct event timer; + int flags; }; static void @@ -110,6 +111,8 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'b')) cdata->item = item; + else + cdata->flags |= JOB_NOWAIT; cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s)); cdata->s = s; @@ -144,8 +147,8 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) if (cdata->cmd != NULL) { if (job_run(cdata->cmd, cdata->s, cdata->cwd, NULL, - cmd_run_shell_callback, cmd_run_shell_free, cdata, 0, -1, - -1) == NULL) + cmd_run_shell_callback, cmd_run_shell_free, cdata, + cdata->flags, -1, -1) == NULL) cmd_run_shell_free(cdata); } else { if (cdata->item != NULL) From 1c78155e70a9f72ed6c191807c2b381cf114b91f Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 13 Jun 2020 09:05:53 +0000 Subject: [PATCH 0515/1006] Add -b flags to insert a window before (like the existing -a for after) to break-pane, move-window, new-window. GitHub issue 2261. --- cmd-break-pane.c | 16 ++++++++-------- cmd-move-window.c | 17 +++++++++-------- cmd-new-window.c | 16 ++++++++++------ tmux.1 | 33 ++++++++++++++++++++------------- tmux.h | 2 +- window.c | 12 ++++++++---- 6 files changed, 56 insertions(+), 40 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 6d5041e8..4c436405 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_break_pane_entry = { .name = "break-pane", .alias = "breakp", - .args = { "adPF:n:s:t:", 0, 0 }, - .usage = "[-adP] [-F format] [-n window-name] [-s src-pane] " + .args = { "abdPF:n:s:t:", 0, 0 }, + .usage = "[-abdP] [-F format] [-n window-name] [-s src-pane] " "[-t dst-window]", .source = { 's', CMD_FIND_PANE, 0 }, @@ -58,16 +58,16 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) struct session *dst_s = target->s; struct window_pane *wp = source->wp; struct window *w = wl->window; - char *name, *cause; - int idx = target->idx; + char *name, *cause, *cp; + int idx = target->idx, before; const char *template; - char *cp; - if (args_has(args, 'a')) { + before = args_has(args, 'b'); + if (args_has(args, 'a') || before) { if (target->wl != NULL) - idx = winlink_shuffle_up(dst_s, target->wl); + idx = winlink_shuffle_up(dst_s, target->wl, before); else - idx = winlink_shuffle_up(dst_s, dst_s->curw); + idx = winlink_shuffle_up(dst_s, dst_s->curw, before); if (idx == -1) return (CMD_RETURN_ERROR); } diff --git a/cmd-move-window.c b/cmd-move-window.c index 94b6c950..61128771 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -32,8 +32,8 @@ const struct cmd_entry cmd_move_window_entry = { .name = "move-window", .alias = "movew", - .args = { "adkrs:t:", 0, 0 }, - .usage = "[-dkr] " CMD_SRCDST_WINDOW_USAGE, + .args = { "abdkrs:t:", 0, 0 }, + .usage = "[-abdkr] " CMD_SRCDST_WINDOW_USAGE, .source = { 's', CMD_FIND_WINDOW, 0 }, /* -t is special */ @@ -46,8 +46,8 @@ const struct cmd_entry cmd_link_window_entry = { .name = "link-window", .alias = "linkw", - .args = { "adks:t:", 0, 0 }, - .usage = "[-dk] " CMD_SRCDST_WINDOW_USAGE, + .args = { "abdks:t:", 0, 0 }, + .usage = "[-abdk] " CMD_SRCDST_WINDOW_USAGE, .source = { 's', CMD_FIND_WINDOW, 0 }, /* -t is special */ @@ -67,7 +67,7 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) struct session *dst; struct winlink *wl = source->wl; char *cause; - int idx, kflag, dflag, sflag; + int idx, kflag, dflag, sflag, before; if (args_has(args, 'r')) { if (cmd_find_target(&target, item, tflag, CMD_FIND_SESSION, @@ -90,11 +90,12 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) dflag = args_has(args, 'd'); sflag = args_has(args, 's'); - if (args_has(args, 'a')) { + before = args_has(args, 'b'); + if (args_has(args, 'a') || before) { if (target.wl != NULL) - idx = winlink_shuffle_up(dst, target.wl); + idx = winlink_shuffle_up(dst, target.wl, before); else - idx = winlink_shuffle_up(dst, dst->curw); + idx = winlink_shuffle_up(dst, dst->curw, before); if (idx == -1) return (CMD_RETURN_ERROR); } diff --git a/cmd-new-window.c b/cmd-new-window.c index 722f89b9..0b24474b 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -38,8 +38,8 @@ const struct cmd_entry cmd_new_window_entry = { .name = "new-window", .alias = "neww", - .args = { "ac:de:F:kn:Pt:", 0, -1 }, - .usage = "[-adkP] [-c start-directory] [-e environment] [-F format] " + .args = { "abc:de:F:kn:Pt:", 0, -1 }, + .usage = "[-abdkP] [-c start-directory] [-e environment] [-F format] " "[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX }, @@ -58,16 +58,20 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct winlink *wl = target->wl; - int idx = target->idx; + int idx = target->idx, before; struct winlink *new_wl; char *cause = NULL, *cp; const char *template, *add; struct cmd_find_state fs; struct args_value *value; - if (args_has(args, 'a') && (idx = winlink_shuffle_up(s, wl)) == -1) { - cmdq_error(item, "couldn't get a window index"); - return (CMD_RETURN_ERROR); + before = args_has(args, 'b'); + if (args_has(args, 'a') || before) { + idx = winlink_shuffle_up(s, wl, before); + if (idx == -1) { + cmdq_error(item, "couldn't get a window index"); + return (CMD_RETURN_ERROR); + } } memset(&sc, 0, sizeof sc); diff --git a/tmux.1 b/tmux.1 index 207fa47c..d6fd1c85 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1792,7 +1792,7 @@ from which the layout was originally defined. Commands related to windows and panes are as follows: .Bl -tag -width Ds .It Xo Ic break-pane -.Op Fl adP +.Op Fl abdP .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar src-pane @@ -1804,9 +1804,11 @@ Break off from its containing window to make it the only pane in .Ar dst-window . With -.Fl a , -the window is moved to the next index up (following windows -are moved if necessary). +.Fl a +or +.Fl b , +the window is moved to the next index after or before (existing windows are +moved if necessary). If .Fl d is given, the new window does not become the current window. @@ -2188,7 +2190,7 @@ If no .Ar target-session is specified, select the last window of the current session. .It Xo Ic link-window -.Op Fl adk +.Op Fl abdk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc @@ -2203,9 +2205,12 @@ is specified and no such window exists, the .Ar src-window is linked there. With -.Fl a , -the window is moved to the next index up (following windows -are moved if necessary). +.Fl a +or +.Fl b +the window is moved to the next index after or before +.Ar dst-window +(existing windows are moved if necessary). If .Fl k is given and @@ -2272,7 +2277,7 @@ section. Does the same as .Ic join-pane . .It Xo Ic move-window -.Op Fl ardk +.Op Fl abrdk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc @@ -2290,7 +2295,7 @@ the .Ic base-index option. .It Xo Ic new-window -.Op Fl adkP +.Op Fl abdkP .Op Fl c Ar start-directory .Op Fl e Ar environment .Op Fl F Ar format @@ -2301,10 +2306,12 @@ option. .D1 (alias: Ic neww ) Create a new window. With -.Fl a , -the new window is inserted at the next index up from the specified +.Fl a +or +.Fl b , +the new window is inserted at the next index after or before the specified .Ar target-window , -moving windows up if necessary, +moving windows up if necessary; otherwise .Ar target-window is the new window location. diff --git a/tmux.h b/tmux.h index 092e0b0e..9bc89628 100644 --- a/tmux.h +++ b/tmux.h @@ -2720,7 +2720,7 @@ void window_set_name(struct window *, const char *); void window_add_ref(struct window *, const char *); void window_remove_ref(struct window *, const char *); void winlink_clear_flags(struct winlink *); -int winlink_shuffle_up(struct session *, struct winlink *); +int winlink_shuffle_up(struct session *, struct winlink *, int); int window_pane_start_input(struct window_pane *, struct cmdq_item *, char **); void *window_pane_get_new_data(struct window_pane *, diff --git a/window.c b/window.c index 523e9913..66298495 100644 --- a/window.c +++ b/window.c @@ -1490,13 +1490,16 @@ winlink_clear_flags(struct winlink *wl) /* Shuffle window indexes up. */ int -winlink_shuffle_up(struct session *s, struct winlink *wl) +winlink_shuffle_up(struct session *s, struct winlink *wl, int before) { int idx, last; if (wl == NULL) return (-1); - idx = wl->idx + 1; + if (before) + idx = wl->idx; + else + idx = wl->idx + 1; /* Find the next free index. */ for (last = idx; last < INT_MAX; last++) { @@ -1509,8 +1512,9 @@ winlink_shuffle_up(struct session *s, struct winlink *wl) /* Move everything from last - 1 to idx up a bit. */ for (; last > idx; last--) { wl = winlink_find_by_index(&s->windows, last - 1); - server_link_window(s, wl, s, last, 0, 0, NULL); - server_unlink_window(s, wl); + RB_REMOVE(winlinks, &s->windows, wl); + wl->idx++; + RB_INSERT(winlinks, &s->windows, wl); } return (idx); From afe4ea4250073e482c6ec6accfc539f873df6977 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jun 2020 07:28:57 +0000 Subject: [PATCH 0516/1006] Correctly move to previous line when looking for previous word, from Derry Jing. --- window-copy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/window-copy.c b/window-copy.c index f30d241e..7b7ab72c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -4535,14 +4535,14 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, separators)) break; } else { - if (data->cy == 0 && + if (py == 0 || + (data->cy == 0 && (screen_hsize(data->backing) == 0 || data->oy >= - screen_hsize(data->backing) - 1)) + screen_hsize(data->backing) - 1))) goto out; - py = screen_hsize(data->backing) + data->cy - - data->oy; + py--; px = window_copy_find_length(wme, py); /* Stop if separator at EOL. */ From 1bf9555e4f1ad19e1e6f97ede6fb19808ff1c267 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jun 2020 08:18:34 +0000 Subject: [PATCH 0517/1006] d and D keys to reset to default in customize mode. --- cmd-set-option.c | 11 +-- input.c | 8 +-- key-bindings.c | 49 ++++++++++++++ mode-tree.c | 15 ++++- options.c | 21 +++++- tmux.1 | 2 + tmux.h | 6 +- window-customize.c | 164 +++++++++++++++++++++++++++++++++++---------- 8 files changed, 222 insertions(+), 54 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 36579f29..0df12aa0 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -151,16 +151,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'u')) { if (o == NULL) goto out; - if (idx == -1) { - if (*name == '@') - options_remove(o); - else if (oo == global_options || - oo == global_s_options || - oo == global_w_options) - options_default(oo, options_table_entry(o)); - else - options_remove(o); - } else if (options_array_set(o, idx, NULL, 0, &cause) != 0) { + if (options_remove_or_default(o, idx, &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); goto fail; diff --git a/input.c b/input.c index 46d8734d..a3850371 100644 --- a/input.c +++ b/input.c @@ -2347,7 +2347,7 @@ static void input_exit_rename(struct input_ctx *ictx) { struct window_pane *wp = ictx->wp; - struct options_entry *oe; + struct options_entry *o; if (wp == NULL) return; @@ -2361,9 +2361,9 @@ input_exit_rename(struct input_ctx *ictx) return; if (ictx->input_len == 0) { - oe = options_get_only(wp->window->options, "automatic-rename"); - if (oe != NULL) - options_remove(oe); + o = options_get_only(wp->window->options, "automatic-rename"); + if (o != NULL) + options_remove_or_default(o, -1, NULL); return; } window_set_name(wp->window, ictx->input_buf); diff --git a/key-bindings.c b/key-bindings.c index 9e198123..f11bb430 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -231,6 +231,38 @@ key_bindings_remove(const char *name, key_code key) } } +void +key_bindings_reset(const char *name, key_code key) +{ + struct key_table *table; + struct key_binding *bd, *dd; + + table = key_bindings_get_table(name, 0); + if (table == NULL) + return; + + bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); + if (bd == NULL) + return; + + dd = key_bindings_get_default(table, bd->key); + if (dd == NULL) { + key_bindings_remove(name, bd->key); + return; + } + + cmd_list_free(bd->cmdlist); + bd->cmdlist = dd->cmdlist; + bd->cmdlist->references++; + + free((void *)bd->note); + if (dd->note != NULL) + bd->note = xstrdup(dd->note); + else + bd->note = NULL; + bd->flags = dd->flags; +} + void key_bindings_remove_table(const char *name) { @@ -248,6 +280,23 @@ key_bindings_remove_table(const char *name) } } +void +key_bindings_reset_table(const char *name) +{ + struct key_table *table; + struct key_binding *bd, *bd1; + + table = key_bindings_get_table(name, 0); + if (table == NULL) + return; + if (RB_EMPTY(&table->default_key_bindings)) { + key_bindings_remove_table(name); + return; + } + RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) + key_bindings_reset(name, bd->key); +} + static enum cmd_retval key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data) { diff --git a/mode-tree.c b/mode-tree.c index 993070ec..c4b776f9 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -80,7 +80,9 @@ struct mode_tree_item { int expanded; int tagged; + int draw_as_parent; + int no_tag; struct mode_tree_list children; TAILQ_ENTRY(mode_tree_item) entry; @@ -565,6 +567,12 @@ mode_tree_draw_as_parent(struct mode_tree_item *mti) mti->draw_as_parent = 1; } +void +mode_tree_no_tag(struct mode_tree_item *mti) +{ + mti->no_tag = 1; +} + void mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti) { @@ -1053,6 +1061,8 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, * Do not allow parents and children to both be tagged: untag * all parents and children of current. */ + if (current->no_tag) + break; if (!current->tagged) { parent = current->parent; while (parent != NULL) { @@ -1072,7 +1082,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, break; case '\024': /* C-t */ for (i = 0; i < mtd->line_size; i++) { - if (mtd->line_list[i].item->parent == NULL) + if ((mtd->line_list[i].item->parent == NULL && + !mtd->line_list[i].item->no_tag) || + (mtd->line_list[i].item->parent != NULL && + mtd->line_list[i].item->parent->no_tag)) mtd->line_list[i].item->tagged = 1; else mtd->line_list[i].item->tagged = 0; diff --git a/options.c b/options.c index 6ed38bcd..336eb732 100644 --- a/options.c +++ b/options.c @@ -66,6 +66,7 @@ struct options { }; static struct options_entry *options_add(struct options *, const char *); +static void options_remove(struct options_entry *); #define OPTIONS_IS_STRING(o) \ ((o)->tableentry == NULL || \ @@ -315,7 +316,7 @@ options_add(struct options *oo, const char *name) return (o); } -void +static void options_remove(struct options_entry *o) { struct options *oo = o->owner; @@ -1106,3 +1107,21 @@ options_push_changes(const char *name) server_redraw_client(loop); } } + +int +options_remove_or_default(struct options_entry *o, int idx, char **cause) +{ + struct options *oo = o->owner; + + if (idx == -1) { + if (o->tableentry != NULL && + (oo == global_options || + oo == global_s_options || + oo == global_w_options)) + options_default(oo, o->tableentry); + else + options_remove(o); + } else if (options_array_set(o, idx, NULL, 0, cause) != 0) + return (-1); + return (0); +} diff --git a/tmux.1 b/tmux.1 index d6fd1c85..3c017e64 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2031,6 +2031,8 @@ The following keys may be used in customize mode: .It Li "s" Ta "Set option value or key attribute" .It Li "S" Ta "Set global option value" .It Li "w" Ta "Set window option value, if option is for pane and window" +.It Li "d" Ta "Set an option or key to the default" +.It Li "D" Ta "Set tagged options and tagged keys to the default" .It Li "u" Ta "Unset an option (set to default value if global) or unbind a key" .It Li "U" Ta "Unset tagged options and unbind tagged keys" .It Li "C-s" Ta "Search by name" diff --git a/tmux.h b/tmux.h index 9bc89628..837566ca 100644 --- a/tmux.h +++ b/tmux.h @@ -1988,7 +1988,6 @@ struct options *options_owner(struct options_entry *); const struct options_table_entry *options_table_entry(struct options_entry *); struct options_entry *options_get_only(struct options *, const char *); struct options_entry *options_get(struct options *, const char *); -void options_remove(struct options_entry *); void options_array_clear(struct options_entry *); union options_value *options_array_get(struct options_entry *, u_int); int options_array_set(struct options_entry *, u_int, const char *, @@ -2025,6 +2024,8 @@ int options_from_string(struct options *, const struct options_table_entry *, const char *, const char *, int, char **); void options_push_changes(const char *); +int options_remove_or_default(struct options_entry *, int, + char **); /* options-table.c */ extern const struct options_table_entry options_table[]; @@ -2325,7 +2326,9 @@ struct key_binding *key_bindings_next(struct key_table *, struct key_binding *); void key_bindings_add(const char *, key_code, const char *, int, struct cmd_list *); void key_bindings_remove(const char *, key_code); +void key_bindings_reset(const char *, key_code); void key_bindings_remove_table(const char *); +void key_bindings_reset_table(const char *); void key_bindings_init(void); struct cmdq_item *key_bindings_dispatch(struct key_binding *, struct cmdq_item *, struct client *, struct key_event *, @@ -2802,6 +2805,7 @@ struct mode_tree_item *mode_tree_add(struct mode_tree_data *, struct mode_tree_item *, void *, uint64_t, const char *, const char *, int); void mode_tree_draw_as_parent(struct mode_tree_item *); +void mode_tree_no_tag(struct mode_tree_item *); void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *); void mode_tree_draw(struct mode_tree_data *); int mode_tree_key(struct mode_tree_data *, struct client *, key_code *, diff --git a/window-customize.c b/window-customize.c index 093ebbe4..f60d2e6e 100644 --- a/window-customize.c +++ b/window-customize.c @@ -76,6 +76,11 @@ enum window_customize_scope { WINDOW_CUSTOMIZE_PANE }; +enum window_customize_change { + WINDOW_CUSTOMIZE_UNSET, + WINDOW_CUSTOMIZE_RESET, +}; + struct window_customize_itemdata { struct window_customize_modedata *data; enum window_customize_scope scope; @@ -101,6 +106,7 @@ struct window_customize_modedata { u_int item_size; struct cmd_find_state fs; + enum window_customize_change change; }; static uint64_t @@ -380,6 +386,7 @@ window_customize_build_options(struct window_customize_modedata *data, enum window_customize_scope scope; top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0); + mode_tree_no_tag(top); /* * We get the options from the first tree, but build it using the @@ -452,6 +459,7 @@ window_customize_build_keys(struct window_customize_modedata *data, xasprintf(&title, "Key Table - %s", kt->name); top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0); + mode_tree_no_tag(top); free(title); ft = format_create_from_state(NULL, NULL, fs); @@ -476,6 +484,8 @@ window_customize_build_keys(struct window_customize_modedata *data, item->scope = WINDOW_CUSTOMIZE_KEY; item->table = xstrdup(kt->name); item->key = bd->key; + item->name = xstrdup(key_string_lookup_key(item->key, 0)); + item->idx = -1; expanded = format_expand(ft, data->format); child = mode_tree_add(data->data, top, item, (uint64_t)bd, @@ -488,6 +498,7 @@ window_customize_build_keys(struct window_customize_modedata *data, mti = mode_tree_add(data->data, child, item, tag|(bd->key << 3)|(0 << 1)|1, "Command", text, -1); mode_tree_draw_as_parent(mti); + mode_tree_no_tag(mti); free(text); if (bd->note != NULL) @@ -497,6 +508,7 @@ window_customize_build_keys(struct window_customize_modedata *data, mti = mode_tree_add(data->data, child, item, tag|(bd->key << 3)|(1 << 1)|1, "Note", text, -1); mode_tree_draw_as_parent(mti); + mode_tree_no_tag(mti); free(text); if (bd->flags & KEY_BINDING_REPEAT) @@ -506,6 +518,7 @@ window_customize_build_keys(struct window_customize_modedata *data, mti = mode_tree_add(data->data, child, item, tag|(bd->key << 3)|(2 << 1)|1, "Repeat", flag, -1); mode_tree_draw_as_parent(mti); + mode_tree_no_tag(mti); bd = key_bindings_next(kt, bd); } @@ -1125,8 +1138,7 @@ static void window_customize_unset_option(struct window_customize_modedata *data, struct window_customize_itemdata *item) { - struct options_entry *o; - const struct options_table_entry *oe; + struct options_entry *o; if (item == NULL || !window_customize_check_item(data, item, NULL)) return; @@ -1134,20 +1146,30 @@ window_customize_unset_option(struct window_customize_modedata *data, o = options_get(item->oo, item->name); if (o == NULL) return; - if (item->idx != -1) { - if (item == mode_tree_get_current(data->data)) - mode_tree_up(data->data, 0); - options_array_set(o, item->idx, NULL, 0, NULL); + if (item->idx != -1 && item == mode_tree_get_current(data->data)) + mode_tree_up(data->data, 0); + options_remove_or_default(o, item->idx, NULL); +} + +static void +window_customize_reset_option(struct window_customize_modedata *data, + struct window_customize_itemdata *item) +{ + struct options *oo; + struct options_entry *o; + + if (item == NULL || !window_customize_check_item(data, item, NULL)) return; + if (item->idx != -1) + return; + + oo = item->oo; + while (oo != NULL) { + o = options_get_only(item->oo, item->name); + if (o != NULL) + options_remove_or_default(o, -1, NULL); + oo = options_get_parent(oo); } - oe = options_table_entry(o); - if (oe != NULL && - options_owner(o) != global_options && - options_owner(o) != global_s_options && - options_owner(o) != global_w_options) - options_remove(o); - else - options_default(options_owner(o), oe); } static int @@ -1286,21 +1308,52 @@ window_customize_unset_key(struct window_customize_modedata *data, } static void -window_customize_unset_each(void *modedata, void *itemdata, +window_customize_reset_key(struct window_customize_modedata *data, + struct window_customize_itemdata *item) +{ + struct key_table *kt; + struct key_binding *dd, *bd; + + if (item == NULL || !window_customize_get_key(item, &kt, &bd)) + return; + + dd = key_bindings_get_default(kt, bd->key); + if (dd != NULL && bd->cmdlist == dd->cmdlist) + return; + if (dd == NULL && item == mode_tree_get_current(data->data)) { + mode_tree_collapse_current(data->data); + mode_tree_up(data->data, 0); + } + key_bindings_reset(kt->name, bd->key); +} + +static void +window_customize_change_each(void *modedata, void *itemdata, __unused struct client *c, __unused key_code key) { + struct window_customize_modedata *data = modedata; struct window_customize_itemdata *item = itemdata; - if (item->scope == WINDOW_CUSTOMIZE_KEY) - window_customize_unset_key(modedata, item); - else { - window_customize_unset_option(modedata, item); - options_push_changes(item->name); + switch (data->change) { + case WINDOW_CUSTOMIZE_UNSET: + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_unset_key(data, item); + else + window_customize_unset_option(data, item); + break; + case WINDOW_CUSTOMIZE_RESET: + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_reset_key(data, item); + else + window_customize_reset_option(data, item); + break; } + if (item->scope != WINDOW_CUSTOMIZE_KEY) + options_push_changes(item->name); } static int -window_customize_unset_current_callback(__unused struct client *c, +window_customize_change_current_callback(__unused struct client *c, void *modedata, const char *s, __unused int done) { struct window_customize_modedata *data = modedata; @@ -1312,12 +1365,22 @@ window_customize_unset_current_callback(__unused struct client *c, return (0); item = mode_tree_get_current(data->data); - if (item->scope == WINDOW_CUSTOMIZE_KEY) - window_customize_unset_key(data, item); - else { - window_customize_unset_option(data, item); - options_push_changes(item->name); + switch (data->change) { + case WINDOW_CUSTOMIZE_UNSET: + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_unset_key(data, item); + else + window_customize_unset_option(data, item); + break; + case WINDOW_CUSTOMIZE_RESET: + if (item->scope == WINDOW_CUSTOMIZE_KEY) + window_customize_reset_key(data, item); + else + window_customize_reset_option(data, item); + break; } + if (item->scope != WINDOW_CUSTOMIZE_KEY) + options_push_changes(item->name); mode_tree_build(data->data); mode_tree_draw(data->data); data->wp->flags |= PANE_REDRAW; @@ -1326,7 +1389,7 @@ window_customize_unset_current_callback(__unused struct client *c, } static int -window_customize_unset_tagged_callback(struct client *c, void *modedata, +window_customize_change_tagged_callback(struct client *c, void *modedata, const char *s, __unused int done) { struct window_customize_modedata *data = modedata; @@ -1336,7 +1399,7 @@ window_customize_unset_tagged_callback(struct client *c, void *modedata, if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') return (0); - mode_tree_each_tagged(data->data, window_customize_unset_each, c, + mode_tree_each_tagged(data->data, window_customize_change_each, c, KEYC_NONE, 0); mode_tree_build(data->data); mode_tree_draw(data->data); @@ -1353,7 +1416,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, struct window_pane *wp = wme->wp; struct window_customize_modedata *data = wme->data; struct window_customize_itemdata *item, *new_item; - int finished; + int finished, idx; char *prompt; u_int tagged; @@ -1390,17 +1453,43 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, options_push_changes(item->name); mode_tree_build(data->data); break; + case 'd': + if (item == NULL || item->idx != -1) + break; + xasprintf(&prompt, "Reset %s to default? ", item->name); + data->references++; + data->change = WINDOW_CUSTOMIZE_RESET; + status_prompt_set(c, NULL, prompt, "", + window_customize_change_current_callback, + window_customize_free_callback, data, + PROMPT_SINGLE|PROMPT_NOFORMAT); + free(prompt); + break; + case 'D': + tagged = mode_tree_count_tagged(data->data); + if (tagged == 0) + break; + xasprintf(&prompt, "Reset %u tagged to default? ", tagged); + data->references++; + data->change = WINDOW_CUSTOMIZE_RESET; + status_prompt_set(c, NULL, prompt, "", + window_customize_change_tagged_callback, + window_customize_free_callback, data, + PROMPT_SINGLE|PROMPT_NOFORMAT); + free(prompt); + break; case 'u': if (item == NULL) break; - if (item->scope == WINDOW_CUSTOMIZE_KEY) { - xasprintf(&prompt, "Unbind key %s? ", - key_string_lookup_key(item->key, 0)); - } else - xasprintf(&prompt, "Unset option %s? ", item->name); + idx = item->idx; + if (idx != -1) + xasprintf(&prompt, "Unset %s[%d]? ", item->name, idx); + else + xasprintf(&prompt, "Unset %s? ", item->name); data->references++; + data->change = WINDOW_CUSTOMIZE_UNSET; status_prompt_set(c, NULL, prompt, "", - window_customize_unset_current_callback, + window_customize_change_current_callback, window_customize_free_callback, data, PROMPT_SINGLE|PROMPT_NOFORMAT); free(prompt); @@ -1409,10 +1498,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, tagged = mode_tree_count_tagged(data->data); if (tagged == 0) break; - xasprintf(&prompt, "Unset or unbind %u tagged? ", tagged); + xasprintf(&prompt, "Unset %u tagged? ", tagged); data->references++; + data->change = WINDOW_CUSTOMIZE_UNSET; status_prompt_set(c, NULL, prompt, "", - window_customize_unset_tagged_callback, + window_customize_change_tagged_callback, window_customize_free_callback, data, PROMPT_SINGLE|PROMPT_NOFORMAT); free(prompt); From 2372b0fdc67f17336e39d8eb86019103aad6bb4e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Jun 2020 08:34:22 +0000 Subject: [PATCH 0518/1006] Add a flag to make a client wait for an empty line before exiting in control mode to avoid stray commands ending up in the shell. --- client.c | 38 +++++++++++++++++++++++++++++++++----- control.c | 1 + server-client.c | 5 +++++ server.c | 2 +- tmux.1 | 2 ++ tmux.h | 2 ++ 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/client.c b/client.c index c97df491..852b09ce 100644 --- a/client.c +++ b/client.c @@ -35,7 +35,7 @@ static struct tmuxproc *client_proc; static struct tmuxpeer *client_peer; -static int client_flags; +static uint64_t client_flags; static enum { CLIENT_EXIT_NONE, CLIENT_EXIT_DETACHED, @@ -247,7 +247,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat) pid_t ppid; enum msgtype msg; struct termios tio, saved_tio; - size_t size; + size_t size, linesize = 0; + ssize_t linelen; + char *line = NULL; /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ signal(SIGCHLD, SIG_IGN); @@ -276,13 +278,14 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat) free(pr->error); } - /* Save the flags. */ - client_flags = flags; - /* Create client process structure (starts logging). */ client_proc = proc_start("client"); proc_set_signals(client_proc, client_signal); + /* Save the flags. */ + client_flags = flags; + log_debug("flags are %#llx", client_flags); + /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, client_flags); if (fd == -1) { @@ -406,8 +409,19 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat) printf("%%exit %s\n", client_exit_message()); else printf("%%exit\n"); + fflush(stdout); + if (client_flags & CLIENT_CONTROL_WAITEXIT) { + setvbuf(stdin, NULL, _IOLBF, 0); + for (;;) { + linelen = getline(&line, &linesize, stdin); + if (linelen <= 1) + break; + } + free(line); + } if (client_flags & CLIENT_CONTROLCONTROL) { printf("\033\\"); + fflush(stdout); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); } } else if (client_exitreason != CLIENT_EXIT_NONE) @@ -870,6 +884,13 @@ client_dispatch_wait(struct imsg *imsg) client_exitval = 1; proc_exit(client_proc); break; + case MSG_FLAGS: + if (datalen != sizeof client_flags) + fatalx("bad MSG_FLAGS string"); + + memcpy(&client_flags, data, sizeof client_flags); + log_debug("new flags are %#llx", client_flags); + break; case MSG_SHELL: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_SHELL string"); @@ -916,6 +937,13 @@ client_dispatch_attached(struct imsg *imsg) datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { + case MSG_FLAGS: + if (datalen != sizeof client_flags) + fatalx("bad MSG_FLAGS string"); + + memcpy(&client_flags, data, sizeof client_flags); + log_debug("new flags are %#llx", client_flags); + break; case MSG_DETACH: case MSG_DETACHKILL: if (datalen == 0 || data[datalen - 1] != '\0') diff --git a/control.c b/control.c index 05093d94..5681d2dc 100644 --- a/control.c +++ b/control.c @@ -695,6 +695,7 @@ control_discard(struct client *c) RB_FOREACH(cp, control_panes, &cs->panes) control_discard_pane(c, cp); + bufferevent_disable(cs->read_event, EV_READ); } /* Stop control mode. */ diff --git a/server-client.c b/server-client.c index 9e7adf55..d86a9fb8 100644 --- a/server-client.c +++ b/server-client.c @@ -2370,6 +2370,8 @@ server_client_control_flags(struct client *c, const char *next) } if (strcmp(next, "no-output") == 0) return (CLIENT_CONTROL_NOOUTPUT); + if (strcmp(next, "wait-exit") == 0) + return (CLIENT_CONTROL_WAITEXIT); return (0); } @@ -2409,6 +2411,7 @@ server_client_set_flags(struct client *c, const char *flags) control_reset_offsets(c); } free(copy); + proc_send(c->peer, MSG_FLAGS, -1, &c->flags, sizeof c->flags); } /* Get client flags. This is only flags useful to show to users. */ @@ -2427,6 +2430,8 @@ server_client_get_flags(struct client *c) strlcat(s, "ignore-size,", sizeof s); if (c->flags & CLIENT_CONTROL_NOOUTPUT) strlcat(s, "no-output,", sizeof s); + if (c->flags & CLIENT_CONTROL_WAITEXIT) + strlcat(s, "wait-exit,", sizeof s); if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { xsnprintf(tmp, sizeof tmp, "pause-after=%u,", c->pause_age / 1000); diff --git a/server.c b/server.c index 67d47f23..f07bf673 100644 --- a/server.c +++ b/server.c @@ -45,7 +45,7 @@ struct clients clients; struct tmuxproc *server_proc; static int server_fd = -1; -static int server_client_flags; +static uint64_t server_client_flags; static int server_exit; static struct event server_ev_accept; diff --git a/tmux.1 b/tmux.1 index 3c017e64..275dd7e5 100644 --- a/tmux.1 +++ b/tmux.1 @@ -989,6 +989,8 @@ output is paused once the pane is behind in control mode .It read-only the client is read-only +.It wait-exit +wait for an empty line input before exiting in control mode .El .Pp A leading diff --git a/tmux.h b/tmux.h index 837566ca..0a1a740b 100644 --- a/tmux.h +++ b/tmux.h @@ -515,6 +515,7 @@ enum msgtype { MSG_UNLOCK, MSG_WAKEUP, MSG_EXEC, + MSG_FLAGS, MSG_READ_OPEN = 300, MSG_READ, @@ -1644,6 +1645,7 @@ struct client { #define CLIENT_NOFORK 0x40000000 #define CLIENT_ACTIVEPANE 0x80000000ULL #define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL +#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ From 068b92b0512bb29e29d1c2aa76c0b29f788e45f6 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Jun 2020 08:41:56 +0000 Subject: [PATCH 0519/1006] The redraw callback could be fired with a NULL pane if it updates while in a mode, problem reported by Martin Vahlensieck. --- screen-write.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/screen-write.c b/screen-write.c index 46ac967e..4c328ca2 100644 --- a/screen-write.c +++ b/screen-write.c @@ -103,7 +103,8 @@ screen_write_redraw_cb(const struct tty_ctx *ttyctx) { struct window_pane *wp = ttyctx->arg; - wp->flags |= PANE_REDRAW; + if (wp != NULL) + wp->flags |= PANE_REDRAW; } /* Update context for client. */ From 3df68d6b00ad554f4527f2057792fc6468ab9375 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 18 Jun 2020 21:01:45 +0100 Subject: [PATCH 0520/1006] Fix regress test for am. --- regress/{xenl-terminal.sh => am-terminal.sh} | 2 +- .../2e0f96ac3e1c144ce48261a4c9d68a48.conf | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) rename regress/{xenl-terminal.sh => am-terminal.sh} (92%) create mode 100644 regress/conf/2e0f96ac3e1c144ce48261a4c9d68a48.conf diff --git a/regress/xenl-terminal.sh b/regress/am-terminal.sh similarity index 92% rename from regress/xenl-terminal.sh rename to regress/am-terminal.sh index 07469ceb..94033468 100644 --- a/regress/xenl-terminal.sh +++ b/regress/am-terminal.sh @@ -13,7 +13,7 @@ TMP=$(mktemp) trap "rm -f $TMP" 0 1 15 $TMUX2 -f/dev/null new -d || exit 1 -$TMUX2 set -as terminal-overrides ',*:xenl@' || exit 1 +$TMUX2 set -as terminal-overrides ',*:am@' || exit 1 $TMUX2 set -g status-right 'RRR' || exit 1 $TMUX2 set -g status-left 'LLL' || exit 1 $TMUX2 set -g window-status-current-format 'WWW' || exit 1 diff --git a/regress/conf/2e0f96ac3e1c144ce48261a4c9d68a48.conf b/regress/conf/2e0f96ac3e1c144ce48261a4c9d68a48.conf new file mode 100644 index 00000000..df4c2b1c --- /dev/null +++ b/regress/conf/2e0f96ac3e1c144ce48261a4c9d68a48.conf @@ -0,0 +1,25 @@ +bind -r Up if -F '#{pane_at_top}' '' 'selectp -U' +bind -r Down if -F '#{pane_at_bottom}' '' 'selectp -D' +bind -r Left if -F '#{pane_at_left}' '' 'selectp -L' +bind -r Right if -F '#{pane_at_right}' '' 'selectp -R' + +bind -n WheelUpPane if -Ft= "#{mouse_any_flag}" "send -M" "send Up" +bind -n WheelDownPane if -Ft= "#{mouse_any_flag}" "send -M" "send Down" + +bind w run 'tmux choose-tree -Nwf"##{==:##{session_name},#{session_name}}"' + +bind C { + splitw -f -l30% '' + set-hook -p pane-mode-changed 'if -F "#{!=:#{pane_mode},copy-mode}" "kill-pane"' + copy-mode -s'{last}' +} + +bind -n C-DoubleClick1Pane if -F '#{m/r:^[^:]*:[0-9]+:,#{mouse_word}}' { + popup -w90% -h90% -KE -d '#{pane_current_path}' -R { + emacs `echo #{mouse_word}|awk -F: '{print "+" $2,$1}'` + } +} { + popup -w90% -h90% -KE -d '#{pane_current_path}' -R { + emacs "#{mouse_word}" + } +} From 3a1fc7315c88868edb18e501879ce22e3c77567b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 18 Jun 2020 21:01:55 +0100 Subject: [PATCH 0521/1006] Add getline compat. --- compat.h | 5 +++ compat/getline.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 1 + 3 files changed, 99 insertions(+) create mode 100644 compat/getline.c diff --git a/compat.h b/compat.h index 37f78e7b..5b23b178 100644 --- a/compat.h +++ b/compat.h @@ -345,6 +345,11 @@ int vasprintf(char **, const char *, va_list); char *fgetln(FILE *, size_t *); #endif +#ifndef HAVE_GETLINE +/* getline.c */ +ssize_t getline(char **, size_t *, FILE *); +#endif + #ifndef HAVE_SETENV /* setenv.c */ int setenv(const char *, const char *, int); diff --git a/compat/getline.c b/compat/getline.c new file mode 100644 index 00000000..90437ccb --- /dev/null +++ b/compat/getline.c @@ -0,0 +1,93 @@ +/* $NetBSD: getline.c,v 1.1.1.6 2015/01/02 20:34:27 christos Exp $ */ + +/* NetBSD: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* NETBSD ORIGINAL: external/bsd/file/dist/src/getline.c */ + +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +static ssize_t +getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) +{ + char *ptr, *eptr; + + + if (*buf == NULL || *bufsiz == 0) { + if ((*buf = malloc(BUFSIZ)) == NULL) + return -1; + *bufsiz = BUFSIZ; + } + + for (ptr = *buf, eptr = *buf + *bufsiz;;) { + int c = fgetc(fp); + if (c == -1) { + if (feof(fp)) { + ssize_t diff = (ssize_t)(ptr - *buf); + if (diff != 0) { + *ptr = '\0'; + return diff; + } + } + return -1; + } + *ptr++ = c; + if (c == delimiter) { + *ptr = '\0'; + return ptr - *buf; + } + if (ptr + 2 >= eptr) { + char *nbuf; + size_t nbufsiz = *bufsiz * 2; + ssize_t d = ptr - *buf; + if ((nbuf = realloc(*buf, nbufsiz)) == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + eptr = nbuf + nbufsiz; + ptr = nbuf + d; + } + } +} + +ssize_t +getline(char **buf, size_t *bufsiz, FILE *fp) +{ + return getdelim(buf, bufsiz, '\n', fp); +} diff --git a/configure.ac b/configure.ac index dc133c25..b5f65df5 100644 --- a/configure.ac +++ b/configure.ac @@ -113,6 +113,7 @@ AC_REPLACE_FUNCS([ \ fgetln \ freezero \ getdtablecount \ + getline \ getprogname \ memmem \ recallocarray \ From b730083d7a09ce84f98a8a586553ebf234f89a7d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 22 Jun 2020 12:53:43 +0100 Subject: [PATCH 0522/1006] Add to CHANGES. --- CHANGES | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index ec6473f1..1373daba 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,14 @@ CHANGES FROM 3.1b TO 3.2 +* Add some formats for search in copy mode (search_present, search_match). + +* Do not wait on shutdown for commands started with run -b. + +* Add -b flags to insert a window before (like the existing -a for after) to + break-pane, move-window, new-window. + +* Make paste -p the default for ]. + * Add support for pausing a pane when the output buffered for a control mode client gets too far behind. The pause-after flag with a time is set on the pane with refresh-client -f and a paused pane may be resumed with From e450416c93b95112640f61d81ea385dedcb14ef0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 22 Jun 2020 12:55:10 +0100 Subject: [PATCH 0523/1006] 3.2-rc. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b5f65df5..b698526d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], next-3.2) +AC_INIT([tmux], 3.2-rc) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 2964dde903f90a8160db00bf496582bff5256a98 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 23 Jun 2020 05:23:26 +0000 Subject: [PATCH 0524/1006] Use xvasprintf not vasprintf. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index b657b81c..20d1fc69 100644 --- a/format.c +++ b/format.c @@ -224,7 +224,7 @@ format_log1(struct format_tree *ft, const char *from, const char *fmt, ...) return; va_start(ap, fmt); - vasprintf(&s, fmt, ap); + xvasprintf(&s, fmt, ap); va_end(ap); log_debug("%s: %s", from, s); From 5340bf556e460c52f02d4f23591eafd5131d95ed Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 23 Jun 2020 14:10:43 +0000 Subject: [PATCH 0525/1006] Correctly redraw pane border bottom line when the status line is on and at the bottom, reported by Kaushal Modi. --- screen-redraw.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 002970e9..258c2fd2 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -28,6 +28,8 @@ static void screen_redraw_draw_panes(struct screen_redraw_ctx *); static void screen_redraw_draw_status(struct screen_redraw_ctx *); static void screen_redraw_draw_pane(struct screen_redraw_ctx *, struct window_pane *); +static void screen_redraw_set_context(struct client *, + struct screen_redraw_ctx *); #define CELL_INSIDE 0 #define CELL_LEFTRIGHT 1 @@ -251,6 +253,10 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, u_int sx = w->sx, sy = w->sy; int borders = 0; + /* Is this outside the window? */ + if (px >= sx || py >= sy) + return (CELL_OUTSIDE); + /* * Construct a bitmask of whether the cells to the left (bit 4), right, * top, and bottom (bit 1) of this cell are borders. @@ -263,13 +269,16 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, if (py != 0 && screen_redraw_cell_border(c, px, py - 1, pane_status)) borders |= 2; + if (screen_redraw_cell_border(c, px, py + 1, pane_status)) + borders |= 1; } else { if (py == 0 || screen_redraw_cell_border(c, px, py - 1, pane_status)) - borders |= 2; + borders |= 2; + if (py != sy - 1 && + screen_redraw_cell_border(c, px, py + 1, pane_status)) + borders |= 1; } - if (py <= sy && screen_redraw_cell_border(c, px, py + 1, pane_status)) - borders |= 1; /* * Figure out what kind of border this cell is. Only one bit set @@ -315,7 +324,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, *wpp = NULL; - if (px > w->sx || py > w->sy) + if (px >= w->sx || py >= w->sy) return (CELL_OUTSIDE); if (px == w->sx || py == w->sy) /* window border */ return (screen_redraw_type_of_cell(c, px, py, pane_status)); @@ -383,14 +392,16 @@ screen_redraw_check_is(u_int px, u_int py, int pane_status, /* Update pane status. */ static int -screen_redraw_make_pane_status(struct client *c, struct window *w, - struct window_pane *wp, int pane_lines) +screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, + struct screen_redraw_ctx *rctx, int pane_lines) { + struct window *w = wp->window; struct grid_cell gc; const char *fmt; struct format_tree *ft; char *expanded; - u_int width, i; + int pane_status = rctx->pane_status; + u_int width, i, cell_type, top, px, py; struct screen_write_ctx ctx; struct screen old; @@ -415,9 +426,20 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, screen_write_start(&ctx, &wp->status_screen); - screen_redraw_border_set(wp, pane_lines, CELL_TOPBOTTOM, &gc); - for (i = 0; i < width; i++) + if (rctx->statustop) + top = rctx->statuslines; + else + top = 0; + for (i = 0; i < width; i++) { + px = wp->xoff + 2 + i; + if (rctx->pane_status == PANE_STATUS_TOP) + py = top + wp->yoff - 1; + else + py = top + wp->yoff + wp->sy; + cell_type = screen_redraw_type_of_cell(c, px, py, pane_status); + screen_redraw_border_set(wp, pane_lines, cell_type, &gc); screen_write_cell(&ctx, &gc); + } gc.attr &= ~GRID_ATTR_CHARSET; screen_write_cursormove(&ctx, 0, 0, 0); @@ -504,6 +526,7 @@ screen_redraw_update(struct client *c, int flags) struct window_pane *wp; struct options *wo = w->options; int redraw, lines; + struct screen_redraw_ctx ctx; if (c->message_string != NULL) redraw = status_message_redraw(c); @@ -518,10 +541,11 @@ screen_redraw_update(struct client *c, int flags) flags |= CLIENT_REDRAWOVERLAY; if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) { + screen_redraw_set_context(c, &ctx); lines = options_get_number(wo, "pane-border-lines"); redraw = 0; TAILQ_FOREACH(wp, &w->panes, entry) { - if (screen_redraw_make_pane_status(c, w, wp, lines)) + if (screen_redraw_make_pane_status(c, wp, &ctx, lines)) redraw = 1; } if (redraw) From e215a566a413dfd939c29bf5dbdb8aceeb25c136 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 23 Jun 2020 05:23:26 +0000 Subject: [PATCH 0526/1006] Use xvasprintf not vasprintf. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index 920dc4c0..c7c6b12e 100644 --- a/format.c +++ b/format.c @@ -224,7 +224,7 @@ format_log1(struct format_tree *ft, const char *from, const char *fmt, ...) return; va_start(ap, fmt); - vasprintf(&s, fmt, ap); + xvasprintf(&s, fmt, ap); va_end(ap); log_debug("%s: %s", from, s); From 43295bd4a5050b7571a26a242828bb2b8c85fd84 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 23 Jun 2020 14:10:43 +0000 Subject: [PATCH 0527/1006] Correctly redraw pane border bottom line when the status line is on and at the bottom, reported by Kaushal Modi. --- screen-redraw.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 002970e9..258c2fd2 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -28,6 +28,8 @@ static void screen_redraw_draw_panes(struct screen_redraw_ctx *); static void screen_redraw_draw_status(struct screen_redraw_ctx *); static void screen_redraw_draw_pane(struct screen_redraw_ctx *, struct window_pane *); +static void screen_redraw_set_context(struct client *, + struct screen_redraw_ctx *); #define CELL_INSIDE 0 #define CELL_LEFTRIGHT 1 @@ -251,6 +253,10 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, u_int sx = w->sx, sy = w->sy; int borders = 0; + /* Is this outside the window? */ + if (px >= sx || py >= sy) + return (CELL_OUTSIDE); + /* * Construct a bitmask of whether the cells to the left (bit 4), right, * top, and bottom (bit 1) of this cell are borders. @@ -263,13 +269,16 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, if (py != 0 && screen_redraw_cell_border(c, px, py - 1, pane_status)) borders |= 2; + if (screen_redraw_cell_border(c, px, py + 1, pane_status)) + borders |= 1; } else { if (py == 0 || screen_redraw_cell_border(c, px, py - 1, pane_status)) - borders |= 2; + borders |= 2; + if (py != sy - 1 && + screen_redraw_cell_border(c, px, py + 1, pane_status)) + borders |= 1; } - if (py <= sy && screen_redraw_cell_border(c, px, py + 1, pane_status)) - borders |= 1; /* * Figure out what kind of border this cell is. Only one bit set @@ -315,7 +324,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, *wpp = NULL; - if (px > w->sx || py > w->sy) + if (px >= w->sx || py >= w->sy) return (CELL_OUTSIDE); if (px == w->sx || py == w->sy) /* window border */ return (screen_redraw_type_of_cell(c, px, py, pane_status)); @@ -383,14 +392,16 @@ screen_redraw_check_is(u_int px, u_int py, int pane_status, /* Update pane status. */ static int -screen_redraw_make_pane_status(struct client *c, struct window *w, - struct window_pane *wp, int pane_lines) +screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, + struct screen_redraw_ctx *rctx, int pane_lines) { + struct window *w = wp->window; struct grid_cell gc; const char *fmt; struct format_tree *ft; char *expanded; - u_int width, i; + int pane_status = rctx->pane_status; + u_int width, i, cell_type, top, px, py; struct screen_write_ctx ctx; struct screen old; @@ -415,9 +426,20 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, screen_write_start(&ctx, &wp->status_screen); - screen_redraw_border_set(wp, pane_lines, CELL_TOPBOTTOM, &gc); - for (i = 0; i < width; i++) + if (rctx->statustop) + top = rctx->statuslines; + else + top = 0; + for (i = 0; i < width; i++) { + px = wp->xoff + 2 + i; + if (rctx->pane_status == PANE_STATUS_TOP) + py = top + wp->yoff - 1; + else + py = top + wp->yoff + wp->sy; + cell_type = screen_redraw_type_of_cell(c, px, py, pane_status); + screen_redraw_border_set(wp, pane_lines, cell_type, &gc); screen_write_cell(&ctx, &gc); + } gc.attr &= ~GRID_ATTR_CHARSET; screen_write_cursormove(&ctx, 0, 0, 0); @@ -504,6 +526,7 @@ screen_redraw_update(struct client *c, int flags) struct window_pane *wp; struct options *wo = w->options; int redraw, lines; + struct screen_redraw_ctx ctx; if (c->message_string != NULL) redraw = status_message_redraw(c); @@ -518,10 +541,11 @@ screen_redraw_update(struct client *c, int flags) flags |= CLIENT_REDRAWOVERLAY; if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) { + screen_redraw_set_context(c, &ctx); lines = options_get_number(wo, "pane-border-lines"); redraw = 0; TAILQ_FOREACH(wp, &w->panes, entry) { - if (screen_redraw_make_pane_status(c, w, wp, lines)) + if (screen_redraw_make_pane_status(c, wp, &ctx, lines)) redraw = 1; } if (redraw) From f69bdda950fde2218ed3fc189ceaa587a5374ab9 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 25 Jun 2020 08:56:02 +0000 Subject: [PATCH 0528/1006] Silently ignore -a or -b if the window index doesn't exist and create using that index (this is how it used to work), reported by Romain Francoise. --- cmd-new-window.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd-new-window.c b/cmd-new-window.c index 0b24474b..ca3e66c4 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -68,10 +68,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) before = args_has(args, 'b'); if (args_has(args, 'a') || before) { idx = winlink_shuffle_up(s, wl, before); - if (idx == -1) { - cmdq_error(item, "couldn't get a window index"); - return (CMD_RETURN_ERROR); - } + if (idx == -1) + idx = target->idx; } memset(&sc, 0, sizeof sc); @@ -114,7 +112,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; cp = format_single(item, template, tc, s, new_wl, - new_wl->window->active); + new_wl->window->active); cmdq_print(item, "%s", cp); free(cp); } From 74df7071adf5e721b15c15e9e07c86d1deb86def Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 27 Jun 2020 10:19:59 +0000 Subject: [PATCH 0529/1006] Fix 0x Unicode character parsing, GitHub issue 2286. --- key-string.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/key-string.c b/key-string.c index 4ee12145..6f4fd2ac 100644 --- a/key-string.c +++ b/key-string.c @@ -18,7 +18,9 @@ #include +#include #include +#include #include "tmux.h" @@ -163,13 +165,13 @@ key_code key_string_lookup_string(const char *string) { static const char *other = "!#()+,-.0123456789:;<=>'\r\t"; - key_code key; - u_int u; - key_code modifiers; - struct utf8_data ud; - u_int i; + key_code key, modifiers; + u_int u, i; + struct utf8_data ud, *udp; enum utf8_state more; utf8_char uc; + char m[MB_LEN_MAX + 1]; + int mlen; /* Is this no key or any key? */ if (strcasecmp(string, "None") == 0) @@ -181,9 +183,21 @@ key_string_lookup_string(const char *string) if (string[0] == '0' && string[1] == 'x') { if (sscanf(string + 2, "%x", &u) != 1) return (KEYC_UNKNOWN); - if (u > 0x1fffff) - return (KEYC_UNKNOWN); - return (u); + mlen = wctomb(m, u); + if (mlen <= 0 || mlen > MB_LEN_MAX) + return (KEYC_UNKNOWN); + m[mlen] = '\0'; + + udp = utf8_fromcstr(m); + if (udp == NULL || + udp[0].size == 0 || + udp[1].size != 0 || + utf8_from_data(&udp[0], &uc) != UTF8_DONE) { + free(udp); + return (KEYC_UNKNOWN); + } + free(udp); + return (uc); } /* Check for modifiers. */ From b6aeb86c20abca3b88722da15747755f8a7690f7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 27 Jun 2020 10:23:10 +0000 Subject: [PATCH 0530/1006] Check for no pane border status line separately from top/bottom. --- screen-redraw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/screen-redraw.c b/screen-redraw.c index 258c2fd2..3391a891 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -271,13 +271,19 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, borders |= 2; if (screen_redraw_cell_border(c, px, py + 1, pane_status)) borders |= 1; - } else { + } else if (pane_status == PANE_STATUS_BOTTOM) { if (py == 0 || screen_redraw_cell_border(c, px, py - 1, pane_status)) borders |= 2; if (py != sy - 1 && screen_redraw_cell_border(c, px, py + 1, pane_status)) borders |= 1; + } else { + if (py == 0 || + screen_redraw_cell_border(c, px, py - 1, pane_status)) + borders |= 2; + if (screen_redraw_cell_border(c, px, py + 1, pane_status)) + borders |= 1; } /* From 6cacaa94a5132ba80ee9a7a733aff5fe580ab117 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 25 Jun 2020 08:56:02 +0000 Subject: [PATCH 0531/1006] Silently ignore -a or -b if the window index doesn't exist and create using that index (this is how it used to work), reported by Romain Francoise. --- cmd-new-window.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd-new-window.c b/cmd-new-window.c index 0b24474b..ca3e66c4 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -68,10 +68,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) before = args_has(args, 'b'); if (args_has(args, 'a') || before) { idx = winlink_shuffle_up(s, wl, before); - if (idx == -1) { - cmdq_error(item, "couldn't get a window index"); - return (CMD_RETURN_ERROR); - } + if (idx == -1) + idx = target->idx; } memset(&sc, 0, sizeof sc); @@ -114,7 +112,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; cp = format_single(item, template, tc, s, new_wl, - new_wl->window->active); + new_wl->window->active); cmdq_print(item, "%s", cp); free(cp); } From c9b4d5a4a5c98d9d0a074800b5291d3c8b02f4b1 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 27 Jun 2020 10:19:59 +0000 Subject: [PATCH 0532/1006] Fix 0x Unicode character parsing, GitHub issue 2286. --- key-string.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/key-string.c b/key-string.c index 4ee12145..6f4fd2ac 100644 --- a/key-string.c +++ b/key-string.c @@ -18,7 +18,9 @@ #include +#include #include +#include #include "tmux.h" @@ -163,13 +165,13 @@ key_code key_string_lookup_string(const char *string) { static const char *other = "!#()+,-.0123456789:;<=>'\r\t"; - key_code key; - u_int u; - key_code modifiers; - struct utf8_data ud; - u_int i; + key_code key, modifiers; + u_int u, i; + struct utf8_data ud, *udp; enum utf8_state more; utf8_char uc; + char m[MB_LEN_MAX + 1]; + int mlen; /* Is this no key or any key? */ if (strcasecmp(string, "None") == 0) @@ -181,9 +183,21 @@ key_string_lookup_string(const char *string) if (string[0] == '0' && string[1] == 'x') { if (sscanf(string + 2, "%x", &u) != 1) return (KEYC_UNKNOWN); - if (u > 0x1fffff) - return (KEYC_UNKNOWN); - return (u); + mlen = wctomb(m, u); + if (mlen <= 0 || mlen > MB_LEN_MAX) + return (KEYC_UNKNOWN); + m[mlen] = '\0'; + + udp = utf8_fromcstr(m); + if (udp == NULL || + udp[0].size == 0 || + udp[1].size != 0 || + utf8_from_data(&udp[0], &uc) != UTF8_DONE) { + free(udp); + return (KEYC_UNKNOWN); + } + free(udp); + return (uc); } /* Check for modifiers. */ From 629ba1b83892ad373dd5b88268969d4cde857f22 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 27 Jun 2020 10:23:10 +0000 Subject: [PATCH 0533/1006] Check for no pane border status line separately from top/bottom. --- screen-redraw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/screen-redraw.c b/screen-redraw.c index 258c2fd2..3391a891 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -271,13 +271,19 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, borders |= 2; if (screen_redraw_cell_border(c, px, py + 1, pane_status)) borders |= 1; - } else { + } else if (pane_status == PANE_STATUS_BOTTOM) { if (py == 0 || screen_redraw_cell_border(c, px, py - 1, pane_status)) borders |= 2; if (py != sy - 1 && screen_redraw_cell_border(c, px, py + 1, pane_status)) borders |= 1; + } else { + if (py == 0 || + screen_redraw_cell_border(c, px, py - 1, pane_status)) + borders |= 2; + if (screen_redraw_cell_border(c, px, py + 1, pane_status)) + borders |= 1; } /* From 2a9bdb700d07a5d0885c22a9e73deaf675ba20c2 Mon Sep 17 00:00:00 2001 From: bket Date: Mon, 29 Jun 2020 15:53:28 +0000 Subject: [PATCH 0534/1006] Replace TAILQ concatenation loop with TAILQ_CONCAT As a result remove unneeded variables OK @nicm --- cmd.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cmd.c b/cmd.c index f6023c20..6b443fa2 100644 --- a/cmd.c +++ b/cmd.c @@ -598,12 +598,7 @@ cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) void cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) { - struct cmd *cmd, *cmd1; - - TAILQ_FOREACH_SAFE(cmd, from->list, qentry, cmd1) { - TAILQ_REMOVE(from->list, cmd, qentry); - TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); - } + TAILQ_CONCAT(cmdlist->list, from->list, qentry); cmdlist->group = cmd_list_next_group++; } From 2b1e8d06e18e22e3196c4558f1d300f7088d2417 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Jul 2020 07:00:12 +0000 Subject: [PATCH 0535/1006] Check if client is NULL before using it, GitHub issue 2295. --- cmd-select-pane.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 313deefe..b0c78d74 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -205,7 +205,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) + if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) activewp = server_client_get_pane(c); else activewp = w->active; @@ -214,7 +214,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); - if (c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) + if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) server_client_set_pane(c, wp); else if (window_set_active_pane(w, wp, 1)) cmd_find_from_winlink_pane(current, wl, wp, 0); From 43e1577b5d6c40369eb353322f68a12b36d609b0 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Jul 2020 07:07:50 +0000 Subject: [PATCH 0536/1006] Missing word, from annihilannic at hotmail dot com, GitHub issue 2288. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 275dd7e5..4d079853 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2947,7 +2947,7 @@ specifies a prefix to print before each key and .Fl 1 lists only the first matching key. .Fl a -lists the command for keys that do have a note rather than skipping them. +lists the command for keys that do not have a note rather than skipping them. .It Xo Ic send-keys .Op Fl FHlMRX .Op Fl N Ar repeat-count From 83868ceb1a18013578265591cea7c04a6e0f0516 Mon Sep 17 00:00:00 2001 From: bket Date: Mon, 29 Jun 2020 15:53:28 +0000 Subject: [PATCH 0537/1006] Replace TAILQ concatenation loop with TAILQ_CONCAT As a result remove unneeded variables OK @nicm --- cmd.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cmd.c b/cmd.c index 7e7cac4d..775bdb73 100644 --- a/cmd.c +++ b/cmd.c @@ -597,12 +597,7 @@ cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) void cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) { - struct cmd *cmd, *cmd1; - - TAILQ_FOREACH_SAFE(cmd, from->list, qentry, cmd1) { - TAILQ_REMOVE(from->list, cmd, qentry); - TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); - } + TAILQ_CONCAT(cmdlist->list, from->list, qentry); cmdlist->group = cmd_list_next_group++; } From 0d0fc13aaaea2fc32d39ea8cd5564dbadb40c718 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Jul 2020 07:00:12 +0000 Subject: [PATCH 0538/1006] Check if client is NULL before using it, GitHub issue 2295. --- cmd-select-pane.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 313deefe..b0c78d74 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -205,7 +205,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) + if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) activewp = server_client_get_pane(c); else activewp = w->active; @@ -214,7 +214,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); - if (c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) + if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) server_client_set_pane(c, wp); else if (window_set_active_pane(w, wp, 1)) cmd_find_from_winlink_pane(current, wl, wp, 0); From 5661346c41fc176c29cc829c177c718ee1775d93 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 3 Jul 2020 07:07:50 +0000 Subject: [PATCH 0539/1006] Missing word, from annihilannic at hotmail dot com, GitHub issue 2288. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index ca470ea2..3441e71b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2947,7 +2947,7 @@ specifies a prefix to print before each key and .Fl 1 lists only the first matching key. .Fl a -lists the command for keys that do have a note rather than skipping them. +lists the command for keys that do not have a note rather than skipping them. .It Xo Ic send-keys .Op Fl FHlMRX .Op Fl N Ar repeat-count From a109e839d1275f9e3b593c87f4b84f1d277a84e5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 3 Jul 2020 12:03:25 +0100 Subject: [PATCH 0540/1006] Fix version. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b698526d..a62029b3 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.2-rc) +AC_INIT([tmux], next-3.3) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 1e426896611f81dd6306263cb337e7ea7d80238e Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 4 Jul 2020 14:24:02 +0000 Subject: [PATCH 0541/1006] kill-window -a cannot just walk the list of windows because if renumber-windows is on, the window it wants to keep could be moved. Change to renumber afterwards and also behave better if the window is linked into the session twice. GitHub issue 2287. --- cmd-join-pane.c | 2 +- cmd-kill-window.c | 48 +++++++++++++++++++++++++++++++++++---------- server-fn.c | 50 ++++++++++++++++++++++++++++++----------------- tmux.h | 4 +++- window-tree.c | 2 +- 5 files changed, 75 insertions(+), 31 deletions(-) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 9802083d..904d2c29 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -160,7 +160,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) server_status_session(dst_s); if (window_count_panes(src_w) == 0) - server_kill_window(src_w); + server_kill_window(src_w, 1); else notify_window("window-layout-changed", src_w); notify_window("window-layout-changed", dst_w); diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 68139faa..430f667e 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -57,9 +57,10 @@ cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct winlink *wl = target->wl, *wl2, *wl3; + struct winlink *wl = target->wl, *loop; struct window *w = wl->window; struct session *s = target->s; + u_int found; if (cmd_get_entry(self) == &cmd_unlink_window_entry) { if (!args_has(args, 'k') && !session_is_linked(s, w)) { @@ -67,16 +68,43 @@ cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } server_unlink_window(s, wl); - } else { - if (args_has(args, 'a')) { - RB_FOREACH_SAFE(wl2, winlinks, &s->windows, wl3) { - if (wl != wl2) - server_kill_window(wl2->window); - } - } else - server_kill_window(wl->window); + recalculate_sizes(); + return (CMD_RETURN_NORMAL); } - recalculate_sizes(); + if (args_has(args, 'a')) { + if (RB_PREV(winlinks, &s->windows, wl) == NULL && + RB_NEXT(winlinks, &s->windows, wl) == NULL) + return (CMD_RETURN_NORMAL); + + /* Kill all windows except the current one. */ + do { + found = 0; + RB_FOREACH(loop, winlinks, &s->windows) { + if (loop->window != wl->window) { + server_kill_window(loop->window, 0); + found++; + break; + } + } + } while (found != 0); + + /* + * If the current window appears in the session more than once, + * kill it as well. + */ + found = 0; + RB_FOREACH(loop, winlinks, &s->windows) { + if (loop->window == wl->window) + found++; + } + if (found > 1) + server_kill_window(wl->window, 0); + + server_renumber_all(); + return (CMD_RETURN_NORMAL); + } + + server_kill_window(wl->window, 1); return (CMD_RETURN_NORMAL); } diff --git a/server-fn.c b/server-fn.c index d66aed0b..84ec4123 100644 --- a/server-fn.c +++ b/server-fn.c @@ -183,7 +183,7 @@ server_kill_pane(struct window_pane *wp) struct window *w = wp->window; if (window_count_panes(w) == 1) { - server_kill_window(w); + server_kill_window(w, 1); recalculate_sizes(); } else { server_unzoom_window(w); @@ -195,19 +195,15 @@ server_kill_pane(struct window_pane *wp) } void -server_kill_window(struct window *w) +server_kill_window(struct window *w, int renumber) { - struct session *s, *next_s, *target_s; - struct session_group *sg; - struct winlink *wl; - - next_s = RB_MIN(sessions, &sessions); - while (next_s != NULL) { - s = next_s; - next_s = RB_NEXT(sessions, &sessions, s); + struct session *s, *s1; + struct winlink *wl; + RB_FOREACH_SAFE(s, sessions, &sessions, s1) { if (!session_has(s, w)) continue; + server_unzoom_window(w); while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { if (session_detach(s, wl)) { @@ -217,17 +213,35 @@ server_kill_window(struct window *w) server_redraw_session_group(s); } - if (options_get_number(s->options, "renumber-windows")) { - if ((sg = session_group_contains(s)) != NULL) { - TAILQ_FOREACH(target_s, &sg->sessions, gentry) - session_renumber_windows(target_s); - } else - session_renumber_windows(s); - } + if (renumber) + server_renumber_session(s); } recalculate_sizes(); } +void +server_renumber_session(struct session *s) +{ + struct session_group *sg; + + if (options_get_number(s->options, "renumber-windows")) { + if ((sg = session_group_contains(s)) != NULL) { + TAILQ_FOREACH(s, &sg->sessions, gentry) + session_renumber_windows(s); + } else + session_renumber_windows(s); + } +} + +void +server_renumber_all(void) +{ + struct session *s; + + RB_FOREACH(s, sessions, &sessions) + server_renumber_session(s); +} + int server_link_window(struct session *src, struct winlink *srcwl, struct session *dst, int dstidx, int killflag, int selectflag, @@ -354,7 +368,7 @@ server_destroy_pane(struct window_pane *wp, int notify) window_remove_pane(w, wp); if (TAILQ_EMPTY(&w->panes)) - server_kill_window(w); + server_kill_window(w, 1); else server_redraw_window(w); } diff --git a/tmux.h b/tmux.h index 0a1a740b..a8c67051 100644 --- a/tmux.h +++ b/tmux.h @@ -2420,7 +2420,9 @@ void server_lock(void); void server_lock_session(struct session *); void server_lock_client(struct client *); void server_kill_pane(struct window_pane *); -void server_kill_window(struct window *); +void server_kill_window(struct window *, int); +void server_renumber_session(struct session *); +void server_renumber_all(void); int server_link_window(struct session *, struct winlink *, struct session *, int, int, int, char **); void server_unlink_window(struct session *, struct winlink *); diff --git a/window-tree.c b/window-tree.c index 32b94e15..384cf392 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1054,7 +1054,7 @@ window_tree_kill_each(__unused void *modedata, void *itemdata, break; case WINDOW_TREE_WINDOW: if (wl != NULL) - server_kill_window(wl->window); + server_kill_window(wl->window, 1); break; case WINDOW_TREE_PANE: if (wp != NULL) From 2bf612a8066ab75725eeb09adf1ebc0f4fe851ab Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 6 Jul 2020 07:27:39 +0000 Subject: [PATCH 0542/1006] Always send xterm-style keys for M-Left and M-Right. GitHub issue 2296. --- cmd-list-keys.c | 1 + key-string.c | 58 +++++++++++++++++++++++++------------------------ tty-keys.c | 16 ++++++++++++++ 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 51c90dfe..b3bdbd12 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -164,6 +164,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "invalid key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } + only &= KEYC_MASK_KEY; } tablename = args_get(args, 'T'); diff --git a/key-string.c b/key-string.c index 6f4fd2ac..194fdef2 100644 --- a/key-string.c +++ b/key-string.c @@ -32,30 +32,30 @@ static const struct { key_code key; } key_string_table[] = { /* Function keys. */ - { "F1", KEYC_F1 }, - { "F2", KEYC_F2 }, - { "F3", KEYC_F3 }, - { "F4", KEYC_F4 }, - { "F5", KEYC_F5 }, - { "F6", KEYC_F6 }, - { "F7", KEYC_F7 }, - { "F8", KEYC_F8 }, - { "F9", KEYC_F9 }, - { "F10", KEYC_F10 }, - { "F11", KEYC_F11 }, - { "F12", KEYC_F12 }, - { "IC", KEYC_IC }, - { "Insert", KEYC_IC }, - { "DC", KEYC_DC }, - { "Delete", KEYC_DC }, - { "Home", KEYC_HOME }, - { "End", KEYC_END }, - { "NPage", KEYC_NPAGE }, - { "PageDown", KEYC_NPAGE }, - { "PgDn", KEYC_NPAGE }, - { "PPage", KEYC_PPAGE }, - { "PageUp", KEYC_PPAGE }, - { "PgUp", KEYC_PPAGE }, + { "F1", KEYC_F1|KEYC_IMPLIED_META }, + { "F2", KEYC_F2|KEYC_IMPLIED_META }, + { "F3", KEYC_F3|KEYC_IMPLIED_META }, + { "F4", KEYC_F4|KEYC_IMPLIED_META }, + { "F5", KEYC_F5|KEYC_IMPLIED_META }, + { "F6", KEYC_F6|KEYC_IMPLIED_META }, + { "F7", KEYC_F7|KEYC_IMPLIED_META }, + { "F8", KEYC_F8|KEYC_IMPLIED_META }, + { "F9", KEYC_F9|KEYC_IMPLIED_META }, + { "F10", KEYC_F10|KEYC_IMPLIED_META }, + { "F11", KEYC_F11|KEYC_IMPLIED_META }, + { "F12", KEYC_F12|KEYC_IMPLIED_META }, + { "IC", KEYC_IC|KEYC_IMPLIED_META }, + { "Insert", KEYC_IC|KEYC_IMPLIED_META }, + { "DC", KEYC_DC|KEYC_IMPLIED_META }, + { "Delete", KEYC_DC|KEYC_IMPLIED_META }, + { "Home", KEYC_HOME|KEYC_IMPLIED_META }, + { "End", KEYC_END|KEYC_IMPLIED_META }, + { "NPage", KEYC_NPAGE|KEYC_IMPLIED_META }, + { "PageDown", KEYC_NPAGE|KEYC_IMPLIED_META }, + { "PgDn", KEYC_NPAGE|KEYC_IMPLIED_META }, + { "PPage", KEYC_PPAGE|KEYC_IMPLIED_META }, + { "PageUp", KEYC_PPAGE|KEYC_IMPLIED_META }, + { "PgUp", KEYC_PPAGE|KEYC_IMPLIED_META }, { "Tab", '\011' }, { "BTab", KEYC_BTAB }, { "Space", ' ' }, @@ -64,10 +64,10 @@ static const struct { { "Escape", '\033' }, /* Arrow keys. */ - { "Up", KEYC_UP|KEYC_CURSOR }, - { "Down", KEYC_DOWN|KEYC_CURSOR }, - { "Left", KEYC_LEFT|KEYC_CURSOR }, - { "Right", KEYC_RIGHT|KEYC_CURSOR }, + { "Up", KEYC_UP|KEYC_CURSOR|KEYC_IMPLIED_META }, + { "Down", KEYC_DOWN|KEYC_CURSOR|KEYC_IMPLIED_META }, + { "Left", KEYC_LEFT|KEYC_CURSOR|KEYC_IMPLIED_META }, + { "Right", KEYC_RIGHT|KEYC_CURSOR|KEYC_IMPLIED_META }, /* Numeric keypad. */ { "KP/", KEYC_KP_SLASH|KEYC_KEYPAD }, @@ -233,6 +233,8 @@ key_string_lookup_string(const char *string) key = key_string_search_table(string); if (key == KEYC_UNKNOWN) return (KEYC_UNKNOWN); + if (~modifiers & KEYC_META) + key &= ~KEYC_IMPLIED_META; } /* Convert the standard control keys. */ diff --git a/tty-keys.c b/tty-keys.c index 4904ba35..19ad4f5b 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -95,18 +95,34 @@ 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 }, + /* Other (xterm) "cursor" keys. */ { "\033OH", KEYC_HOME }, { "\033OF", KEYC_END }, + { "\033\033OH", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, + { "\033\033OF", KEYC_END|KEYC_META|KEYC_IMPLIED_META }, + { "\033[H", KEYC_HOME }, { "\033[F", KEYC_END }, + { "\033\033[H", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, + { "\033\033[F", KEYC_END|KEYC_META|KEYC_IMPLIED_META }, + /* rxvt-style arrow + modifier keys. */ { "\033Oa", KEYC_UP|KEYC_CTRL }, { "\033Ob", KEYC_DOWN|KEYC_CTRL }, From 66d5e5de7ac6a81f638d1a2b2f5262368a68fee2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 6 Jul 2020 09:14:20 +0000 Subject: [PATCH 0543/1006] Add a way for control mode clients to subscribe to a format and be notified of changes rather than having to poll. GitHub issue 2242. --- cmd-refresh-client.c | 65 +++++-- control.c | 391 +++++++++++++++++++++++++++++++++++++++++++ server-client.c | 6 +- tmux.1 | 48 +++++- tmux.h | 12 ++ 5 files changed, 509 insertions(+), 13 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index f7b6269b..e55ce907 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -34,27 +34,62 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "A:cC:Df:F:lLRSt:U", 0, 1 }, - .usage = "[-cDlLRSU] [-A pane:state] [-C XxY] [-f flags] " - CMD_TARGET_CLIENT_USAGE " [adjustment]", + .args = { "A:B:cC:Df:F:lLRSt:U", 0, 1 }, + .usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] " + "[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_refresh_client_exec }; +static void +cmd_refresh_client_update_subscription(struct client *tc, const char *value) +{ + char *copy, *split, *name, *what; + enum control_sub_type subtype; + int subid = -1; + + copy = name = xstrdup(value); + if ((split = strchr(copy, ':')) == NULL) { + control_remove_sub(tc, copy); + goto out; + } + *split++ = '\0'; + + what = split; + if ((split = strchr(what, ':')) == NULL) + goto out; + *split++ = '\0'; + + if (strcmp(what, "%*") == 0) + subtype = CONTROL_SUB_ALL_PANES; + else if (sscanf(what, "%%%d", &subid) == 1 && subid >= 0) + subtype = CONTROL_SUB_PANE; + else if (strcmp(what, "@*") == 0) + subtype = CONTROL_SUB_ALL_WINDOWS; + else if (sscanf(what, "@%d", &subid) == 1 && subid >= 0) + subtype = CONTROL_SUB_WINDOW; + else + subtype = CONTROL_SUB_SESSION; + control_add_sub(tc, name, subtype, subid, split); + +out: + free(copy); +} + static void cmd_refresh_client_update_offset(struct client *tc, const char *value) { struct window_pane *wp; - char *copy, *colon; + char *copy, *split; u_int pane; if (*value != '%') return; copy = xstrdup(value); - if ((colon = strchr(copy, ':')) == NULL) + if ((split = strchr(copy, ':')) == NULL) goto out; - *colon++ = '\0'; + *split++ = '\0'; if (sscanf(copy, "%%%u", &pane) != 1) goto out; @@ -62,13 +97,13 @@ cmd_refresh_client_update_offset(struct client *tc, const char *value) if (wp == NULL) goto out; - if (strcmp(colon, "on") == 0) + if (strcmp(split, "on") == 0) control_set_pane_on(tc, wp); - else if (strcmp(colon, "off") == 0) + else if (strcmp(split, "off") == 0) control_set_pane_off(tc, wp); - else if (strcmp(colon, "continue") == 0) + else if (strcmp(split, "continue") == 0) control_continue_pane(tc, wp); - else if (strcmp(colon, "pause") == 0) + else if (strcmp(split, "pause") == 0) control_pause_pane(tc, wp); out: @@ -156,6 +191,16 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) } return (CMD_RETURN_NORMAL); } + if (args_has(args, 'B')) { + if (~tc->flags & CLIENT_CONTROL) + goto not_control_client; + value = args_first_value(args, 'B', &av); + while (value != NULL) { + cmd_refresh_client_update_subscription(tc, value); + value = args_next_value(&av); + } + return (CMD_RETURN_NORMAL); + } if (args_has(args, 'C')) { if (~tc->flags & CLIENT_CONTROL) goto not_control_client; diff --git a/control.c b/control.c index 5681d2dc..c52f2020 100644 --- a/control.c +++ b/control.c @@ -76,6 +76,42 @@ struct control_pane { }; RB_HEAD(control_panes, control_pane); +/* Subscription pane. */ +struct control_sub_pane { + u_int pane; + u_int idx; + char *last; + + RB_ENTRY(control_sub_pane) entry; +}; +RB_HEAD(control_sub_panes, control_sub_pane); + +/* Subscription window. */ +struct control_sub_window { + u_int window; + u_int idx; + char *last; + + RB_ENTRY(control_sub_window) entry; +}; +RB_HEAD(control_sub_windows, control_sub_window); + +/* Control client subscription. */ +struct control_sub { + char *name; + char *format; + + enum control_sub_type type; + u_int id; + + char *last; + struct control_sub_panes panes; + struct control_sub_windows windows; + + RB_ENTRY(control_sub) entry; +}; +RB_HEAD(control_subs, control_sub); + /* Control client state. */ struct control_state { struct control_panes panes; @@ -87,6 +123,9 @@ struct control_state { struct bufferevent *read_event; struct bufferevent *write_event; + + struct control_subs subs; + struct event subs_timer; }; /* Low and high watermarks. */ @@ -116,6 +155,73 @@ control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2) } RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp); +/* Compare client subs. */ +static int +control_sub_cmp(struct control_sub *csub1, struct control_sub *csub2) +{ + return (strcmp(csub1->name, csub2->name)); +} +RB_GENERATE_STATIC(control_subs, control_sub, entry, control_sub_cmp); + +/* Compare client subscription panes. */ +static int +control_sub_pane_cmp(struct control_sub_pane *csp1, + struct control_sub_pane *csp2) +{ + if (csp1->pane < csp2->pane) + return (-1); + if (csp1->pane > csp2->pane) + return (1); + if (csp1->idx < csp2->idx) + return (-1); + if (csp1->idx > csp2->idx) + return (1); + return (0); +} +RB_GENERATE_STATIC(control_sub_panes, control_sub_pane, entry, + control_sub_pane_cmp); + +/* Compare client subscription windows. */ +static int +control_sub_window_cmp(struct control_sub_window *csw1, + struct control_sub_window *csw2) +{ + if (csw1->window < csw2->window) + return (-1); + if (csw1->window > csw2->window) + return (1); + if (csw1->idx < csw2->idx) + return (-1); + if (csw1->idx > csw2->idx) + return (1); + return (0); +} +RB_GENERATE_STATIC(control_sub_windows, control_sub_window, entry, + control_sub_window_cmp); + +/* Free a subscription. */ +static void +control_free_sub(struct control_state *cs, struct control_sub *csub) +{ + struct control_sub_pane *csp, *csp1; + struct control_sub_window *csw, *csw1; + + RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) { + RB_REMOVE(control_sub_panes, &csub->panes, csp); + free(csp); + } + RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) { + RB_REMOVE(control_sub_windows, &csub->windows, csw); + free(csw); + } + free(csub->last); + + RB_REMOVE(control_subs, &cs->subs, csub); + free(csub->name); + free(csub->format); + free(csub); +} + /* Free a block. */ static void control_free_block(struct control_state *cs, struct control_block *cb) @@ -666,6 +772,7 @@ control_start(struct client *c) RB_INIT(&cs->panes); TAILQ_INIT(&cs->pending_list); TAILQ_INIT(&cs->all_blocks); + RB_INIT(&cs->subs); cs->read_event = bufferevent_new(c->fd, control_read_callback, control_write_callback, control_error_callback, c); @@ -704,14 +811,298 @@ control_stop(struct client *c) { struct control_state *cs = c->control_state; struct control_block *cb, *cb1; + struct control_sub *csub, *csub1; if (~c->flags & CLIENT_CONTROLCONTROL) bufferevent_free(cs->write_event); bufferevent_free(cs->read_event); + RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) + control_free_sub(cs, csub); + if (evtimer_initialized(&cs->subs_timer)) + evtimer_del(&cs->subs_timer); + TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) control_free_block(cs, cb); control_reset_offsets(c); free(cs); } + +/* Check session subscription. */ +static void +control_check_subs_session(struct client *c, struct control_sub *csub) +{ + struct session *s = c->session; + struct format_tree *ft; + char *value; + + ft = format_create_defaults(NULL, c, s, NULL, NULL); + value = format_expand(ft, csub->format); + format_free(ft); + + if (csub->last != NULL && strcmp(value, csub->last) == 0) { + free(value); + return; + } + control_write(c, + "%%subscription-changed %s $%u - - - : %s", + csub->name, s->id, value); + free(csub->last); + csub->last = value; +} + +/* Check pane subscription. */ +static void +control_check_subs_pane(struct client *c, struct control_sub *csub) +{ + struct session *s = c->session; + struct window_pane *wp; + struct window *w; + struct winlink *wl; + struct format_tree *ft; + char *value; + struct control_sub_pane *csp, find; + + wp = window_pane_find_by_id(csub->id); + if (wp == NULL) + return; + w = wp->window; + + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + if (wl->session != s) + continue; + + ft = format_create_defaults(NULL, c, s, wl, wp); + value = format_expand(ft, csub->format); + format_free(ft); + + find.pane = wp->id; + find.idx = wl->idx; + + csp = RB_FIND(control_sub_panes, &csub->panes, &find); + if (csp == NULL) { + csp = xcalloc(1, sizeof *csp); + csp->pane = wp->id; + csp->idx = wl->idx; + RB_INSERT(control_sub_panes, &csub->panes, csp); + } + + if (csp->last != NULL && strcmp(value, csp->last) == 0) { + free(value); + continue; + } + control_write(c, + "%%subscription-changed %s $%u @%u %u %%%u : %s", + csub->name, s->id, w->id, wl->idx, wp->id, value); + free(csp->last); + csp->last = value; + } +} + +/* Check all panes subscription. */ +static void +control_check_subs_all_panes(struct client *c, struct control_sub *csub) +{ + struct session *s = c->session; + struct window_pane *wp; + struct window *w; + struct winlink *wl; + struct format_tree *ft; + char *value; + struct control_sub_pane *csp, find; + + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + TAILQ_FOREACH(wp, &w->panes, entry) { + ft = format_create_defaults(NULL, c, s, wl, wp); + value = format_expand(ft, csub->format); + format_free(ft); + + find.pane = wp->id; + find.idx = wl->idx; + + csp = RB_FIND(control_sub_panes, &csub->panes, &find); + if (csp == NULL) { + csp = xcalloc(1, sizeof *csp); + csp->pane = wp->id; + csp->idx = wl->idx; + RB_INSERT(control_sub_panes, &csub->panes, csp); + } + + if (csp->last != NULL && + strcmp(value, csp->last) == 0) { + free(value); + continue; + } + control_write(c, + "%%subscription-changed %s $%u @%u %u %%%u : %s", + csub->name, s->id, w->id, wl->idx, wp->id, value); + free(csp->last); + csp->last = value; + } + } +} + +/* Check window subscription. */ +static void +control_check_subs_window(struct client *c, struct control_sub *csub) +{ + struct session *s = c->session; + struct window *w; + struct winlink *wl; + struct format_tree *ft; + char *value; + struct control_sub_window *csw, find; + + w = window_find_by_id(csub->id); + if (w == NULL) + return; + + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + if (wl->session != s) + continue; + + ft = format_create_defaults(NULL, c, s, wl, NULL); + value = format_expand(ft, csub->format); + format_free(ft); + + find.window = w->id; + find.idx = wl->idx; + + csw = RB_FIND(control_sub_windows, &csub->windows, &find); + if (csw == NULL) { + csw = xcalloc(1, sizeof *csw); + csw->window = w->id; + csw->idx = wl->idx; + RB_INSERT(control_sub_windows, &csub->windows, csw); + } + + if (csw->last != NULL && strcmp(value, csw->last) == 0) { + free(value); + continue; + } + control_write(c, + "%%subscription-changed %s $%u @%u %u - : %s", + csub->name, s->id, w->id, wl->idx, value); + free(csw->last); + csw->last = value; + } +} + +/* Check all windows subscription. */ +static void +control_check_subs_all_windows(struct client *c, struct control_sub *csub) +{ + struct session *s = c->session; + struct window *w; + struct winlink *wl; + struct format_tree *ft; + char *value; + struct control_sub_window *csw, find; + + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + + ft = format_create_defaults(NULL, c, s, wl, NULL); + value = format_expand(ft, csub->format); + format_free(ft); + + find.window = w->id; + find.idx = wl->idx; + + csw = RB_FIND(control_sub_windows, &csub->windows, &find); + if (csw == NULL) { + csw = xcalloc(1, sizeof *csw); + csw->window = w->id; + csw->idx = wl->idx; + RB_INSERT(control_sub_windows, &csub->windows, csw); + } + + if (csw->last != NULL && strcmp(value, csw->last) == 0) { + free(value); + continue; + } + control_write(c, + "%%subscription-changed %s $%u @%u %u - : %s", + csub->name, s->id, w->id, wl->idx, value); + free(csw->last); + csw->last = value; + } +} + +/* Check subscriptions timer. */ +static void +control_check_subs_timer(__unused int fd, __unused short events, void *data) +{ + struct client *c = data; + struct control_state *cs = c->control_state; + struct control_sub *csub, *csub1; + struct timeval tv = { .tv_sec = 1 }; + + log_debug("%s: timer fired", __func__); + evtimer_add(&cs->subs_timer, &tv); + + RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) { + switch (csub->type) { + case CONTROL_SUB_SESSION: + control_check_subs_session(c, csub); + break; + case CONTROL_SUB_PANE: + control_check_subs_pane(c, csub); + break; + case CONTROL_SUB_ALL_PANES: + control_check_subs_all_panes(c, csub); + break; + case CONTROL_SUB_WINDOW: + control_check_subs_window(c, csub); + break; + case CONTROL_SUB_ALL_WINDOWS: + control_check_subs_all_windows(c, csub); + break; + } + } +} + +/* Add a subscription. */ +void +control_add_sub(struct client *c, const char *name, enum control_sub_type type, + int id, const char *format) +{ + struct control_state *cs = c->control_state; + struct control_sub *csub, find; + struct timeval tv = { .tv_sec = 1 }; + + find.name = (char *)name; + if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL) + control_free_sub(cs, csub); + + csub = xcalloc(1, sizeof *csub); + csub->name = xstrdup(name); + csub->type = type; + csub->id = id; + csub->format = xstrdup(format); + RB_INSERT(control_subs, &cs->subs, csub); + + RB_INIT(&csub->panes); + RB_INIT(&csub->windows); + + if (!evtimer_initialized(&cs->subs_timer)) + evtimer_set(&cs->subs_timer, control_check_subs_timer, c); + if (!evtimer_pending(&cs->subs_timer, NULL)) + evtimer_add(&cs->subs_timer, &tv); +} + +/* Remove a subscription. */ +void +control_remove_sub(struct client *c, const char *name) +{ + struct control_state *cs = c->control_state; + struct control_sub *csub, find; + + find.name = (char *)name; + if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL) + control_free_sub(cs, csub); + if (RB_EMPTY(&cs->subs)) + evtimer_del(&cs->subs_timer); +} diff --git a/server-client.c b/server-client.c index d86a9fb8..3a79a5d1 100644 --- a/server-client.c +++ b/server-client.c @@ -1474,11 +1474,13 @@ server_client_check_pane_resize(struct window_pane *wp) * Otherwise resize to the force size and start the timer. */ if (wp->flags & PANE_RESIZENOW) { - log_debug("%s: resizing %%%u after forced resize", __func__, wp->id); + log_debug("%s: resizing %%%u after forced resize", + __func__, wp->id); window_pane_send_resize(wp, 0); wp->flags &= ~(PANE_RESIZE|PANE_RESIZEFORCE|PANE_RESIZENOW); } else if (!evtimer_pending(&wp->force_timer, NULL)) { - log_debug("%s: forcing resize of %%%u", __func__, wp->id); + log_debug("%s: forcing resize of %%%u", __func__, + wp->id); window_pane_send_resize(wp, 1); server_client_start_force_timer(wp); } diff --git a/tmux.1 b/tmux.1 index 4d079853..1c02bdda 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1255,6 +1255,7 @@ specified multiple times. .It Xo Ic refresh-client .Op Fl cDlLRSU .Op Fl A Ar pane:state +.Op Fl B Ar name:what:format .Op Fl C Ar XxY .Op Fl f Ar flags .Op Fl t Ar target-client @@ -1328,6 +1329,31 @@ will pause the pane. .Fl A may be given multiple times for different panes. .Pp +.Fl B +sets a subscription to a format for a control mode client. +The argument is split into three items by colons: +.Ar name +is a name for the subscription; +.Ar what +is a type of item to subscribe to; +.Ar format +is the format. +After a subscription is added, changes to the format are reported with the +.Ic %subscription-changed +notification, at most once a second. +If only the name is given, the subscription is removed. +.Ar what +may be empty to check the format only for the attached session, or one of: +a pane ID such as +.Ql %0 ; +.Ql %* +for all panes in the attached session; +an window ID such as +.Ql @0 ; +or +.Ql @* +for all windows in the attached session. +.Pp .Fl f sets a comma-separated list of client flags, see .Ic attach-session . @@ -5932,7 +5958,7 @@ or an error occurred. If present, .Ar reason describes why the client exited. -.It Ic %extended-output Ar pane-id Ar age Ar ... : Ar value +.It Ic %extended-output Ar pane-id Ar age Ar ... \& : Ar value New form of .Ic %output sent when the @@ -5980,6 +6006,26 @@ changed its active window to the window with ID .Ar window-id . .It Ic %sessions-changed A session was created or destroyed. +.It Xo Ic %subscription-changed +.Ar name +.Ar session-id +.Ar window-id +.Ar window-index +.Ar pane-id ... \& : +.Ar value +.Xc +The value of the format associated with subscription +.Ar name +has changed to +.Ar value . +See +.Ic refresh-client +.Fl B . +Any arguments after +.Ar pane-id +up until a single +.Ql \&: +are for future use and should be ignored. .It Ic %unlinked-window-add Ar window-id The window with ID .Ar window-id diff --git a/tmux.h b/tmux.h index a8c67051..07dfc0ae 100644 --- a/tmux.h +++ b/tmux.h @@ -1722,6 +1722,15 @@ struct client { }; TAILQ_HEAD(clients, client); +/* Control mode subscription type. */ +enum control_sub_type { + CONTROL_SUB_SESSION, + CONTROL_SUB_PANE, + CONTROL_SUB_ALL_PANES, + CONTROL_SUB_WINDOW, + CONTROL_SUB_ALL_WINDOWS +}; + /* Key binding and key table. */ struct key_binding { key_code key; @@ -2862,6 +2871,9 @@ void control_reset_offsets(struct client *); void printflike(2, 3) control_write(struct client *, const char *, ...); void control_write_output(struct client *, struct window_pane *); int control_all_done(struct client *); +void control_add_sub(struct client *, const char *, enum control_sub_type, + int, const char *); +void control_remove_sub(struct client *, const char *); /* control-notify.c */ void control_notify_input(struct client *, struct window_pane *, From 2aa177d102b573e5802f69885e738e554d350e7e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 6 Jul 2020 10:07:02 +0000 Subject: [PATCH 0544/1006] Do not eliminate redundant clears, the code is wrong and doing it correctly wouldn't be worth it. GitHub issue 2298. --- screen-write.c | 61 ++++++++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/screen-write.c b/screen-write.c index 4c328ca2..16a9c1d7 100644 --- a/screen-write.c +++ b/screen-write.c @@ -25,10 +25,10 @@ static void screen_write_collect_clear(struct screen_write_ctx *, u_int, u_int); -static int screen_write_collect_clear_end(struct screen_write_ctx *, u_int, +static void screen_write_collect_clear_end(struct screen_write_ctx *, u_int, + u_int); +static void screen_write_collect_clear_start(struct screen_write_ctx *, u_int, u_int); -static int screen_write_collect_clear_start(struct screen_write_ctx *, - u_int, u_int, u_int); static void screen_write_collect_scroll(struct screen_write_ctx *); static void screen_write_collect_flush(struct screen_write_ctx *, int, const char *); @@ -1127,13 +1127,12 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); - if (!screen_write_collect_clear_end(ctx, s->cy, s->cx, bg)) { - ci->x = s->cx; - ci->type = CLEAR_END; - ci->bg = bg; - TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); - ctx->item = xcalloc(1, sizeof *ctx->item); - } + screen_write_collect_clear_end(ctx, s->cy, s->cx); + ci->x = s->cx; + ci->type = CLEAR_END; + ci->bg = bg; + TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); + ctx->item = xcalloc(1, sizeof *ctx->item); } /* Clear to start of line from cursor. */ @@ -1154,13 +1153,12 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) else grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); - if (!screen_write_collect_clear_start(ctx, s->cy, s->cx, bg)) { - ci->x = s->cx; - ci->type = CLEAR_START; - ci->bg = bg; - TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); - ctx->item = xcalloc(1, sizeof *ctx->item); - } + screen_write_collect_clear_start(ctx, s->cy, s->cx); + ci->x = s->cx; + ci->type = CLEAR_START; + ci->bg = bg; + TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); + ctx->item = xcalloc(1, sizeof *ctx->item); } /* Move cursor to px,py. */ @@ -1393,25 +1391,18 @@ screen_write_clearhistory(struct screen_write_ctx *ctx) } /* Clear to start of a collected line. */ -static int -screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x, - u_int bg) +static void +screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x) { struct screen_write_collect_item *ci, *tmp; size_t size = 0; u_int items = 0; - int redundant = 0; if (TAILQ_EMPTY(&ctx->s->write_list[y].items)) - return (0); + return; TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) { switch (ci->type) { case CLEAR_START: - if (ci->x >= x) { - if (ci->bg == bg) - redundant = 1; - continue; - } break; case CLEAR_END: if (ci->x <= x) @@ -1430,21 +1421,18 @@ screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x, ctx->skipped += size; log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items, size, y); - return (redundant); } /* Clear to end of a collected line. */ -static int -screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x, - u_int bg) +static void +screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x) { struct screen_write_collect_item *ci, *tmp; size_t size = 0; - int redundant = 0; u_int items = 0; if (TAILQ_EMPTY(&ctx->s->write_list[y].items)) - return (0); + return; TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) { switch (ci->type) { case CLEAR_START: @@ -1452,11 +1440,6 @@ screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x, ci->x = x; continue; case CLEAR_END: - if (ci->x <= x) { - if (ci->bg == bg) - redundant = 1; - continue; - } break; case TEXT: if (ci->x < x) @@ -1471,7 +1454,6 @@ screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x, ctx->skipped += size; log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items, size, y); - return (redundant); } /* Clear collected lines. */ @@ -1566,6 +1548,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { screen_write_set_cursor(ctx, ci->x, y); if (ci->type == CLEAR_END) { + log_debug("XXX %u %u", ci->x, ci->bg); screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; tty_write(tty_cmd_clearendofline, &ttyctx); From b30989a9649699ba08908a1a9aa08b0ecc7b24d1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 6 Jul 2020 14:03:33 +0100 Subject: [PATCH 0545/1006] Pull 3.2-rc up to master. --- CHANGES | 3 +++ configure.ac | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 1373daba..cf00a5b0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,8 @@ CHANGES FROM 3.1b 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. + * Add some formats for search in copy mode (search_present, search_match). * Do not wait on shutdown for commands started with run -b. diff --git a/configure.ac b/configure.ac index a62029b3..b698526d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], next-3.3) +AC_INIT([tmux], 3.2-rc) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From a5f99e14c6f264e568b860692b89d11f5298a3f2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 6 Jul 2020 14:07:11 +0100 Subject: [PATCH 0546/1006] Update version. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b698526d..a62029b3 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.2-rc) +AC_INIT([tmux], next-3.3) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From f26b7b7788bdeea4d523ac1699b845350165644e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2020 07:04:17 +0000 Subject: [PATCH 0547/1006] Clarify /tmp permissions and use, GitHub issue 2300. --- tmux.1 | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 1c02bdda..77cb3c56 100644 --- a/tmux.1 +++ b/tmux.1 @@ -169,8 +169,17 @@ independent servers to be run. Unlike .Fl S -a full path is not necessary: the sockets are all created in the same -directory. +a full path is not necessary: the sockets are all created in a directory +.Pa tmux-UID +under the directory given by +.Ev TMUX_TMPDIR +or in +.Pa /tmp . +The +.Pa tmux-UID +directory is created by +.Nm +and must not be world readable, writable or executable. .Pp If the socket is accidentally removed, the .Dv SIGUSR1 From 468be2a37f6185b43e6fa08cd1940abd040a7c03 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2020 10:10:10 +0000 Subject: [PATCH 0548/1006] Do not dereference NULL environment variable value, GitHub issue 2304. --- cmd-parse.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-parse.y b/cmd-parse.y index 0a0b1993..c8995d8b 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -1432,7 +1432,7 @@ yylex_token_variable(char **buf, size_t *len) name[namelen] = '\0'; envent = environ_find(global_environ, name); - if (envent != NULL) { + if (envent != NULL && envent->value != NULL) { value = envent->value; log_debug("%s: %s -> %s", __func__, name, value); yylex_append(buf, len, value, strlen(value)); From 8f1179d65685b8f470f91194c685a008422d49e7 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Jul 2020 10:09:54 +0000 Subject: [PATCH 0549/1006] Handle padding cells correctly when searching, GitHub issue 2301. --- window-copy.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/window-copy.c b/window-copy.c index 7b7ab72c..7103131d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2601,6 +2601,11 @@ window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size, } gce = &gl->celldata[px]; + if (gce->flags & GRID_FLAG_PADDING) { + *size = 0; + *allocated = 0; + return (NULL); + } if (~gce->flags & GRID_FLAG_EXTENDED) { *size = 1; *allocated = 0; From 5e008eefaa5a20f8aaed2f8cc9890b24efaffe28 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Jul 2020 11:03:17 +0000 Subject: [PATCH 0550/1006] Renumber after killing windows for choose-tree. --- window-tree.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/window-tree.c b/window-tree.c index 384cf392..a687af16 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1054,7 +1054,7 @@ window_tree_kill_each(__unused void *modedata, void *itemdata, break; case WINDOW_TREE_WINDOW: if (wl != NULL) - server_kill_window(wl->window, 1); + server_kill_window(wl->window, 0); break; case WINDOW_TREE_PANE: if (wp != NULL) @@ -1076,6 +1076,7 @@ window_tree_kill_current_callback(struct client *c, void *modedata, return (0); window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE); + server_renumber_all(); data->references++; cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); @@ -1096,6 +1097,7 @@ window_tree_kill_tagged_callback(struct client *c, void *modedata, return (0); mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1); + server_renumber_all(); data->references++; cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); From 3b089fc69f3b111797e36bd2a881fff6b8cb5213 Mon Sep 17 00:00:00 2001 From: daniel Date: Sat, 18 Jul 2020 02:53:47 +0000 Subject: [PATCH 0551/1006] Properly escape a backslash. Found by CompCert which notes that \E is not a valid escape sequence. ok nicm@ --- tty-features.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tty-features.c b/tty-features.c index ea662bb4..5891e2c3 100644 --- a/tty-features.c +++ b/tty-features.c @@ -110,8 +110,8 @@ static const struct tty_feature tty_feature_overline = { /* Terminal supports underscore styles. */ 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", + "Smulx=\\E[4::%p1%dm", + "Setulc=\\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m", NULL }; static const struct tty_feature tty_feature_usstyle = { From 743ab5728da1ce950a3782d5bc08831f61c5d744 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Jul 2020 05:24:33 +0000 Subject: [PATCH 0552/1006] Fix show-buffer when run from inside tmux, GitHub issue 2314. --- cmd-save-buffer.c | 14 +++++++++++--- screen-write.c | 5 ++++- tmux.h | 1 + utf8.c | 14 ++++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index a6ad188f..5e45e279 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -74,11 +74,12 @@ static enum cmd_retval cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *c = cmdq_get_client(item); struct paste_buffer *pb; int flags; const char *bufname = args_get(args, 'b'), *bufdata; size_t bufsize; - char *path; + char *path, *tmp; if (bufname == NULL) { if ((pb = paste_get_top(NULL)) == NULL) { @@ -94,9 +95,16 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) } bufdata = paste_buffer_data(pb, &bufsize); - if (cmd_get_entry(self) == &cmd_show_buffer_entry) + if (cmd_get_entry(self) == &cmd_show_buffer_entry) { + if (c->session != NULL || (c->flags & CLIENT_CONTROL)) { + utf8_stravisx(&tmp, bufdata, bufsize, + VIS_OCTAL|VIS_CSTYLE|VIS_TAB); + cmdq_print(item, "%s", tmp); + free(tmp); + return (CMD_RETURN_NORMAL); + } path = xstrdup("-"); - else + } else path = format_single_from_target(item, args->argv[0]); if (args_has(args, 'a')) flags = O_APPEND; diff --git a/screen-write.c b/screen-write.c index 16a9c1d7..c9c61086 100644 --- a/screen-write.c +++ b/screen-write.c @@ -514,7 +514,10 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, if (*ptr == '\001') gc.attr ^= GRID_ATTR_CHARSET; - else if (*ptr > 0x1f && *ptr < 0x7f) { + else if (*ptr == '\n') { + screen_write_linefeed(ctx, 0, 8); + screen_write_carriagereturn(ctx); + } else if (*ptr > 0x1f && *ptr < 0x7f) { size++; screen_write_putc(ctx, &gc, *ptr); } diff --git a/tmux.h b/tmux.h index 07dfc0ae..a6749e99 100644 --- a/tmux.h +++ b/tmux.h @@ -2940,6 +2940,7 @@ enum utf8_state utf8_append(struct utf8_data *, u_char); int utf8_isvalid(const char *); int utf8_strvis(char *, const char *, size_t, int); int utf8_stravis(char **, const char *, int); +int utf8_stravisx(char **, const char *, size_t, int); char *utf8_sanitize(const char *); size_t utf8_strlen(const struct utf8_data *); u_int utf8_strwidth(const struct utf8_data *, ssize_t); diff --git a/utf8.c b/utf8.c index e3fe570d..36c9daad 100644 --- a/utf8.c +++ b/utf8.c @@ -341,6 +341,20 @@ utf8_stravis(char **dst, const char *src, int flag) return (len); } +/* Same as utf8_strvis but allocate the buffer. */ +int +utf8_stravisx(char **dst, const char *src, size_t srclen, int flag) +{ + char *buf; + int len; + + buf = xreallocarray(NULL, 4, srclen + 1); + len = utf8_strvis(buf, src, srclen, flag); + + *dst = xrealloc(buf, len + 1); + return (len); +} + /* Does this string contain anything that isn't valid UTF-8? */ int utf8_isvalid(const char *s) From 1f5e520def5a66ad47e7e0a8755f2952241096e5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Jul 2020 06:21:46 +0000 Subject: [PATCH 0553/1006] Correct checks for window borders. --- screen-redraw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 3391a891..50df2671 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -254,7 +254,7 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, int borders = 0; /* Is this outside the window? */ - if (px >= sx || py >= sy) + if (px > sx || py > sy) return (CELL_OUTSIDE); /* @@ -330,7 +330,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, *wpp = NULL; - if (px >= w->sx || py >= w->sy) + if (px > w->sx || py > w->sy) return (CELL_OUTSIDE); if (px == w->sx || py == w->sy) /* window border */ return (screen_redraw_type_of_cell(c, px, py, pane_status)); From 112b0f417c43e3f9ba7f5aa9df2e49dfdddfc225 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 23 Jul 2020 14:17:56 +0000 Subject: [PATCH 0554/1006] Check all lists if option not found already. --- window-customize.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/window-customize.c b/window-customize.c index f60d2e6e..ecafc776 100644 --- a/window-customize.c +++ b/window-customize.c @@ -403,9 +403,9 @@ window_customize_build_options(struct window_customize_modedata *data, for (i = 0; i < size; i++) { if (oo2 != NULL) o = options_get(oo0, list[i]); - else if (oo1 != NULL) + if (o == NULL && oo1 != NULL) o = options_get(oo1, list[i]); - else + if (o == NULL) o = options_get(oo2, list[i]); if (options_owner(o) == oo2) scope = scope2; From d329b035cee47d968a8c93b5cbd8fde879ce6f0d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 24 Jul 2020 07:05:37 +0000 Subject: [PATCH 0555/1006] 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 31bc4c4346a809188a908fea73a212dd71c8b096 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Jul 2020 08:38:34 +0100 Subject: [PATCH 0556/1006] 3.2-rc version. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index a62029b3..07f9ae06 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], next-3.3) +AC_INIT([tmux], 3.2-rc2) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 40e65c511502fe47932e230290537e7391ab8a83 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jul 2020 08:03:10 +0000 Subject: [PATCH 0557/1006] 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 0558/1006] 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 0559/1006] 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 0560/1006] 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 0561/1006] 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 0562/1006] 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 0563/1006] 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 0564/1006] 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 0565/1006] 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 0566/1006] 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 0567/1006] 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 0568/1006] 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 0569/1006] 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 0570/1006] 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 0571/1006] 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 0572/1006] 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 0573/1006] 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 0574/1006] 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 0575/1006] 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 0576/1006] 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 0577/1006] 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 0578/1006] 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 0579/1006] 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 0580/1006] 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 0581/1006] 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 0582/1006] 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 0583/1006] 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 0584/1006] 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 0585/1006] 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 0586/1006] 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 0587/1006] 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 0588/1006] 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 0589/1006] 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 9d597390caf911cb23a2a748c1159a1cbc50de84 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 25 Sep 2020 09:41:41 +0100 Subject: [PATCH 0590/1006] 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 a8802371417ac5c41260fc3798c7a3a7abbfacd2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 25 Sep 2020 09:43:35 +0100 Subject: [PATCH 0591/1006] 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 f70eda38175af89e05bf7b959128829375830143 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 25 Sep 2020 10:04:52 +0100 Subject: [PATCH 0592/1006] 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 07f9ae06..22a48c8d 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 68c2d5c48dbb5e3ca40f7271f43a4dc2165068d7 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 23 Sep 2020 14:57:33 +0000 Subject: [PATCH 0593/1006] 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 19ad4f5b..941a9005 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 3bece648bd6f285959f90e58016fd9a636735e58 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 30 Sep 2020 13:26:31 +0100 Subject: [PATCH 0594/1006] 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 ec9e03d09a20ca00dd9d96f7938a2d03a504b535 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 24 Aug 2020 05:23:30 +0000 Subject: [PATCH 0595/1006] 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 941a9005..59426772 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1197,7 +1197,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) @@ -1206,7 +1209,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); @@ -1224,6 +1227,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 cf8ef63c4a5cbbc29f655a076b2e57c144ad8316 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 30 Sep 2020 13:35:51 +0100 Subject: [PATCH 0596/1006] Fix some warnings, GitHub issue 2382. --- format.c | 5 ----- menu.c | 2 +- utf8.c | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/format.c b/format.c index c7c6b12e..ced7b515 100644 --- a/format.c +++ b/format.c @@ -1372,7 +1372,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) @@ -1396,10 +1395,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/utf8.c b/utf8.c index e640d845..458363b8 100644 --- a/utf8.c +++ b/utf8.c @@ -100,9 +100,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); } @@ -117,8 +117,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); } From f43e3e5b4f4df398df39cf870e5c6b0ef2fec0ec Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 30 Sep 2020 13:36:58 +0100 Subject: [PATCH 0597/1006] 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 0598/1006] 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 0599/1006] 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 0600/1006] 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 0601/1006] 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 0602/1006] 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 0603/1006] 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 0604/1006] 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 0605/1006] 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 0606/1006] 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 0607/1006] 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 0608/1006] 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 0609/1006] 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 0610/1006] 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 0611/1006] 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 0612/1006] 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 0613/1006] 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 3c298b98ce92a69573241c87846a1a7610ce68e4 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 26 Oct 2020 19:00:37 +0000 Subject: [PATCH 0614/1006] 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 ebc39386..a9aa070a 100644 --- a/proc.c +++ b/proc.c @@ -282,6 +282,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 7ffb41429955b714f8b872fe375360e615a05003 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 19 Oct 2020 06:39:28 +0000 Subject: [PATCH 0615/1006] 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 07ffed8b6fe39ccd8b0ff675e40643d9f0d86f91 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Oct 2020 10:15:23 +0000 Subject: [PATCH 0616/1006] 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 f11bb430..4c4d4ba5 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -370,7 +370,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 977cf3cf697153c827724988db9e715cf4af2a67 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Oct 2020 07:29:24 +0000 Subject: [PATCH 0617/1006] 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 9ad88597..f320db89 100644 --- a/tty-term.c +++ b/tty-term.c @@ -580,6 +580,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 7a4aa146187cc60d2df66333b3e7dd5a5176f793 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 28 Oct 2020 10:09:10 +0000 Subject: [PATCH 0618/1006] 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 5749ba94..e9587e4c 100644 --- a/server-client.c +++ b/server-client.c @@ -2237,7 +2237,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); #endif - 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) { @@ -2252,13 +2252,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 0619/1006] 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 733abfcfc5b05cb3e1f2cf08f00a9325c6f6fa04 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 29 Oct 2020 16:33:01 +0000 Subject: [PATCH 0620/1006] 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 a3850371..60bfa8e2 100644 --- a/input.c +++ b/input.c @@ -1975,8 +1975,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 0621/1006] 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 0622/1006] 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 0623/1006] 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 0624/1006] 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 0625/1006] 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 0626/1006] 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 0627/1006] 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 0628/1006] 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 0629/1006] 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 From 9d83c5e94827db0248226149cffee23296c851aa Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 3 Nov 2020 08:09:35 +0000 Subject: [PATCH 0630/1006] Expand menu and popup -x and -y as a format, from teo_paul1 at yahoo dot com in GitHub issue 2442. --- cmd-display-menu.c | 254 ++++++++++++++++++++++++++++++--------------- tmux.1 | 22 ++++ 2 files changed, 190 insertions(+), 86 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 205d1243..7115dc81 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -61,7 +61,7 @@ const struct cmd_entry cmd_display_popup_entry = { .exec = cmd_display_popup_exec }; -static void +static int cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, struct args *args, u_int *px, u_int *py, u_int w, u_int h) { @@ -71,44 +71,46 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, struct session *s = tc->session; struct winlink *wl = target->wl; struct window_pane *wp = target->wp; - struct style_ranges *ranges; - struct style_range *sr; + struct style_ranges *ranges = NULL; + struct style_range *sr = NULL; const char *xp, *yp; - u_int line, ox, oy, sx, sy, lines; + char *p; + int top; + u_int line, ox, oy, sx, sy, lines, position; + long n; + struct format_tree *ft; - lines = status_line_size(tc); - for (line = 0; line < lines; line++) { - ranges = &tc->status.entries[line].ranges; - TAILQ_FOREACH(sr, ranges, entry) { - if (sr->type == STYLE_RANGE_WINDOW) - break; - } - if (sr != NULL) - break; + /* + * Work out the position from the -x and -y arguments. This is the + * bottom-left position. + */ + + /* If the popup is too big, stop now. */ + if (w > tty->sx || h > tty->sy) + return (0); + + /* Create format with mouse position if any. */ + ft = format_create_from_target(item); + if (event->m.valid) { + format_add(ft, "popup_mouse_x", "%u", event->m.x); + format_add(ft, "popup_mouse_y", "%u", event->m.y); } - if (line == lines) - ranges = &tc->status.entries[0].ranges; - xp = args_get(args, 'x'); - if (xp == NULL || strcmp(xp, "C") == 0) - *px = (tty->sx - 1) / 2 - w / 2; - else if (strcmp(xp, "R") == 0) - *px = tty->sx - 1; - else if (strcmp(xp, "P") == 0) { - tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); - if (wp->xoff >= ox) - *px = wp->xoff - ox; + /* + * If there are any status lines, add this window position and the + * status line position. + */ + top = status_at_line(tc); + if (top != -1) { + lines = status_line_size(tc); + if (top == 0) + top = lines; else - *px = 0; - } else if (strcmp(xp, "M") == 0) { - if (event->m.valid && event->m.x > w / 2) - *px = event->m.x - w / 2; - else - *px = 0; - } else if (strcmp(xp, "W") == 0) { - if (status_at_line(tc) == -1) - *px = 0; - else { + top = 0; + position = options_get_number(s->options, "status-position"); + + for (line = 0; line < lines; line++) { + ranges = &tc->status.entries[line].ranges; TAILQ_FOREACH(sr, ranges, entry) { if (sr->type != STYLE_RANGE_WINDOW) continue; @@ -116,61 +118,137 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, break; } if (sr != NULL) - *px = sr->start; - else - *px = 0; + break; + } + if (line == lines) + ranges = &tc->status.entries[0].ranges; + + if (sr != NULL) { + format_add(ft, "popup_window_status_line_x", "%u", + sr->start); + if (position == 0) { + format_add(ft, "popup_window_status_line_y", + "%u", line + 1 + h); + } else { + format_add(ft, "popup_window_status_line_y", + "%u", tty->sy - lines + line); + } + } + + if (position == 0) + format_add(ft, "popup_status_line_y", "%u", lines + h); + else { + format_add(ft, "popup_status_line_y", "%u", + tty->sy - lines); } } else - *px = strtoul(xp, NULL, 10); - if ((*px) + w >= tty->sx) - *px = tty->sx - w; + top = 0; + /* Popup width and height. */ + format_add(ft, "popup_width", "%u", w); + format_add(ft, "popup_height", "%u", h); + + /* Position so popup is in the centre. */ + n = (long)(tty->sx - 1) / 2 - w / 2; + if (n < 0) + format_add(ft, "popup_centre_x", "%u", 0); + else + format_add(ft, "popup_centre_x", "%ld", n); + n = (tty->sy - 1) / 2 + h / 2; + if (n + h >= tty->sy) + format_add(ft, "popup_centre_y", "%u", tty->sy - h); + else + format_add(ft, "popup_centre_y", "%ld", n); + + /* Position of popup relative to mouse. */ + if (event->m.valid) { + n = (long)event->m.x - w / 2; + if (n < 0) + format_add(ft, "popup_mouse_centre_x", "%u", 0); + else + format_add(ft, "popup_mouse_centre_x", "%ld", n); + n = event->m.y - h / 2; + if (n + h >= tty->sy) { + format_add(ft, "popup_mouse_centre_y", "%u", + tty->sy - h); + } else + format_add(ft, "popup_mouse_centre_y", "%ld", n); + n = (long)event->m.y + h; + if (n + h >= tty->sy) + format_add(ft, "popup_mouse_top", "%u", tty->sy - h); + else + format_add(ft, "popup_mouse_top", "%ld", n); + n = event->m.y - h; + if (n < 0) + format_add(ft, "popup_mouse_bottom", "%u", 0); + else + format_add(ft, "popup_mouse_bottom", "%ld", n); + } + + /* Position in pane. */ + tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); + n = top + wp->yoff - oy + h; + if (n >= tty->sy) + format_add(ft, "popup_pane_top", "%u", tty->sy - h); + else + format_add(ft, "popup_pane_top", "%ld", n); + format_add(ft, "popup_pane_bottom", "%u", top + wp->yoff + wp->sy - oy); + format_add(ft, "popup_pane_left", "%u", wp->xoff - ox); + n = (long)wp->xoff + wp->sx - ox - w; + if (n < 0) + format_add(ft, "popup_pane_right", "%u", 0); + else + format_add(ft, "popup_pane_right", "%ld", n); + + /* Expand horizontal position. */ + xp = args_get(args, 'x'); + if (xp == NULL || strcmp(xp, "C") == 0) + xp = "#{popup_centre_x}"; + else if (strcmp(xp, "R") == 0) + xp = "#{popup_right}"; + else if (strcmp(xp, "P") == 0) + xp = "#{popup_pane_left}"; + else if (strcmp(xp, "M") == 0) + xp = "#{popup_mouse_centre_x}"; + else if (strcmp(xp, "W") == 0) + xp = "#{popup_window_status_line_x}"; + p = format_expand(ft, xp); + n = strtol(p, NULL, 10); + if (n + w >= tty->sx) + n = tty->sx - w; + else if (n < 0) + n = 0; + *px = n; + log_debug("%s: -x: %s = %s = %u", __func__, xp, p, *px); + free(p); + + /* Expand vertical position */ yp = args_get(args, 'y'); if (yp == NULL || strcmp(yp, "C") == 0) - *py = (tty->sy - 1) / 2 + h / 2; - else if (strcmp(yp, "P") == 0) { - tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); - if (wp->yoff + wp->sy >= oy) - *py = wp->yoff + wp->sy - oy; - else - *py = 0; - } else if (strcmp(yp, "M") == 0) { - if (event->m.valid) - *py = event->m.y + h; - else - *py = 0; - } else if (strcmp(yp, "S") == 0) { - if (options_get_number(s->options, "status-position") == 0) { - if (lines != 0) - *py = lines + h; - else - *py = 0; - } else { - if (lines != 0) - *py = tty->sy - lines; - else - *py = tty->sy; - } - } else if (strcmp(yp, "W") == 0) { - if (options_get_number(s->options, "status-position") == 0) { - if (lines != 0) - *py = line + 1 + h; - else - *py = 0; - } else { - if (lines != 0) - *py = tty->sy - lines + line; - else - *py = tty->sy; - } - } else - *py = strtoul(yp, NULL, 10); - if (*py < h) - *py = 0; + yp = "#{popup_centre_y}"; + else if (strcmp(yp, "P") == 0) + yp = "#{popup_pane_bottom}"; + else if (strcmp(yp, "M") == 0) + yp = "#{popup_mouse_top}"; + else if (strcmp(yp, "S") == 0) + yp = "#{popup_status_line_y}"; + else if (strcmp(yp, "W") == 0) + yp = "#{popup_window_status_line_y}"; + p = format_expand(ft, yp); + n = strtol(p, NULL, 10); + if (n < h) + n = 0; else - *py -= h; - if ((*py) + h >= tty->sy) - *py = tty->sy - h; + n -= h; + if (n + h >= tty->sy) + n = tty->sy - h; + else if (n < 0) + n = 0; + *py = n; + log_debug("%s: -y: %s = %s = %u", __func__, yp, p, *py); + free(p); + + return (1); } static enum cmd_retval @@ -226,8 +304,11 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) menu_free(menu); return (CMD_RETURN_NORMAL); } - cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4, - menu->count + 2); + if (!cmd_display_menu_get_position(tc, item, args, &px, &py, + menu->width + 4, menu->count + 2)) { + menu_free(menu); + return (CMD_RETURN_NORMAL); + } if (args_has(args, 'O')) flags |= MENU_STAYOPEN; @@ -296,7 +377,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) w = tty->sx - 1; if (h > tty->sy - 1) h = tty->sy - 1; - cmd_display_menu_get_position(tc, item, args, &px, &py, w, h); + if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h)) + return (CMD_RETURN_NORMAL); value = args_get(args, 'd'); if (value != NULL) diff --git a/tmux.1 b/tmux.1 index 3d58b32f..0a71cc37 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5388,6 +5388,28 @@ Both may be a row or column number, or one of the following special values: .It Li "S" Ta Fl y Ta "The line above or below the status line" .El .Pp +Or a format, which is expanded including the following additional variables: +.Bl -column "XXXXXXXXXXXXXXXXXXXXXXXXXX" -offset indent +.It Sy "Variable name" Ta Sy "Replaced with" +.It Li "popup_centre_x" Ta "Centered in the client" +.It Li "popup_centre_y" Ta "Centered in the client" +.It Li "popup_height" Ta "Height of menu or popup" +.It Li "popup_mouse_bottom" Ta "Bottom of at the mouse" +.It Li "popup_mouse_centre_x" Ta "Horizontal centre at the mouse" +.It Li "popup_mouse_centre_y" Ta "Vertical centre at the mouse" +.It Li "popup_mouse_top" Ta "Top at the mouse" +.It Li "popup_mouse_x" Ta "Mouse X position" +.It Li "popup_mouse_y" Ta "Mouse Y position" +.It Li "popup_pane_bottom" Ta "Bottom of the pane" +.It Li "popup_pane_left" Ta "Left of the pane" +.It Li "popup_pane_right" Ta "Right of the pane" +.It Li "popup_pane_top" Ta "Top of the pane" +.It Li "popup_status_line_y" Ta "Above or below the status line" +.It Li "popup_width" Ta "Width of menu or popup" +.It Li "popup_window_status_line_x" Ta "At the window position in status line" +.It Li "popup_window_status_line_y" Ta "At the status line showing the window" +.El +.Pp 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. From 5306bb0db79b4cc0b8e620bfe52e8fed446a101c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 3 Nov 2020 08:37:08 +0000 Subject: [PATCH 0631/1006] Update to 3.2-rc3, bring in all the changes from master. --- CHANGES | 4 +--- configure.ac | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index d385ed16..6f3944c7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,4 @@ -CHANGES FROM 3.2 TO 3.3 +CHANGES FROM 3.1c TO 3.2 * Fire focus events even when the pane is in a mode. @@ -27,8 +27,6 @@ CHANGES FROM 3.2 TO 3.3 * 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. diff --git a/configure.ac b/configure.ac index 93246fc8..768d3db3 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], next-3.3) +AC_INIT([tmux], 3.2-rc3) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 572a6b21b5a4d4e9587ddfc933449bb89fafeab1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 3 Nov 2020 08:41:24 +0000 Subject: [PATCH 0632/1006] Back to 3.3. --- CHANGES | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 6f3944c7..8a438f81 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +CHANGES FROM 3.2 TO 3.3 + +XXX + CHANGES FROM 3.1c TO 3.2 * Fire focus events even when the pane is in a mode. diff --git a/configure.ac b/configure.ac index 768d3db3..93246fc8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.2-rc3) +AC_INIT([tmux], next-3.3) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 1326529f99366e7d438364fd8727ec132a2f7433 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 9 Nov 2020 08:42:43 +0000 Subject: [PATCH 0633/1006] Remove some old debug logging. --- screen-write.c | 1 - 1 file changed, 1 deletion(-) diff --git a/screen-write.c b/screen-write.c index c9c61086..ab6a020c 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1551,7 +1551,6 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { screen_write_set_cursor(ctx, ci->x, y); if (ci->type == CLEAR_END) { - log_debug("XXX %u %u", ci->x, ci->bg); screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; tty_write(tty_cmd_clearendofline, &ttyctx); From 72c46aa15e50ef6391ab3fe6e230ed3abc9485b0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 9 Nov 2020 09:00:41 +0000 Subject: [PATCH 0634/1006] Add support for Haiku, from David Carlier. GitHub issue 2453. --- Makefile.am | 5 +++ compat.h | 4 +++ compat/forkpty-haiku.c | 82 ++++++++++++++++++++++++++++++++++++++++++ configure.ac | 30 ++++++++++++---- osdep-haiku.c | 53 +++++++++++++++++++++++++++ 5 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 compat/forkpty-haiku.c create mode 100644 osdep-haiku.c diff --git a/Makefile.am b/Makefile.am index 91d641fd..7b84b1e9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -58,6 +58,11 @@ if IS_NETBSD AM_CPPFLAGS += -D_OPENBSD_SOURCE endif +# Set flags for Haiku. +if IS_HAIKU +AM_CPPFLAGS += -D_BSD_SOURCE +endif + # List of sources. dist_tmux_SOURCES = \ alerts.c \ diff --git a/compat.h b/compat.h index b213336f..ec125ced 100644 --- a/compat.h +++ b/compat.h @@ -110,6 +110,10 @@ void warnx(const char *, ...); #define pledge(s, p) (0) #endif +#ifndef IMAXBEL +#define IMAXBEL 0 +#endif + #ifdef HAVE_STDINT_H #include #else diff --git a/compat/forkpty-haiku.c b/compat/forkpty-haiku.c new file mode 100644 index 00000000..6112164c --- /dev/null +++ b/compat/forkpty-haiku.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2008 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 +#include +#include + +#include "compat.h" + +void fatal(const char *, ...); +void fatalx(const char *, ...); + +pid_t +forkpty(int *master, char *name, struct termios *tio, struct winsize *ws) +{ + int slave = -1; + char *path; + pid_t pid; + + if ((*master = open("/dev/ptmx", O_RDWR|O_NOCTTY)) == -1) + return (-1); + if (grantpt(*master) != 0) + goto out; + if (unlockpt(*master) != 0) + goto out; + + if ((path = ptsname(*master)) == NULL) + goto out; + if (name != NULL) + strlcpy(name, path, TTY_NAME_MAX); + if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) + goto out; + + switch (pid = fork()) { + case -1: + goto out; + case 0: + close(*master); + + setsid(); + if (ioctl(slave, TIOCSCTTY, NULL) == -1) + fatal("ioctl failed"); + + if (tio != NULL && tcsetattr(slave, TCSAFLUSH, tio) == -1) + fatal("tcsetattr failed"); + if (ioctl(slave, TIOCSWINSZ, ws) == -1) + fatal("ioctl failed"); + + dup2(slave, 0); + dup2(slave, 1); + dup2(slave, 2); + if (slave > 2) + close(slave); + return (0); + } + + close(slave); + return (pid); + +out: + if (*master != -1) + close(*master); + if (slave != -1) + close(slave); + return (-1); +} diff --git a/configure.ac b/configure.ac index 93246fc8..cf621835 100644 --- a/configure.ac +++ b/configure.ac @@ -296,12 +296,25 @@ AC_TRY_LINK( found_b64_ntop=yes, found_b64_ntop=no ) +OLD_LIBS="$LIBS" if test "x$found_b64_ntop" = xno; then AC_MSG_RESULT(no) - AC_MSG_CHECKING(for b64_ntop with -lresolv) - OLD_LIBS="$LIBS" - LIBS="$LIBS -lresolv" + LIBS="$OLD_LIBS -lresolv" + AC_TRY_LINK( + [ + #include + #include + #include + ], + [b64_ntop(NULL, 0, NULL, 0);], + found_b64_ntop=yes, + found_b64_ntop=no + ) +fi +if test "x$found_b64_ntop" = xno; then + AC_MSG_CHECKING(for b64_ntop with -lnetwork) + LIBS="$OLD_LIBS -lnetwork" AC_TRY_LINK( [ #include @@ -312,16 +325,14 @@ if test "x$found_b64_ntop" = xno; then found_b64_ntop=yes, found_b64_ntop=no ) - if test "x$found_b64_ntop" = xno; then - LIBS="$OLD_LIBS" - AC_MSG_RESULT(no) - fi fi if test "x$found_b64_ntop" = xyes; then AC_DEFINE(HAVE_B64_NTOP) AC_MSG_RESULT(yes) else + LIBS="$OLD_LIBS" AC_LIBOBJ(base64) + AC_MSG_RESULT(no) fi # Look for networking libraries. @@ -656,6 +667,10 @@ case "$host_os" in AC_MSG_RESULT(cygwin) PLATFORM=cygwin ;; + *haiku*) + AC_MSG_RESULT(haiku) + PLATFORM=haiku + ;; *) AC_MSG_RESULT(unknown) PLATFORM=unknown @@ -671,6 +686,7 @@ AM_CONDITIONAL(IS_NETBSD, test "x$PLATFORM" = xnetbsd) AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd) AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos) AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux) +AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku) AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown) # Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user diff --git a/osdep-haiku.c b/osdep-haiku.c new file mode 100644 index 00000000..298dc05e --- /dev/null +++ b/osdep-haiku.c @@ -0,0 +1,53 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 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 +#include + +#include "tmux.h" + +char * +osdep_get_name(int fd, __unused char *tty) +{ + pid_t tid; + team_info tinfo; + + if ((tid = tcgetpgrp(fd)) == -1) + return (NULL); + + if (get_team_info(tid, &tinfo) != B_OK) + return (NULL); + + /* Up to the first 64 characters. */ + return (xstrdup(tinfo.args)); +} + +char * +osdep_get_cwd(int fd) +{ + return (NULL); +} + +struct event_base * +osdep_event_init(void) +{ + return (event_init()); +} From 61e55fa50dc7119a8c88bb385e615a8e6c8d6a85 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 9 Nov 2020 09:10:10 +0000 Subject: [PATCH 0635/1006] Change how escaping is processed for formats so that ## and # can be used in styles. Also add a 'w' format modifier for the width. From Chas J Owens IV in GitHub issue 2389. --- format-draw.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++---- format.c | 40 ++++++++++-- tmux.1 | 4 +- 3 files changed, 201 insertions(+), 17 deletions(-) diff --git a/format-draw.c b/format-draw.c index ec98ba95..e73c5df4 100644 --- a/format-draw.c +++ b/format-draw.c @@ -486,6 +486,18 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, focus_end, frs); } +/* Draw multiple characters. */ +static void +format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch, + u_int n) +{ + u_int i; + + utf8_set(&sy->gc.data, ch); + for (i = 0; i < n; i++) + screen_write_cell(ctx, &sy->gc); +} + /* Draw a format to a screen. */ void format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, @@ -509,10 +521,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, size_t size = strlen(expanded); struct screen *os = octx->s, s[TOTAL]; struct screen_write_ctx ctx[TOTAL]; - u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL]; + u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL]; u_int map[] = { LEFT, LEFT, CENTRE, RIGHT }; int focus_start = -1, focus_end = -1; - int list_state = -1, fill = -1; + int list_state = -1, fill = -1, even; enum style_align list_align = STYLE_ALIGN_DEFAULT; struct grid_cell gc, current_default; struct style sy, saved_sy; @@ -547,6 +559,34 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, */ cp = expanded; while (*cp != '\0') { + /* Handle sequences of #. */ + if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') { + for (n = 1; cp[n] == '#'; n++) + /* nothing */; + if (cp[n] != '[') { + width[current] += n; + cp += n; + format_draw_many(&ctx[current], &sy, '#', n); + continue; + } + even = ((n % 2) == 0); + if (even) + cp += (n + 1); + else + cp += (n - 1); + if (sy.ignore) + continue; + format_draw_many(&ctx[current], &sy, '#', n / 2); + width[current] += (n / 2); + if (even) { + utf8_set(ud, '['); + screen_write_cell(&ctx[current], &sy.gc); + width[current]++; + } + continue; + } + + /* Is this not a style? */ if (cp[0] != '#' || cp[1] != '[' || sy.ignore) { /* See if this is a UTF-8 character. */ if ((more = utf8_open(ud, *cp)) == UTF8_MORE) { @@ -796,13 +836,33 @@ u_int format_width(const char *expanded) { const char *cp, *end; - u_int width = 0; + u_int n, width = 0; struct utf8_data ud; enum utf8_state more; cp = expanded; while (*cp != '\0') { - if (cp[0] == '#' && cp[1] == '[') { + if (*cp == '#') { + for (n = 1; cp[n] == '#'; n++) + /* nothing */; + if (cp[n] != '[') { + width += n; + cp += n; + continue; + } + width += (n / 2); /* one for each ## */ + + if ((n % 2) == 0) { + /* + * An even number of #s means that all #s are + * escaped, so not a style. + */ + width++; /* one for the [ */ + cp += (n + 1); + continue; + } + cp += (n - 1); /* point to the [ */ + end = format_skip(cp + 2, "]"); if (end == NULL) return (0); @@ -823,19 +883,57 @@ format_width(const char *expanded) return (width); } -/* Trim on the left, taking #[] into account. */ +/* + * Trim on the left, taking #[] into account. Note, we copy the whole set of + * unescaped #s, but only add their escaped size to width. This is because the + * format_draw function will actually do the escaping when it runs + */ char * format_trim_left(const char *expanded, u_int limit) { char *copy, *out; const char *cp = expanded, *end; - u_int width = 0; + u_int even, n, width = 0; struct utf8_data ud; enum utf8_state more; - out = copy = xmalloc(strlen(expanded) + 1); + out = copy = xcalloc(1, strlen(expanded) + 1); while (*cp != '\0') { - if (cp[0] == '#' && cp[1] == '[') { + if (width >= limit) + break; + if (*cp == '#') { + for (end = cp + 1; *end == '#'; end++) + /* nothing */; + n = end - cp; + if (*end != '[') { + if (n > limit - width) + n = limit - width; + memcpy(out, cp, n); + out += n; + width += n; + cp = end; + continue; + } + even = ((n % 2) == 0); + + n /= 2; + if (n > limit - width) + n = limit - width; + width += n; + n *= 2; + memcpy(out, cp, n); + out += n; + + if (even) { + if (width + 1 <= limit) { + *out++ = '['; + width++; + } + cp = end + 1; + continue; + } + cp = end - 1; + end = format_skip(cp + 2, "]"); if (end == NULL) break; @@ -873,7 +971,7 @@ format_trim_right(const char *expanded, u_int limit) { char *copy, *out; const char *cp = expanded, *end; - u_int width = 0, total_width, skip; + u_int width = 0, total_width, skip, old_n, even, n; struct utf8_data ud; enum utf8_state more; @@ -882,12 +980,64 @@ format_trim_right(const char *expanded, u_int limit) return (xstrdup(expanded)); skip = total_width - limit; - out = copy = xmalloc(strlen(expanded) + 1); + out = copy = xcalloc(1, strlen(expanded) + 1); while (*cp != '\0') { - if (cp[0] == '#' && cp[1] == '[') { + if (*cp == '#') { + for (end = cp + 1; *end == '#'; end++) + /* nothing */; + old_n = n = end - cp; + if (*end != '[') { + if (width <= skip) { + if (skip - width >= n) + n = 0; + else + n -= (skip - width); + } + if (n != 0) { + memcpy(out, cp, n); + out += n; + } + + /* + * The width always increases by the full + * amount even if we can't copy anything yet. + */ + width += old_n; + cp = end; + continue; + } + even = ((n % 2) == 0); + + n /= 2; + if (width <= skip) { + if (skip - width >= n) + n = 0; + else + n -= (skip - width); + } + if (n != 0) { + /* + * Copy the full amount because it hasn't been + * escaped yet. + */ + memcpy(out, cp, old_n); + out += old_n; + } + cp += old_n; + width += (old_n / 2) - even; + + if (even) { + if (width > skip) + *out++ = '['; + width++; + continue; + } + cp = end - 1; + end = format_skip(cp + 2, "]"); - if (end == NULL) + if (end == NULL) { break; + } memcpy(out, cp, end + 1 - cp); out += (end + 1 - cp); cp = end + 1; diff --git a/format.c b/format.c index 99efbc1c..91955259 100644 --- a/format.c +++ b/format.c @@ -98,6 +98,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_PANES 0x200 #define FORMAT_PRETTY 0x400 #define FORMAT_LENGTH 0x800 +#define FORMAT_WIDTH 0x1000 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 @@ -1671,7 +1672,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, /* * Modifiers are a ; separated list of the forms: - * l,m,C,b,d,n,t,q,E,T,S,W,P,<,> + * l,m,C,b,d,n,t,w,q,E,T,S,W,P,<,> * =a * =/a * =/a/ @@ -1688,7 +1689,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lbdnqETSWP<>", cp[0]) != NULL && + if (strchr("lbdnqwETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -2184,6 +2185,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, if (errptr != NULL) width = 0; break; + case 'w': + modifiers |= FORMAT_WIDTH; + break; case 'e': if (fm->argc < 1 || fm->argc > 3) break; @@ -2456,13 +2460,19 @@ done: format_log(es, "applied padding width %d: %s", width, value); } - /* Replace with the length if needed. */ + /* Replace with the length or width if needed. */ if (modifiers & FORMAT_LENGTH) { xasprintf(&new, "%zu", strlen(value)); free(value); value = new; format_log(es, "replacing with length: %s", new); } + if (modifiers & FORMAT_WIDTH) { + xasprintf(&new, "%u", format_width(value)); + free(value); + value = new; + format_log(es, "replacing with width: %s", new); + } /* Expand the buffer and copy in the value. */ valuelen = strlen(value); @@ -2589,8 +2599,30 @@ format_expand1(struct format_expand_state *es, const char *fmt) break; fmt += n + 1; continue; - case '}': case '#': + /* + * If ##[ (with two or more #s), then it is a style and + * can be left for format_draw to handle. + */ + ptr = fmt; + n = 2; + while (*ptr == '#') { + ptr++; + n++; + } + if (*ptr == '[') { + format_log(es, "found #*%zu[", n); + while (len - off < n + 2) { + buf = xreallocarray(buf, 2, len); + len *= 2; + } + memcpy(buf + off, fmt - 2, n + 1); + off += n + 1; + fmt = ptr + 1; + continue; + } + /* FALLTHROUGH */ + case '}': case ',': format_log(es, "found #%c", ch); while (len - off < 2) { diff --git a/tmux.1 b/tmux.1 index 0a71cc37..37f6f165 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4591,7 +4591,9 @@ pads the string to a given width, for example 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 +expands to the length of the variable and +.Ql w +to its width when displayed, for example .Ql #{n:window_name} . .Pp Prefixing a time variable with From f1193b48910aed15a2c73cdb5784a26f2ea0f64c Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 9 Nov 2020 10:54:28 +0000 Subject: [PATCH 0636/1006] If mouse bits change, clear them all and set again to avoid problems with some bits implying others. GitHub issue 2458. --- tty.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/tty.c b/tty.c index bcbccca6..777b639b 100644 --- a/tty.c +++ b/tty.c @@ -694,28 +694,26 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } if ((changed & ALL_MOUSE_MODES) && tty_term_has(tty->term, TTYC_KMOUS)) { - if ((mode & ALL_MOUSE_MODES) == 0) + /* + * If the mouse modes have changed, clear any that are set and + * apply again. There are differences in how terminals track + * the various bits. + */ + if (tty->mode & MODE_MOUSE_SGR) tty_puts(tty, "\033[?1006l"); - if ((changed & MODE_MOUSE_STANDARD) && - (~mode & MODE_MOUSE_STANDARD)) + if (tty->mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000l"); - if ((changed & MODE_MOUSE_BUTTON) && - (~mode & MODE_MOUSE_BUTTON)) + if (tty->mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1002l"); - if ((changed & MODE_MOUSE_ALL) && - (~mode & MODE_MOUSE_ALL)) + if (tty->mode & MODE_MOUSE_ALL) tty_puts(tty, "\033[?1003l"); - if (mode & ALL_MOUSE_MODES) tty_puts(tty, "\033[?1006h"); - if ((changed & MODE_MOUSE_STANDARD) && - (mode & MODE_MOUSE_STANDARD)) + if (mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000h"); - if ((changed & MODE_MOUSE_BUTTON) && - (mode & MODE_MOUSE_BUTTON)) + if (mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1002h"); - if ((changed & MODE_MOUSE_ALL) && - (mode & MODE_MOUSE_ALL)) + if (mode & MODE_MOUSE_ALL) tty_puts(tty, "\033[?1003h"); } if (changed & MODE_BRACKETPASTE) { From 3eb1519bd784076f63fed6678b88f918317a2124 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 9 Nov 2020 16:41:55 +0000 Subject: [PATCH 0637/1006] Scaffold for oss-fuzz, from Sergey Nizovtsev. --- .gitignore | 2 + Makefile.am | 6 +++ compat.h | 8 ++++ configure.ac | 47 +++++++++++++++++++-- fuzz/input-fuzzer.c | 89 +++++++++++++++++++++++++++++++++++++++ fuzz/input-fuzzer.dict | 8 ++++ fuzz/input-fuzzer.options | 2 + 7 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 fuzz/input-fuzzer.c create mode 100644 fuzz/input-fuzzer.dict create mode 100644 fuzz/input-fuzzer.options diff --git a/.gitignore b/.gitignore index d01a0166..ec49a6de 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ configure tmux.1.* *.dSYM cmd-parse.c +fuzz/*-fuzzer +.dirstamp diff --git a/Makefile.am b/Makefile.am index 7b84b1e9..b40e0944 100644 --- a/Makefile.am +++ b/Makefile.am @@ -202,6 +202,12 @@ if HAVE_UTF8PROC nodist_tmux_SOURCES += compat/utf8proc.c endif +if NEED_FUZZING +check_PROGRAMS = fuzz/input-fuzzer +fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS) +fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) +endif + # Install tmux.1 in the right format. install-exec-hook: if test x@MANFORMAT@ = xmdoc; then \ diff --git a/compat.h b/compat.h index ec125ced..6e26a02c 100644 --- a/compat.h +++ b/compat.h @@ -52,6 +52,9 @@ #ifndef __packed #define __packed __attribute__ ((__packed__)) #endif +#ifndef __weak +#define __weak __attribute__ ((__weak__)) +#endif #ifndef ECHOPRT #define ECHOPRT 0 @@ -395,6 +398,11 @@ int utf8proc_mbtowc(wchar_t *, const char *, size_t); int utf8proc_wctomb(char *, wchar_t); #endif +#ifdef NEED_FUZZING +/* tmux.c */ +#define main __weak main +#endif + /* getopt.c */ extern int BSDopterr; extern int BSDoptind; diff --git a/configure.ac b/configure.ac index cf621835..97010df4 100644 --- a/configure.ac +++ b/configure.ac @@ -21,6 +21,26 @@ SAVED_CFLAGS="$CFLAGS" SAVED_CPPFLAGS="$CPPFLAGS" SAVED_LDFLAGS="$LDFLAGS" +# Is this oss-fuzz build? +AC_ARG_ENABLE( + fuzzing, + AC_HELP_STRING(--enable-fuzzing, build fuzzers) +) +AC_ARG_VAR( + FUZZING_LIBS, + AC_HELP_STRING(libraries to link fuzzing targets with) +) + +# Set up convenient fuzzing defaults before initializing compiler. +if test "x$enable_fuzzing" = xyes; then + AC_DEFINE(NEED_FUZZING) + test "x$CC" == x && CC=clang + test "x$FUZZING_LIBS" == x && \ + FUZZING_LIBS="-fsanitize=fuzzer" + test "x$SAVED_CFLAGS" == x && \ + AM_CFLAGS="-g -fsanitize=fuzzer-no-link,address" +fi + # Set up the compiler in two different ways and say yes we may want to install. AC_PROG_CC AM_PROG_CC_C_O @@ -54,8 +74,11 @@ if test "x$enable_static" = xyes; then LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS" fi +# Do we need fuzzers? +AM_CONDITIONAL(NEED_FUZZING, test "x$enable_fuzzing" = xyes) + # Is this gcc? -AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) +AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes -a "x$enable_fuzzing" != xyes) # Is this Sun CC? AC_EGREP_CPP( @@ -117,8 +140,6 @@ AC_REPLACE_FUNCS([ \ getline \ getprogname \ memmem \ - recallocarray \ - reallocarray \ setenv \ setproctitle \ strcasestr \ @@ -130,6 +151,26 @@ AC_REPLACE_FUNCS([ \ ]) AC_FUNC_STRNLEN +# Clang sanitizers wrap reallocarray even if it isn't available on the target +# system. When compiled it always returns NULL and crashes the program. To +# detect this we need a more complicated test. +AC_MSG_CHECKING([for working reallocarray]) +AC_RUN_IFELSE([AC_LANG_PROGRAM( + [#include ], + [return (reallocarray(NULL, 1, 1) == NULL);] + )], + AC_MSG_RESULT(yes), + [AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])] +) +AC_MSG_CHECKING([for working recallocarray]) +AC_RUN_IFELSE([AC_LANG_PROGRAM( + [#include ], + [return (recallocarray(NULL, 1, 1, 1) == NULL);] + )], + AC_MSG_RESULT(yes), + [AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])] +) + # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) diff --git a/fuzz/input-fuzzer.c b/fuzz/input-fuzzer.c new file mode 100644 index 00000000..27f2be3d --- /dev/null +++ b/fuzz/input-fuzzer.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 Sergey Nizovtsev + * + * 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 "tmux.h" + +#define FUZZER_MAXLEN 512 +#define PANE_WIDTH 80 +#define PANE_HEIGHT 25 + +struct event_base *libevent; + +int +LLVMFuzzerTestOneInput(const unsigned char *data, size_t size) +{ + struct bufferevent *vpty[2]; + struct window *w; + struct window_pane *wp; + int error; + + /* + * Since AFL doesn't support -max_len paramenter we have to + * discard long inputs manually. + */ + if (size > FUZZER_MAXLEN) + return 0; + + w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0); + wp = window_add_pane(w, NULL, 0, 0); + bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty); + wp->ictx = input_init(wp, vpty[0]); + window_add_ref(w, __func__); + + input_parse_buffer(wp, (u_char*) data, size); + while (cmdq_next(NULL) != 0) + ; + error = event_base_loop(libevent, EVLOOP_NONBLOCK); + if (error == -1) + errx(1, "event_base_loop failed"); + + assert(w->references == 1); + window_remove_ref(w, __func__); + + bufferevent_free(vpty[0]); + bufferevent_free(vpty[1]); + + return 0; +} + +int +LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv) +{ + const struct options_table_entry *oe; + + global_environ = environ_create(); + global_options = options_create(NULL); + global_s_options = options_create(NULL); + global_w_options = options_create(NULL); + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope & OPTIONS_TABLE_SERVER) + options_default(global_options, oe); + if (oe->scope & OPTIONS_TABLE_SESSION) + options_default(global_s_options, oe); + if (oe->scope & OPTIONS_TABLE_WINDOW) + options_default(global_w_options, oe); + } + libevent = osdep_event_init(); + + options_set_number(global_w_options, "monitor-bell", 0); + options_set_number(global_w_options, "allow-rename", 1); + options_set_number(global_options, "set-clipboard", 2); + + return 0; +} diff --git a/fuzz/input-fuzzer.dict b/fuzz/input-fuzzer.dict new file mode 100644 index 00000000..2091b970 --- /dev/null +++ b/fuzz/input-fuzzer.dict @@ -0,0 +1,8 @@ +"\x1b[" +"1000" +"2004" +"1049" +"38;2" +"100;" +"tmux;" +"rgb:00/00/00" diff --git a/fuzz/input-fuzzer.options b/fuzz/input-fuzzer.options new file mode 100644 index 00000000..5d468bc6 --- /dev/null +++ b/fuzz/input-fuzzer.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 512 From bbab5b7a30717c4455f7725e6adc364f6c274e7d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 10 Nov 2020 08:16:52 +0000 Subject: [PATCH 0638/1006] Allow previous-word to scroll onto the first line, noticed by Anindya Mukherjee. --- window-copy.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/window-copy.c b/window-copy.c index 1dc0c293..6dc03b34 100644 --- a/window-copy.c +++ b/window-copy.c @@ -4526,10 +4526,11 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, const char *separators, int already, u_int *ppx, u_int *ppy) { struct window_copy_mode_data *data = wme->data; - u_int px, py; + u_int px, py, hsize; + hsize = screen_hsize(data->backing); px = data->cx; - py = screen_hsize(data->backing) + data->cy - data->oy; + py = hsize + data->cy - data->oy; /* Move back to the previous word character. */ if (already || window_copy_in_set(wme, px, py, separators)) { @@ -4542,9 +4543,7 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, } else { if (py == 0 || (data->cy == 0 && - (screen_hsize(data->backing) == 0 || - data->oy >= - screen_hsize(data->backing) - 1))) + (hsize == 0 || data->oy > hsize - 1))) goto out; py--; @@ -4573,10 +4572,11 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, const char *separators, int already) { struct window_copy_mode_data *data = wme->data; - u_int px, py; + u_int px, py, hsize; + hsize = screen_hsize(data->backing); px = data->cx; - py = screen_hsize(data->backing) + data->cy - data->oy; + py = hsize + data->cy - data->oy; /* Move back to the previous word character. */ if (already || window_copy_in_set(wme, px, py, separators)) { @@ -4588,14 +4588,11 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, break; } else { if (data->cy == 0 && - (screen_hsize(data->backing) == 0 || - data->oy >= - screen_hsize(data->backing) - 1)) + (hsize == 0 || data->oy > hsize - 1)) goto out; window_copy_cursor_up(wme, 0); - py = screen_hsize(data->backing) + data->cy - - data->oy; + py = hsize + data->cy - data->oy; px = window_copy_find_length(wme, py); /* Stop if separator at EOL. */ From 0d28ee927421c46315bc0b47b5f8f49a8392efa4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Nov 2020 08:13:35 +0000 Subject: [PATCH 0639/1006] Log missing keys when extended keys is on rather than fatal(). --- input-keys.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/input-keys.c b/input-keys.c index 224bcfa5..5cd43dc3 100644 --- a/input-keys.c +++ b/input-keys.c @@ -330,7 +330,8 @@ static struct input_key_entry input_key_defaults[] = { .data = "\033[2;_~" }, { .key = KEYC_DC|KEYC_BUILD_MODIFIERS, - .data = "\033[3;_~" } + .data = "\033[3;_~" + } }; static const key_code input_key_modifiers[] = { 0, @@ -557,7 +558,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) modifier = '8'; break; default: - fatalx("invalid key modifiers: %llx", key); + goto missing; } xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier); bufferevent_write(bev, tmp, strlen(tmp)); From bfdc4373d71895a5f454834da4b0bd92ab964dd4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 17 Nov 2020 17:56:55 +0000 Subject: [PATCH 0640/1006] Update closefrom from OpenSSH for macOS code which is now needed. --- compat/closefrom.c | 110 ++++++++++++++++++++++++++++++++------------- configure.ac | 4 +- 2 files changed, 81 insertions(+), 33 deletions(-) diff --git a/compat/closefrom.c b/compat/closefrom.c index 7915cde4..28be3680 100644 --- a/compat/closefrom.c +++ b/compat/closefrom.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "compat.h" + #ifndef HAVE_CLOSEFROM #include @@ -44,8 +46,9 @@ # include # endif #endif - -#include "compat.h" +#if defined(HAVE_LIBPROC_H) +# include +#endif #ifndef OPEN_MAX # define OPEN_MAX 256 @@ -55,39 +58,15 @@ __unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; #endif /* lint */ +#ifndef HAVE_FCNTL_CLOSEM /* * Close all file descriptors greater than or equal to lowfd. */ -#ifdef HAVE_FCNTL_CLOSEM -void -closefrom(int lowfd) +static void +closefrom_fallback(int lowfd) { - (void) fcntl(lowfd, F_CLOSEM, 0); -} -#else -void -closefrom(int lowfd) -{ - long fd, maxfd; -#if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) - char fdpath[PATH_MAX], *endp; - struct dirent *dent; - DIR *dirp; - int len; + long fd, maxfd; - /* Check for a /proc/$$/fd directory. */ - len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); - if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) { - while ((dent = readdir(dirp)) != NULL) { - fd = strtol(dent->d_name, &endp, 10); - if (dent->d_name != endp && *endp == '\0' && - fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) - (void) close((int) fd); - } - (void) closedir(dirp); - } else -#endif - { /* * Fall back on sysconf() or getdtablesize(). We avoid checking * resource limits since it is possible to open a file descriptor @@ -99,11 +78,78 @@ closefrom(int lowfd) maxfd = getdtablesize(); #endif /* HAVE_SYSCONF */ if (maxfd < 0) - maxfd = OPEN_MAX; + maxfd = OPEN_MAX; for (fd = lowfd; fd < maxfd; fd++) - (void) close((int) fd); + (void) close((int) fd); +} +#endif /* HAVE_FCNTL_CLOSEM */ + +#ifdef HAVE_FCNTL_CLOSEM +void +closefrom(int lowfd) +{ + (void) fcntl(lowfd, F_CLOSEM, 0); +} +#elif defined(HAVE_LIBPROC_H) && defined(HAVE_PROC_PIDINFO) +void +closefrom(int lowfd) +{ + int i, r, sz; + pid_t pid = getpid(); + struct proc_fdinfo *fdinfo_buf = NULL; + + sz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); + if (sz == 0) + return; /* no fds, really? */ + else if (sz == -1) + goto fallback; + if ((fdinfo_buf = malloc(sz)) == NULL) + goto fallback; + r = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdinfo_buf, sz); + if (r < 0 || r > sz) + goto fallback; + for (i = 0; i < r / (int)PROC_PIDLISTFD_SIZE; i++) { + if (fdinfo_buf[i].proc_fd >= lowfd) + close(fdinfo_buf[i].proc_fd); + } + free(fdinfo_buf); + return; + fallback: + free(fdinfo_buf); + closefrom_fallback(lowfd); + return; +} +#elif defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) +void +closefrom(int lowfd) +{ + long fd; + char fdpath[PATH_MAX], *endp; + struct dirent *dent; + DIR *dirp; + int len; + + /* Check for a /proc/$$/fd directory. */ + len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); + if (len > 0 && (size_t)len < sizeof(fdpath) && (dirp = opendir(fdpath))) { + while ((dent = readdir(dirp)) != NULL) { + fd = strtol(dent->d_name, &endp, 10); + if (dent->d_name != endp && *endp == '\0' && + fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) + (void) close((int) fd); + } + (void) closedir(dirp); + return; } + /* /proc/$$/fd strategy failed, fall back to brute force closure */ + closefrom_fallback(lowfd); +} +#else +void +closefrom(int lowfd) +{ + closefrom_fallback(lowfd); } #endif /* !HAVE_FCNTL_CLOSEM */ #endif /* HAVE_CLOSEFROM */ diff --git a/configure.ac b/configure.ac index 97010df4..8dd00d79 100644 --- a/configure.ac +++ b/configure.ac @@ -99,6 +99,7 @@ AC_CHECK_HEADERS([ \ dirent.h \ fcntl.h \ inttypes.h \ + libproc.h \ libutil.h \ ndir.h \ paths.h \ @@ -124,7 +125,8 @@ AC_CHECK_FUNCS([ \ dirfd \ flock \ prctl \ - sysconf \ + proc_pidinfo \ + sysconf ]) # Check for functions with a compatibility implementation. From 2f1578ef8303abb8a60dcc94dd220ed29c96839d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 17 Nov 2020 17:56:55 +0000 Subject: [PATCH 0641/1006] Update closefrom from OpenSSH for macOS code which is now needed. --- compat/closefrom.c | 110 ++++++++++++++++++++++++++++++++------------- configure.ac | 4 +- 2 files changed, 81 insertions(+), 33 deletions(-) diff --git a/compat/closefrom.c b/compat/closefrom.c index 7915cde4..28be3680 100644 --- a/compat/closefrom.c +++ b/compat/closefrom.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "compat.h" + #ifndef HAVE_CLOSEFROM #include @@ -44,8 +46,9 @@ # include # endif #endif - -#include "compat.h" +#if defined(HAVE_LIBPROC_H) +# include +#endif #ifndef OPEN_MAX # define OPEN_MAX 256 @@ -55,39 +58,15 @@ __unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; #endif /* lint */ +#ifndef HAVE_FCNTL_CLOSEM /* * Close all file descriptors greater than or equal to lowfd. */ -#ifdef HAVE_FCNTL_CLOSEM -void -closefrom(int lowfd) +static void +closefrom_fallback(int lowfd) { - (void) fcntl(lowfd, F_CLOSEM, 0); -} -#else -void -closefrom(int lowfd) -{ - long fd, maxfd; -#if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) - char fdpath[PATH_MAX], *endp; - struct dirent *dent; - DIR *dirp; - int len; + long fd, maxfd; - /* Check for a /proc/$$/fd directory. */ - len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); - if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) { - while ((dent = readdir(dirp)) != NULL) { - fd = strtol(dent->d_name, &endp, 10); - if (dent->d_name != endp && *endp == '\0' && - fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) - (void) close((int) fd); - } - (void) closedir(dirp); - } else -#endif - { /* * Fall back on sysconf() or getdtablesize(). We avoid checking * resource limits since it is possible to open a file descriptor @@ -99,11 +78,78 @@ closefrom(int lowfd) maxfd = getdtablesize(); #endif /* HAVE_SYSCONF */ if (maxfd < 0) - maxfd = OPEN_MAX; + maxfd = OPEN_MAX; for (fd = lowfd; fd < maxfd; fd++) - (void) close((int) fd); + (void) close((int) fd); +} +#endif /* HAVE_FCNTL_CLOSEM */ + +#ifdef HAVE_FCNTL_CLOSEM +void +closefrom(int lowfd) +{ + (void) fcntl(lowfd, F_CLOSEM, 0); +} +#elif defined(HAVE_LIBPROC_H) && defined(HAVE_PROC_PIDINFO) +void +closefrom(int lowfd) +{ + int i, r, sz; + pid_t pid = getpid(); + struct proc_fdinfo *fdinfo_buf = NULL; + + sz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); + if (sz == 0) + return; /* no fds, really? */ + else if (sz == -1) + goto fallback; + if ((fdinfo_buf = malloc(sz)) == NULL) + goto fallback; + r = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdinfo_buf, sz); + if (r < 0 || r > sz) + goto fallback; + for (i = 0; i < r / (int)PROC_PIDLISTFD_SIZE; i++) { + if (fdinfo_buf[i].proc_fd >= lowfd) + close(fdinfo_buf[i].proc_fd); + } + free(fdinfo_buf); + return; + fallback: + free(fdinfo_buf); + closefrom_fallback(lowfd); + return; +} +#elif defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) +void +closefrom(int lowfd) +{ + long fd; + char fdpath[PATH_MAX], *endp; + struct dirent *dent; + DIR *dirp; + int len; + + /* Check for a /proc/$$/fd directory. */ + len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); + if (len > 0 && (size_t)len < sizeof(fdpath) && (dirp = opendir(fdpath))) { + while ((dent = readdir(dirp)) != NULL) { + fd = strtol(dent->d_name, &endp, 10); + if (dent->d_name != endp && *endp == '\0' && + fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) + (void) close((int) fd); + } + (void) closedir(dirp); + return; } + /* /proc/$$/fd strategy failed, fall back to brute force closure */ + closefrom_fallback(lowfd); +} +#else +void +closefrom(int lowfd) +{ + closefrom_fallback(lowfd); } #endif /* !HAVE_FCNTL_CLOSEM */ #endif /* HAVE_CLOSEFROM */ diff --git a/configure.ac b/configure.ac index 768d3db3..21123315 100644 --- a/configure.ac +++ b/configure.ac @@ -76,6 +76,7 @@ AC_CHECK_HEADERS([ \ dirent.h \ fcntl.h \ inttypes.h \ + libproc.h \ libutil.h \ ndir.h \ paths.h \ @@ -101,7 +102,8 @@ AC_CHECK_FUNCS([ \ dirfd \ flock \ prctl \ - sysconf \ + proc_pidinfo \ + sysconf ]) # Check for functions with a compatibility implementation. From 76cfb5f471ac785b133cdfd6ec53a5097c0bdf3c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 26 Nov 2020 09:19:10 +0000 Subject: [PATCH 0642/1006] Add -N flag to display-panes to ignore keys, GitHub issue 2473. --- cmd-display-panes.c | 15 +++++++++++---- tmux.1 | 5 +++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 352b2e4d..64efb89a 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_display_panes_entry = { .name = "display-panes", .alias = "displayp", - .args = { "bd:t:", 0, 1 }, - .usage = "[-b] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", + .args = { "bd:Nt:", 0, 1 }, + .usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_display_panes_exec @@ -284,8 +284,15 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) else cdata->item = item; - server_client_set_overlay(tc, delay, NULL, NULL, cmd_display_panes_draw, - cmd_display_panes_key, cmd_display_panes_free, cdata); + if (args_has(args, 'N')) { + server_client_set_overlay(tc, delay, NULL, NULL, + cmd_display_panes_draw, NULL, cmd_display_panes_free, + cdata); + } else { + server_client_set_overlay(tc, delay, NULL, NULL, + cmd_display_panes_draw, cmd_display_panes_key, + cmd_display_panes_free, cdata); + } if (args_has(args, 'b')) return (CMD_RETURN_NORMAL); diff --git a/tmux.1 b/tmux.1 index 37f6f165..4cc4bfe4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2098,7 +2098,7 @@ starts without the option information. This command works only if at least one client is attached. .It Xo .Ic display-panes -.Op Fl b +.Op Fl bN .Op Fl d Ar duration .Op Fl t Ar target-client .Op Ar template @@ -2111,7 +2111,8 @@ See the and .Ic display-panes-active-colour session options. -The indicator is closed when a key is pressed or +The indicator is closed when a key is pressed (unless +.Fl N is given) or .Ar duration milliseconds have passed. If From fd5c3e6122faea45d3eb44275424d78bc67b0ce2 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 26 Nov 2020 13:06:21 +0000 Subject: [PATCH 0643/1006] Fix check for vertical centre. --- cmd-display-menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 7115dc81..ae000abe 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -155,7 +155,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, else format_add(ft, "popup_centre_x", "%ld", n); n = (tty->sy - 1) / 2 + h / 2; - if (n + h >= tty->sy) + if (n >= tty->sy) format_add(ft, "popup_centre_y", "%u", tty->sy - h); else format_add(ft, "popup_centre_y", "%ld", n); From 33046ecee2090a7ff733097d1c05d377936b3e5f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 Nov 2020 13:37:45 +0000 Subject: [PATCH 0644/1006] Ignore running command when checking for no-hooks flag if it is blocked. GitHub issue 2483. --- cmd-queue.c | 6 +++++- tmux.1 | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 36f1c9be..05f439f5 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -768,7 +768,11 @@ cmdq_running(struct client *c) { struct cmdq_list *queue = cmdq_get(c); - return (queue->item); + if (queue->item == NULL) + return (NULL); + if (queue->item->flags & CMDQ_WAITING) + return (NULL); + return (queue->item); } /* Print a guard line. */ diff --git a/tmux.1 b/tmux.1 index 4cc4bfe4..b3a74b0a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2112,7 +2112,8 @@ and .Ic display-panes-active-colour session options. The indicator is closed when a key is pressed (unless -.Fl N is given) or +.Fl N +is given) or .Ar duration milliseconds have passed. If From 9a74bba007a60b93d1fdf68772e5cfb61b3558ff Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Dec 2020 08:12:58 +0000 Subject: [PATCH 0645/1006] Make replacement of ##s consistent when drawing formats, whether followed by [ or not. Add a flag (e) to the q: format modifier to double up #s and use it for the window_flags format variable so it doesn't end up escaping any following text. GitHub issue 2485. --- format-draw.c | 8 ++++++-- format.c | 32 +++++++++++++++++++++++++++++--- options-table.c | 4 ++-- tmux.1 | 7 ++++++- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/format-draw.c b/format-draw.c index e73c5df4..67b961d9 100644 --- a/format-draw.c +++ b/format-draw.c @@ -563,13 +563,17 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') { for (n = 1; cp[n] == '#'; n++) /* nothing */; + even = ((n % 2) == 0); if (cp[n] != '[') { - width[current] += n; cp += n; + if (even) + n = (n / 2); + else + n = (n / 2) + 1; + width[current] += n; format_draw_many(&ctx[current], &sy, '#', n); continue; } - even = ((n % 2) == 0); if (even) cp += (n + 1); else diff --git a/format.c b/format.c index 91955259..98dc4cb7 100644 --- a/format.c +++ b/format.c @@ -99,6 +99,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_PRETTY 0x400 #define FORMAT_LENGTH 0x800 #define FORMAT_WIDTH 0x1000 +#define FORMAT_ESCAPE 0x2000 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 @@ -1394,6 +1395,23 @@ format_quote(const char *s) return (out); } +/* Escape #s in string. */ +static char * +format_escape(const char *s) +{ + const char *cp; + char *out, *at; + + at = out = xmalloc(strlen(s) * 2 + 1); + for (cp = s; *cp != '\0'; cp++) { + if (*cp == '#') + *at++ = '#'; + *at++ = *cp; + } + *at = '\0'; + return (out); +} + /* Make a prettier time. */ static char * format_pretty_time(time_t t) @@ -1539,6 +1557,11 @@ found: found = xstrdup(format_quote(saved)); free(saved); } + if (modifiers & FORMAT_ESCAPE) { + saved = found; + found = xstrdup(format_escape(saved)); + free(saved); + } return (found); } @@ -1689,7 +1712,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lbdnqwETSWP<>", cp[0]) != NULL && + if (strchr("lbdnwETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -1710,7 +1733,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, } /* Now try single character with arguments. */ - if (strchr("mCst=pe", cp[0]) == NULL) + if (strchr("mCst=peq", cp[0]) == NULL) break; c = cp[0]; @@ -2216,7 +2239,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, time_format = format_strip(fm->argv[1]); break; case 'q': - modifiers |= FORMAT_QUOTE; + if (fm->argc < 1) + modifiers |= FORMAT_QUOTE; + else if (strchr(fm->argv[0], 'e') != NULL) + modifiers |= FORMAT_ESCAPE; break; case 'E': modifiers |= FORMAT_EXPAND; diff --git a/options-table.c b/options-table.c index 2ae49d7a..56e1c895 100644 --- a/options-table.c +++ b/options-table.c @@ -1014,7 +1014,7 @@ const struct options_table_entry options_table[] = { { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "#I:#W#{?window_flags,#{window_flags}, }", + .default_str = "#I:#W#{?window_flags,#{q/e:window_flags}, }", .text = "Format of the current window in the status line." }, @@ -1030,7 +1030,7 @@ const struct options_table_entry options_table[] = { { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "#I:#W#{?window_flags,#{window_flags}, }", + .default_str = "#I:#W#{?window_flags,#{q/e:window_flags}, }", .text = "Format of windows in the status line, except the current " "window." }, diff --git a/tmux.1 b/tmux.1 index b3a74b0a..9d1021f3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4638,7 +4638,12 @@ of the variable respectively. .Ql q:\& will escape .Xr sh 1 -special characters. +special characters or with an +.Ql e +suffix, escape hash characters (so +.Ql # +becomes +.Ql ## ). .Ql E:\& will expand the format twice, for example .Ql #{E:status-left} From f0c1233d4f97b499dd51688b089ad7c485c14b2a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Dec 2020 10:48:03 +0000 Subject: [PATCH 0646/1006] Leave newlines inside multiline quotes. --- cmd-parse.y | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd-parse.y b/cmd-parse.y index c8995d8b..7b42c621 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -1505,8 +1505,12 @@ yylex_token(int ch) state == NONE) break; - /* Spaces and comments inside quotes after \n are removed. */ + /* + * Spaces and comments inside quotes after \n are removed but + * the \n is left. + */ if (ch == '\n' && state != NONE) { + yylex_append1(&buf, &len, '\n'); while ((ch = yylex_getc()) == ' ' || ch == '\t') /* nothing */; if (ch != '#') From fd451aa7962f399250fd166f207451fcf4b9cb94 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Dec 2020 07:12:11 +0000 Subject: [PATCH 0647/1006] Redraw any visible modes when status line changes so that formats like the pane title are updated. GitHub issue 2487. Also a man page fix from jmc. --- server-client.c | 24 ++++++++++++++++++++++++ tmux.1 | 2 +- tmux.h | 1 + tty.c | 4 ++-- window-buffer.c | 12 ++++++++++++ window-client.c | 12 ++++++++++++ window-tree.c | 12 ++++++++++++ 7 files changed, 64 insertions(+), 3 deletions(-) diff --git a/server-client.c b/server-client.c index 3e256a92..f85304a6 100644 --- a/server-client.c +++ b/server-client.c @@ -42,6 +42,7 @@ static void server_client_repeat_timer(int, short, void *); static void server_client_click_timer(int, short, void *); static void server_client_check_exit(struct client *); static void server_client_check_redraw(struct client *); +static void server_client_check_modes(struct client *); static void server_client_set_title(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); @@ -1355,6 +1356,7 @@ server_client_loop(void) TAILQ_FOREACH(c, &clients, entry) { server_client_check_exit(c); if (c->session != NULL) { + server_client_check_modes(c); server_client_check_redraw(c); server_client_reset_state(c); } @@ -1810,6 +1812,28 @@ server_client_redraw_timer(__unused int fd, __unused short events, log_debug("redraw timer fired"); } +/* + * Check if modes need to be updated. Only modes in the current window are + * updated and it is done when the status line is redrawn. + */ +static void +server_client_check_modes(struct client *c) +{ + struct window *w = c->session->curw->window; + struct window_pane *wp; + struct window_mode_entry *wme; + + if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) + return; + if (~c->flags & CLIENT_REDRAWSTATUS) + return; + TAILQ_FOREACH(wp, &w->panes, entry) { + wme = TAILQ_FIRST(&wp->modes); + if (wme != NULL && wme->mode->update != NULL) + wme->mode->update(wme); + } +} + /* Check for client redraws. */ static void server_client_check_redraw(struct client *c) diff --git a/tmux.1 b/tmux.1 index 9d1021f3..f9a9cdf3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4643,7 +4643,7 @@ special characters or with an suffix, escape hash characters (so .Ql # becomes -.Ql ## ). +.Ql ## ) . .Ql E:\& will expand the format twice, for example .Ql #{E:status-left} diff --git a/tmux.h b/tmux.h index 1845775d..986eed00 100644 --- a/tmux.h +++ b/tmux.h @@ -887,6 +887,7 @@ struct window_mode { struct cmd_find_state *, struct args *); void (*free)(struct window_mode_entry *); void (*resize)(struct window_mode_entry *, u_int, u_int); + void (*update)(struct window_mode_entry *); void (*key)(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); diff --git a/tty.c b/tty.c index 777b639b..fac7a99e 100644 --- a/tty.c +++ b/tty.c @@ -2447,7 +2447,7 @@ tty_check_fg(struct tty *tty, int *palette, struct grid_cell *gc) /* Is this a 256-colour colour? */ if (gc->fg & COLOUR_FLAG_256) { /* And not a 256 colour mode? */ - if (colours != 256) { + if (colours < 256) { gc->fg = colour_256to16(gc->fg); if (gc->fg & 8) { gc->fg &= 7; @@ -2500,7 +2500,7 @@ tty_check_bg(struct tty *tty, int *palette, struct grid_cell *gc) * palette. Bold background doesn't exist portably, so just * discard the bold bit if set. */ - if (colours != 256) { + if (colours < 256) { gc->bg = colour_256to16(gc->bg); if (gc->bg & 8) { gc->bg &= 7; diff --git a/window-buffer.c b/window-buffer.c index ae6f13ce..4599cbc5 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -31,6 +31,7 @@ static struct screen *window_buffer_init(struct window_mode_entry *, static void window_buffer_free(struct window_mode_entry *); static void window_buffer_resize(struct window_mode_entry *, u_int, u_int); +static void window_buffer_update(struct window_mode_entry *); static void window_buffer_key(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); @@ -63,6 +64,7 @@ const struct window_mode window_buffer_mode = { .init = window_buffer_init, .free = window_buffer_free, .resize = window_buffer_resize, + .update = window_buffer_update, .key = window_buffer_key, }; @@ -335,6 +337,16 @@ window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy) mode_tree_resize(data->data, sx, sy); } +static void +window_buffer_update(struct window_mode_entry *wme) +{ + struct window_buffer_modedata *data = wme->data; + + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; +} + static void window_buffer_do_delete(void *modedata, void *itemdata, __unused struct client *c, __unused key_code key) diff --git a/window-client.c b/window-client.c index 5e02462b..ec3c646a 100644 --- a/window-client.c +++ b/window-client.c @@ -30,6 +30,7 @@ static struct screen *window_client_init(struct window_mode_entry *, static void window_client_free(struct window_mode_entry *); static void window_client_resize(struct window_mode_entry *, u_int, u_int); +static void window_client_update(struct window_mode_entry *); static void window_client_key(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); @@ -59,6 +60,7 @@ const struct window_mode window_client_mode = { .init = window_client_init, .free = window_client_free, .resize = window_client_resize, + .update = window_client_update, .key = window_client_key, }; @@ -311,6 +313,16 @@ window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy) mode_tree_resize(data->data, sx, sy); } +static void +window_client_update(struct window_mode_entry *wme) +{ + struct window_client_modedata *data = wme->data; + + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; +} + static void window_client_do_detach(void *modedata, void *itemdata, __unused struct client *c, key_code key) diff --git a/window-tree.c b/window-tree.c index a687af16..1498b337 100644 --- a/window-tree.c +++ b/window-tree.c @@ -29,6 +29,7 @@ static struct screen *window_tree_init(struct window_mode_entry *, static void window_tree_free(struct window_mode_entry *); static void window_tree_resize(struct window_mode_entry *, u_int, u_int); +static void window_tree_update(struct window_mode_entry *); static void window_tree_key(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); @@ -79,6 +80,7 @@ const struct window_mode window_tree_mode = { .init = window_tree_init, .free = window_tree_free, .resize = window_tree_resize, + .update = window_tree_update, .key = window_tree_key, }; @@ -937,6 +939,16 @@ window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy) mode_tree_resize(data->data, sx, sy); } +static void +window_tree_update(struct window_mode_entry *wme) +{ + struct window_tree_modedata *data = wme->data; + + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; +} + static char * window_tree_get_target(struct window_tree_itemdata *item, struct cmd_find_state *fs) From ed786309ccc7ceb2077f658e689a5f35549bb2bc Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 7 Dec 2020 09:23:57 +0000 Subject: [PATCH 0648/1006] Do not clear the wrapped flag on linefeeds if it is already set - this does not appear to be what applications want. GitHub issue 2478 and 2414. --- screen-write.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/screen-write.c b/screen-write.c index ab6a020c..92f1aa39 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1240,8 +1240,6 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) gl = grid_get_line(gd, gd->hsize + s->cy); if (wrapped) gl->flags |= GRID_LINE_WRAPPED; - else - gl->flags &= ~GRID_LINE_WRAPPED; log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, s->rupper, s->rlower); From f6095cad993293ec0f1988c1f6ae22921f9d2b2e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 7 Dec 2020 09:46:58 +0000 Subject: [PATCH 0649/1006] Do not include the status line size when working out the character for the pane status line. GitHub issue 2493. --- screen-redraw.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 50df2671..47c1a838 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -407,7 +407,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, struct format_tree *ft; char *expanded; int pane_status = rctx->pane_status; - u_int width, i, cell_type, top, px, py; + u_int width, i, cell_type, px, py; struct screen_write_ctx ctx; struct screen old; @@ -432,16 +432,12 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, screen_write_start(&ctx, &wp->status_screen); - if (rctx->statustop) - top = rctx->statuslines; - else - top = 0; for (i = 0; i < width; i++) { px = wp->xoff + 2 + i; if (rctx->pane_status == PANE_STATUS_TOP) - py = top + wp->yoff - 1; + py = wp->yoff - 1; else - py = top + wp->yoff + wp->sy; + py = wp->yoff + wp->sy; cell_type = screen_redraw_type_of_cell(c, px, py, pane_status); screen_redraw_border_set(wp, pane_lines, cell_type, &gc); screen_write_cell(&ctx, &gc); From 681c0d2bfbf22a51f90c977d0b3e91d30c23c11e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 7 Dec 2020 12:12:40 +0000 Subject: [PATCH 0650/1006] Include compat.h after system headers, GitHub issue 2492. --- compat/closefrom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compat/closefrom.c b/compat/closefrom.c index 28be3680..be008239 100644 --- a/compat/closefrom.c +++ b/compat/closefrom.c @@ -14,8 +14,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "compat.h" - #ifndef HAVE_CLOSEFROM #include @@ -50,6 +48,8 @@ # include #endif +#include "compat.h" + #ifndef OPEN_MAX # define OPEN_MAX 256 #endif From 8bd29a30bff4e9d50765e2168a7aad11e163ccde Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2020 08:31:50 +0000 Subject: [PATCH 0651/1006] Make synchronize-panes a pane option and add -U flag to set-option to unset an option on all panes. GitHub issue 2491 from Rostislav Nesin. --- cmd-set-option.c | 21 +++++++++++++++++---- format.c | 2 +- options-table.c | 2 +- tmux.1 | 19 ++++++++++++------- window.c | 29 ++++++++++++++++++----------- 5 files changed, 49 insertions(+), 24 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 0df12aa0..70e3c54d 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_set_option_entry = { .name = "set-option", .alias = "set", - .args = { "aFgopqst:uw", 1, 2 }, - .usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]", + .args = { "aFgopqst:uUw", 1, 2 }, + .usage = "[-aFgopqsuUw] " CMD_TARGET_PANE_USAGE " option [value]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -74,8 +74,9 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); struct cmd_find_state *target = cmdq_get_target(item); + struct window_pane *loop; struct options *oo; - struct options_entry *parent, *o; + struct options_entry *parent, *o, *po; char *name, *argument, *value = NULL, *cause; int window, idx, already, error, ambiguous; int scope; @@ -148,7 +149,19 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) } /* Change the option. */ - if (args_has(args, 'u')) { + if (args_has(args, 'U') && scope == OPTIONS_TABLE_WINDOW) { + TAILQ_FOREACH(loop, &target->w->panes, entry) { + po = options_get_only(loop->options, name); + if (po == NULL) + continue; + if (options_remove_or_default(po, idx, &cause) != 0) { + cmdq_error(item, "%s", cause); + free(cause); + goto fail; + } + } + } + if (args_has(args, 'u') || args_has(args, 'U')) { if (o == NULL) goto out; if (options_remove_or_default(o, idx, &cause) != 0) { diff --git a/format.c b/format.c index 98dc4cb7..3611efe9 100644 --- a/format.c +++ b/format.c @@ -3085,7 +3085,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add_cb(ft, "pane_in_mode", format_cb_pane_in_mode); format_add(ft, "pane_synchronized", "%d", - !!options_get_number(w->options, "synchronize-panes")); + !!options_get_number(wp->options, "synchronize-panes")); if (wp->searchstr != NULL) format_add(ft, "pane_search_string", "%s", wp->searchstr); diff --git a/options-table.c b/options-table.c index 56e1c895..34a4c57b 100644 --- a/options-table.c +++ b/options-table.c @@ -958,7 +958,7 @@ const struct options_table_entry options_table[] = { { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_num = 0, .text = "Whether typing should be sent to all panes simultaneously." }, diff --git a/tmux.1 b/tmux.1 index f9a9cdf3..344f3b72 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3136,7 +3136,7 @@ abc123 Commands which set options are as follows: .Bl -tag -width Ds .It Xo Ic set-option -.Op Fl aFgopqsuw +.Op Fl aFgopqsuUw .Op Fl t Ar target-pane .Ar option Ar value .Xc @@ -3169,6 +3169,11 @@ 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). +.Fl U +unsets an option (like +.Fl u ) +but if the option is a pane option also unsets the option on any panes in the +window. .Ar value depends on the option and may be a number, a string, or a flag (on, off, or omitted to toggle). @@ -4062,12 +4067,6 @@ see the section. Attributes are ignored. .Pp -.It Xo Ic synchronize-panes -.Op Ic on | off -.Xc -Duplicate input to any pane to all other panes in the same window (only -for panes that are not in any special mode). -.Pp .It Ic window-status-activity-style Ar style Set status line style for windows with an activity alert. For how to specify @@ -4190,6 +4189,12 @@ The pane may be reactivated with the .Ic respawn-pane command. .Pp +.It Xo Ic synchronize-panes +.Op Ic on | off +.Xc +Duplicate input to all other panes in the same window where this option is also +on (only for panes that are not in any mode). +.Pp .It Ic window-active-style Ar style Set the pane style when it is the active pane. For how to specify diff --git a/window.c b/window.c index 66298495..22986a04 100644 --- a/window.c +++ b/window.c @@ -1145,12 +1145,27 @@ window_pane_reset_mode_all(struct window_pane *wp) window_pane_reset_mode(wp); } +static void +window_pane_copy_key(struct window_pane *wp, key_code key) +{ + struct window_pane *loop; + + TAILQ_FOREACH(loop, &wp->window->panes, entry) { + if (loop != wp && + TAILQ_EMPTY(&loop->modes) && + loop->fd != -1 && + (~loop->flags & PANE_INPUTOFF) && + window_pane_visible(loop) && + options_get_number(loop->options, "synchronize-panes")) + input_key_pane(loop, key, NULL); + } +} + int window_pane_key(struct window_pane *wp, struct client *c, struct session *s, struct winlink *wl, key_code key, struct mouse_event *m) { struct window_mode_entry *wme; - struct window_pane *wp2; if (KEYC_IS_MOUSE(key) && m == NULL) return (-1); @@ -1172,16 +1187,8 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, if (KEYC_IS_MOUSE(key)) return (0); - if (options_get_number(wp->window->options, "synchronize-panes")) { - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (wp2 != wp && - TAILQ_EMPTY(&wp2->modes) && - wp2->fd != -1 && - (~wp2->flags & PANE_INPUTOFF) && - window_pane_visible(wp2)) - input_key_pane(wp2, key, NULL); - } - } + if (options_get_number(wp->options, "synchronize-panes")) + window_pane_copy_key(wp, key); return (0); } From c43f2dce1b0ca64b43f7614d4da52bc9f2c195fe Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Dec 2020 09:22:14 +0000 Subject: [PATCH 0652/1006] Break cursor movement in grid into a common set of functions that can handle line wrapping and so on in one place and use them for the obvious copy mode commands. From Anindya Mukherjee. --- Makefile | 4 +- grid-reader.c | 303 ++++++++++++++++++++++++++++++++++++++++++++++ tmux.h | 23 ++++ window-copy.c | 329 ++++++++++++++++++++++++++++---------------------- 4 files changed, 515 insertions(+), 144 deletions(-) create mode 100644 grid-reader.c diff --git a/Makefile b/Makefile index 7fb664ad..21141317 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,7 @@ SRCS= alerts.c \ file.c \ format.c \ format-draw.c \ + grid-reader.c \ grid-view.c \ grid.c \ input-keys.c \ @@ -123,8 +124,7 @@ SRCS= alerts.c \ window-customize.c \ window-tree.c \ window.c \ - xmalloc.c \ - xterm-keys.c + xmalloc.c CDIAGFLAGS+= -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CDIAGFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations diff --git a/grid-reader.c b/grid-reader.c new file mode 100644 index 00000000..a1af3aaa --- /dev/null +++ b/grid-reader.c @@ -0,0 +1,303 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2020 Anindya Mukherjee + * + * 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 "tmux.h" + +/* Initialise virtual cursor. */ +void +grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy) +{ + gr->gd = gd; + gr->cx = cx; + gr->cy = cy; +} + +/* Get cursor position from reader. */ +void +grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy) +{ + *cx = gr->cx; + *cy = gr->cy; +} + +/* Get length of line containing the cursor. */ +u_int +grid_reader_line_length(struct grid_reader *gr) +{ + return (grid_line_length(gr->gd, gr->cy)); +} + +/* Move cursor forward one position. */ +void +grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all) +{ + u_int px; + struct grid_cell gc; + + if (all) + px = gr->gd->sx; + else + px = grid_reader_line_length(gr); + + if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) { + grid_reader_cursor_start_of_line(gr, 0); + grid_reader_cursor_down(gr); + } else if (gr->cx < px) { + gr->cx++; + while (gr->cx < px) { + grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) + break; + gr->cx++; + } + } +} + +/* Move cursor back one position. */ +void +grid_reader_cursor_left(struct grid_reader *gr) +{ + struct grid_cell gc; + + while (gr->cx > 0) { + grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) + break; + gr->cx--; + } + if (gr->cx == 0 && gr->cy > 0) { + grid_reader_cursor_up(gr); + grid_reader_cursor_end_of_line(gr, 0, 0); + } else if (gr->cx > 0) + gr->cx--; +} + +/* Move cursor down one line. */ +void +grid_reader_cursor_down(struct grid_reader *gr) +{ + struct grid_cell gc; + + if (gr->cy < gr->gd->hsize + gr->gd->sy - 1) + gr->cy++; + while (gr->cx > 0) { + grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) + break; + gr->cx--; + } +} + +/* Move cursor up one line. */ +void +grid_reader_cursor_up(struct grid_reader *gr) +{ + struct grid_cell gc; + + if (gr->cy > 0) + gr->cy--; + while (gr->cx > 0) { + grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) + break; + gr->cx--; + } +} + +/* Move cursor to the start of the line. */ +void +grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap) +{ + if (wrap) { + while (gr->cy > 0 && + grid_get_line(gr->gd, gr->cy - 1)->flags & + GRID_LINE_WRAPPED) + gr->cy--; + } + gr->cx = 0; +} + +/* Move cursor to the end of the line. */ +void +grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all) +{ + u_int yy; + + if (wrap) { + yy = gr->gd->hsize + gr->gd->sy - 1; + while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags & + GRID_LINE_WRAPPED) + gr->cy++; + } + if (all) + gr->cx = gr->gd->sx; + else + gr->cx = grid_reader_line_length(gr); +} + +/* Check if character under cursor is in set. */ +int +grid_reader_in_set(struct grid_reader *gr, const char *set) +{ + struct grid_cell gc; + + grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); + if (gc.flags & GRID_FLAG_PADDING) + return (0); + return (utf8_cstrhas(set, &gc.data)); +} + +/* Move cursor to the start of the next word. */ +void +grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) +{ + u_int xx, yy; + int expected = 0; + + /* Do not break up wrapped words. */ + if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) + xx = grid_reader_line_length(gr) - 1; + else + xx = grid_reader_line_length(gr); + yy = gr->gd->hsize + gr->gd->sy - 1; + + /* + * If we started inside a word, skip over word characters. Then skip + * over separators till the next word. + * + * expected is initially set to 0 for the former and then 1 for the + * latter. It is finally set to 0 when the beginning of the next word is + * found. + */ + do { + while (gr->cx > xx || + grid_reader_in_set(gr, separators) == expected) { + /* Move down if we are past the end of the line. */ + if (gr->cx > xx) { + if (gr->cy == yy) + return; + grid_reader_cursor_start_of_line(gr, 0); + grid_reader_cursor_down(gr); + + if (grid_get_line(gr->gd, gr->cy)->flags & + GRID_LINE_WRAPPED) + xx = grid_reader_line_length(gr) - 1; + else + xx = grid_reader_line_length(gr); + } else + gr->cx++; + } + expected = !expected; + } while (expected == 1); +} + +/* Move cursor to the end of the next word. */ +void +grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) +{ + u_int xx, yy; + int expected = 1; + + /* Do not break up wrapped words. */ + if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) + xx = grid_reader_line_length(gr) - 1; + else + xx = grid_reader_line_length(gr); + yy = gr->gd->hsize + gr->gd->sy - 1; + + /* + * If we started on a separator, skip over separators. Then skip over + * word characters till the next separator. + * + * expected is initially set to 1 for the former and then 1 for the + * latter. It is finally set to 1 when the end of the next word is + * found. + */ + do { + while (gr->cx > xx || + grid_reader_in_set(gr, separators) == expected) { + /* Move down if we are past the end of the line. */ + if (gr->cx > xx) { + if (gr->cy == yy) + return; + grid_reader_cursor_start_of_line(gr, 0); + grid_reader_cursor_down(gr); + + if (grid_get_line(gr->gd, gr->cy)->flags & + GRID_LINE_WRAPPED) + xx = grid_reader_line_length(gr) - 1; + else + xx = grid_reader_line_length(gr); + } else + gr->cx++; + } + expected = !expected; + } while (expected == 0); +} + +/* Move to the previous place where a word begins. */ +void +grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, + int already) +{ + int oldx, oldy, r; + + /* Move back to the previous word character. */ + if (already || grid_reader_in_set(gr, separators)) { + for (;;) { + if (gr->cx > 0) { + gr->cx--; + if (!grid_reader_in_set(gr, separators)) + break; + } else { + if (gr->cy == 0) + return; + grid_reader_cursor_up(gr); + grid_reader_cursor_end_of_line(gr, 0, 0); + + /* Stop if separator at EOL. */ + if (gr->cx > 0) { + oldx = gr->cx; + gr->cx--; + r = grid_reader_in_set(gr, separators); + gr->cx = oldx; + if (r) + break; + } + } + } + } + + /* Move back to the beginning of this word. */ + do { + oldx = gr->cx; + oldy = gr->cy; + if (gr->cx == 0) { + if (gr->cy == 0 || + ~grid_get_line(gr->gd, gr->cy - 1)->flags & + GRID_LINE_WRAPPED) + break; + grid_reader_cursor_up(gr); + grid_reader_cursor_end_of_line(gr, 0, 0); + } + if (gr->cx > 0) + gr->cx--; + } while (!grid_reader_in_set(gr, separators)); + gr->cx = oldx; + gr->cy = oldy; +} diff --git a/tmux.h b/tmux.h index 986eed00..0defff34 100644 --- a/tmux.h +++ b/tmux.h @@ -726,6 +726,13 @@ struct grid { struct grid_line *linedata; }; +/* Virtual cursor in a grid. */ +struct grid_reader { + struct grid *gd; + u_int cx; + u_int cy; +}; + /* Style alignment. */ enum style_align { STYLE_ALIGN_DEFAULT, @@ -2548,6 +2555,22 @@ void grid_wrap_position(struct grid *, u_int, u_int, u_int *, u_int *); void grid_unwrap_position(struct grid *, u_int *, u_int *, u_int, u_int); u_int grid_line_length(struct grid *, u_int); +/* grid-reader.c */ +void grid_reader_start(struct grid_reader *, struct grid *, u_int, u_int); +void grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *); +u_int grid_reader_line_length(struct grid_reader *); +int grid_reader_in_set(struct grid_reader *, const char *); +void grid_reader_cursor_right(struct grid_reader *, int, int); +void grid_reader_cursor_left(struct grid_reader *); +void grid_reader_cursor_down(struct grid_reader *); +void grid_reader_cursor_up(struct grid_reader *); +void grid_reader_cursor_start_of_line(struct grid_reader *, int); +void grid_reader_cursor_end_of_line(struct grid_reader *, int, int); +void grid_reader_cursor_next_word(struct grid_reader *, const char *); +void grid_reader_cursor_next_word_end(struct grid_reader *, const char *); +void grid_reader_cursor_previous_word(struct grid_reader *, const char *, + int); + /* grid-view.c */ void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_view_set_cell(struct grid *, u_int, u_int, diff --git a/window-copy.c b/window-copy.c index 6dc03b34..764f6b5b 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3993,18 +3993,31 @@ window_copy_cursor_start_of_line(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; - struct grid *gd = back_s->grid; - u_int py; + struct grid_reader gr; + u_int px, py, cy, yy, ny, hsize; - if (data->cx == 0 && data->lineflag == LINE_SEL_NONE) { - py = screen_hsize(back_s) + data->cy - data->oy; - while (py > 0 && - grid_get_line(gd, py - 1)->flags & GRID_LINE_WRAPPED) { - window_copy_cursor_up(wme, 0); - py = screen_hsize(back_s) + data->cy - data->oy; - } + px = data->cx; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + + grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_start_of_line(&gr, 1); + grid_reader_get_cursor(&gr, &px, &py); + + /* Scroll up if we went off the visible screen. */ + yy = hsize - data->oy; + if (py < yy) { + ny = yy - py; + cy = 0; + } else { + ny = 0; + cy = py - yy; } - window_copy_update_cursor(wme, 0, data->cy); + while (ny > 0) { + window_copy_cursor_up(wme, 1); + ny--; + } + window_copy_update_cursor(wme, px, cy); if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -4037,30 +4050,35 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; - struct grid *gd = back_s->grid; - struct grid_line *gl; - u_int px, py; + struct grid_reader gr; + u_int px, py, cy, yy, ny, hsize; - py = screen_hsize(back_s) + data->cy - data->oy; - px = window_copy_find_length(wme, py); + px = data->cx; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; - if (data->cx == px && data->lineflag == LINE_SEL_NONE) { - if (data->screen.sel != NULL && data->rectflag) - px = screen_size_x(back_s); - gl = grid_get_line(gd, py); - if (gl->flags & GRID_LINE_WRAPPED) { - while (py < gd->sy + gd->hsize) { - gl = grid_get_line(gd, py); - if (~gl->flags & GRID_LINE_WRAPPED) - break; - window_copy_cursor_down(wme, 0); - py = screen_hsize(back_s) + data->cy - data->oy; - } - px = window_copy_find_length(wme, py); - } + grid_reader_start(&gr, back_s->grid, px, py); + if (data->screen.sel != NULL && data->rectflag) + grid_reader_cursor_end_of_line(&gr, 1, 1); + else + grid_reader_cursor_end_of_line(&gr, 1, 0); + grid_reader_get_cursor(&gr, &px, &py); + + /* Scroll down if we went off the visible screen. */ + cy = py - hsize + data->oy; + yy = screen_size_y(back_s) - 1; + if (cy > yy) + ny = cy - yy; + else + ny = 0; + while (ny > 0) { + window_copy_cursor_down(wme, 1); + ny--; } - window_copy_update_cursor(wme, px, data->cy); - + if (cy > yy) + window_copy_update_cursor(wme, px, yy); + else + window_copy_update_cursor(wme, px, cy); if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -4120,57 +4138,69 @@ static void window_copy_cursor_left(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; - u_int py, cx; - struct grid_cell gc; + struct screen *back_s = data->backing; + struct grid_reader gr; + u_int px, py, cy, yy, ny, hsize; - py = screen_hsize(data->backing) + data->cy - data->oy; - cx = data->cx; - while (cx > 0) { - grid_get_cell(data->backing->grid, cx, py, &gc); - if (~gc.flags & GRID_FLAG_PADDING) - break; - cx--; + px = data->cx; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + + grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_left(&gr); + grid_reader_get_cursor(&gr, &px, &py); + + /* Scroll up if we went off the visible screen. */ + yy = hsize - data->oy; + if (py < yy) { + ny = yy - py; + cy = 0; + } else { + ny = 0; + cy = py - yy; } - if (cx == 0 && py > 0) { - window_copy_cursor_up(wme, 0); - window_copy_cursor_end_of_line(wme); - } else if (cx > 0) { - window_copy_update_cursor(wme, cx - 1, data->cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); + while (ny > 0) { + window_copy_cursor_up(wme, 1); + ny--; } + window_copy_update_cursor(wme, px, cy); + if (window_copy_update_selection(wme, 1, 0)) + window_copy_redraw_lines(wme, data->cy, 1); } static void 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; - struct grid_cell gc; + struct screen *back_s = data->backing; + struct grid_reader gr; + u_int px, py, cy, yy, ny, hsize; - py = screen_hsize(data->backing) + data->cy - data->oy; - yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; - if (all || (data->screen.sel != NULL && data->rectflag)) - px = screen_size_x(&data->screen); + px = data->cx; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + + grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_right(&gr, 1, all); + grid_reader_get_cursor(&gr, &px, &py); + + /* Scroll down if we went off the visible screen. */ + cy = py - hsize + data->oy; + yy = screen_size_y(back_s) - 1; + if (cy > yy) + ny = cy - yy; else - px = window_copy_find_length(wme, py); - - if (data->cx >= px && py < yy) { - window_copy_cursor_start_of_line(wme); - window_copy_cursor_down(wme, 0); - } else if (data->cx < px) { - cx = data->cx + 1; - cy = screen_hsize(data->backing) + data->cy - data->oy; - while (cx < px) { - grid_get_cell(data->backing->grid, cx, cy, &gc); - if (~gc.flags & GRID_FLAG_PADDING) - break; - cx++; - } - window_copy_update_cursor(wme, cx, data->cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); + ny = 0; + while (ny > 0) { + window_copy_cursor_down(wme, 1); + ny--; } + if (cy > yy) + window_copy_update_cursor(wme, px, yy); + else + window_copy_update_cursor(wme, px, cy); + if (window_copy_update_selection(wme, 1, 0)) + window_copy_redraw_lines(wme, data->cy, 1); } static void @@ -4214,13 +4244,30 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) - window_copy_cursor_end_of_line(wme); + { + window_copy_update_cursor(wme, px, data->cy); + if (window_copy_update_selection(wme, 1, 0)) + window_copy_redraw_lines(wme, data->cy, 1); + } } if (data->lineflag == LINE_SEL_LEFT_RIGHT) - window_copy_cursor_end_of_line(wme); + { + py = screen_hsize(data->backing) + data->cy - data->oy; + if (data->rectflag) + px = screen_size_x(data->backing); + else + px = window_copy_find_length(wme, py); + window_copy_update_cursor(wme, px, data->cy); + if (window_copy_update_selection(wme, 1, 0)) + window_copy_redraw_lines(wme, data->cy, 1); + } else if (data->lineflag == LINE_SEL_RIGHT_LEFT) - window_copy_cursor_start_of_line(wme); + { + window_copy_update_cursor(wme, 0, data->cy); + if (window_copy_update_selection(wme, 1, 0)) + window_copy_redraw_lines(wme, data->cy, 1); + } } static void @@ -4256,13 +4303,30 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) - window_copy_cursor_end_of_line(wme); + { + window_copy_update_cursor(wme, px, data->cy); + if (window_copy_update_selection(wme, 1, 0)) + window_copy_redraw_lines(wme, data->cy, 1); + } } if (data->lineflag == LINE_SEL_LEFT_RIGHT) - window_copy_cursor_end_of_line(wme); + { + py = screen_hsize(data->backing) + data->cy - data->oy; + if (data->rectflag) + px = screen_size_x(data->backing); + else + px = window_copy_find_length(wme, py); + window_copy_update_cursor(wme, px, data->cy); + if (window_copy_update_selection(wme, 1, 0)) + window_copy_redraw_lines(wme, data->cy, 1); + } else if (data->lineflag == LINE_SEL_RIGHT_LEFT) - window_copy_cursor_start_of_line(wme); + { + window_copy_update_cursor(wme, 0, data->cy); + if (window_copy_update_selection(wme, 1, 0)) + window_copy_redraw_lines(wme, data->cy, 1); + } } static void @@ -4476,46 +4540,38 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct options *oo = wp->window->options; struct screen *back_s = data->backing; - u_int px, py, xx, yy; - int keys, expected = 1; + struct grid_reader gr; + u_int px, py, cy, yy, ny, hsize; + int keys; px = data->cx; - py = screen_hsize(back_s) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); - yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + grid_reader_start(&gr, back_s->grid, px, py); keys = options_get_number(oo, "mode-keys"); - if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators)) - px++; + if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators)) + grid_reader_cursor_right(&gr, 0, 0); + grid_reader_cursor_next_word_end(&gr, separators); + if (keys == MODEKEY_VI) + grid_reader_cursor_left(&gr); + grid_reader_get_cursor(&gr, &px, &py); - /* - * First skip past any word characters, then any nonword characters. - * - * expected is initially set to 1 for the former and then 0 for the - * latter. - */ - do { - while (px > xx || - window_copy_in_set(wme, px, py, separators) == expected) { - /* Move down if we're past the end of the line. */ - if (px > xx) { - if (py == yy) - return; - window_copy_cursor_down(wme, 0); - px = 0; - - py = screen_hsize(back_s) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); - } else - px++; - } - expected = !expected; - } while (expected == 0); - - if (keys == MODEKEY_VI && px != 0) - px--; - - window_copy_update_cursor(wme, px, data->cy); + /* Scroll down if we went off the visible screen. */ + cy = py - hsize + data->oy; + yy = screen_size_y(back_s) - 1; + if (cy > yy) + ny = cy - yy; + else + ny = 0; + while (ny > 0) { + window_copy_cursor_down(wme, 1); + ny--; + } + if (cy > yy) + window_copy_update_cursor(wme, px, yy); + else + window_copy_update_cursor(wme, px, cy); if (window_copy_update_selection(wme, 1, no_reset)) window_copy_redraw_lines(wme, data->cy, 1); } @@ -4572,43 +4628,32 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, const char *separators, int already) { struct window_copy_mode_data *data = wme->data; - u_int px, py, hsize; + struct screen *back_s = data->backing; + struct grid_reader gr; + u_int px, py, cy, yy, ny, hsize; - hsize = screen_hsize(data->backing); px = data->cx; + hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; - /* Move back to the previous word character. */ - if (already || window_copy_in_set(wme, px, py, separators)) { - for (;;) { - if (px > 0) { - px--; - if (!window_copy_in_set(wme, px, py, - separators)) - break; - } else { - if (data->cy == 0 && - (hsize == 0 || data->oy > hsize - 1)) - goto out; - window_copy_cursor_up(wme, 0); + grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_previous_word(&gr, separators, already); + grid_reader_get_cursor(&gr, &px, &py); - py = hsize + data->cy - data->oy; - px = window_copy_find_length(wme, py); - - /* Stop if separator at EOL. */ - if (px > 0 && window_copy_in_set(wme, px - 1, - py, separators)) - break; - } - } + /* Scroll up if we went off the visible screen. */ + yy = hsize - data->oy; + if (py < yy) { + ny = yy - py; + cy = 0; + } else { + ny = 0; + cy = py - yy; } - - /* Move back to the beginning of this word. */ - while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators)) - px--; - -out: - window_copy_update_cursor(wme, px, data->cy); + while (ny > 0) { + window_copy_cursor_up(wme, 1); + ny--; + } + window_copy_update_cursor(wme, px, cy); if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy, 1); } From d936fde7ef62863fbb8b142c3ee229cb460dbfdb Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 24 Dec 2020 22:22:10 +0000 Subject: [PATCH 0653/1006] Makefile.am: add grid-reader.c Add grid-reader.c to Makefile.am so it's included for compilation. --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index b40e0944..e9b1cb15 100644 --- a/Makefile.am +++ b/Makefile.am @@ -141,6 +141,7 @@ dist_tmux_SOURCES = \ file.c \ format.c \ format-draw.c \ + grid-reader.c \ grid-view.c \ grid.c \ input-keys.c \ From c68baaad98807963da954bc1aa77c22f2df05ff0 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Dec 2020 09:36:26 +0000 Subject: [PATCH 0654/1006] Remove current match indicator which can't work anymore since we only search the visible region. From Anindya Mukherjee, GitHub issue 2508. --- window-copy.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/window-copy.c b/window-copy.c index 764f6b5b..d6af397f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -272,7 +272,7 @@ struct window_copy_mode_data { u_char *searchmark; int searchcount; int searchmore; - int searchthis; + int searchall; int searchx; int searchy; int searcho; @@ -396,6 +396,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->searchstr = NULL; } data->searchx = data->searchy = data->searcho = -1; + data->searchall = 1; data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; @@ -2334,9 +2335,6 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) { window_copy_clear_marks(wme); data->searchx = data->searchy = -1; - } else if (data->searchthis != -1) { - data->searchthis = -1; - action = WINDOW_COPY_CMD_REDRAW; } if (action == WINDOW_COPY_CMD_NOTHING) action = WINDOW_COPY_CMD_REDRAW; @@ -2929,9 +2927,11 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex, if (data->timeout) return (0); - if (wp->searchstr == NULL || wp->searchregex != regex) + if (data->searchall || wp->searchstr == NULL || + wp->searchregex != regex) { visible_only = 0; - else + data->searchall = 0; + } else visible_only = (strcmp(wp->searchstr, str) == 0); free(wp->searchstr); wp->searchstr = xstrdup(str); @@ -3116,7 +3116,6 @@ again: if (!visible_only) { if (stopped) { - data->searchthis = -1; if (nfound > 1000) data->searchcount = 1000; else if (nfound > 100) @@ -3127,10 +3126,6 @@ again: data->searchcount = -1; data->searchmore = 1; } else { - if (which != -1) - data->searchthis = 1 + nfound - which; - else - data->searchthis = -1; data->searchcount = nfound; data->searchmore = 0; } @@ -3366,15 +3361,11 @@ window_copy_write_line(struct window_mode_entry *wme, if (data->searchcount == -1) { size = xsnprintf(hdr, sizeof hdr, "[%u/%u]", data->oy, hsize); - } else if (data->searchthis == -1) { + } else { size = xsnprintf(hdr, sizeof hdr, "(%d%s results) [%u/%u]", data->searchcount, data->searchmore ? "+" : "", data->oy, hsize); - } else { - size = xsnprintf(hdr, sizeof hdr, - "(%d/%d results) [%u/%u]", data->searchthis, - data->searchcount, data->oy, hsize); } } if (size > screen_size_x(s)) From a98ee00dd988b813b0b0cba1af939978505936b7 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Dec 2020 09:40:27 +0000 Subject: [PATCH 0655/1006] Do not list user options with show-hooks. --- cmd-show-options.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cmd-show-options.c b/cmd-show-options.c index 8e70eaa9..a9c5bd2a 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -201,11 +201,13 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, u_int idx; int parent; - o = options_first(oo); - while (o != NULL) { - if (options_table_entry(o) == NULL) - cmd_show_options_print(self, item, o, -1, 0); - o = options_next(o); + if (cmd_get_entry(self) != &cmd_show_hooks_entry) { + o = options_first(oo); + while (o != NULL) { + if (options_table_entry(o) == NULL) + cmd_show_options_print(self, item, o, -1, 0); + o = options_next(o); + } } for (oe = options_table; oe->name != NULL; oe++) { if (~oe->scope & scope) From f97305af317daded5d234ea1d3fcea78974f1200 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 30 Dec 2020 18:29:40 +0000 Subject: [PATCH 0656/1006] Use right format for session loop, GitHub issue 2519. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index 3611efe9..2d7402af 100644 --- a/format.c +++ b/format.c @@ -1876,7 +1876,7 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt) RB_FOREACH(s, sessions, &sessions) { format_log(es, "session loop: $%u", s->id); nft = format_create(c, item, FORMAT_NONE, ft->flags); - format_defaults(next.ft, ft->c, s, NULL, NULL); + format_defaults(nft, ft->c, s, NULL, NULL); format_copy_state(&next, es, 0); next.ft = nft; expanded = format_expand1(&next, fmt); From 606bd5f8c6be326ea2e4746de5d5e007fd04eef8 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 1 Jan 2021 08:36:51 +0000 Subject: [PATCH 0657/1006] Add a -C flag to run-shell to use a tmux command rather than a shell command. --- cmd-run-shell.c | 85 +++++++++++++++++++++++++++++++++++++------------ tmux.1 | 23 ++++++++----- 2 files changed, 80 insertions(+), 28 deletions(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 647f533f..b259276d 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -40,8 +40,8 @@ const struct cmd_entry cmd_run_shell_entry = { .name = "run-shell", .alias = "run", - .args = { "bd:t:", 0, 1 }, - .usage = "[-b] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", + .args = { "bd:Ct:", 0, 1 }, + .usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -50,13 +50,16 @@ const struct cmd_entry cmd_run_shell_entry = { }; struct cmd_run_shell_data { + struct client *client; char *cmd; + int shell; char *cwd; struct cmdq_item *item; struct session *s; int wp_id; struct event timer; int flags; + struct cmd_parse_input pi; }; static void @@ -93,49 +96,69 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); struct cmd_run_shell_data *cdata; + struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct window_pane *wp = target->wp; const char *delay; double d; struct timeval tv; char *end; + int wait = !args_has(args, 'b'); + + if ((delay = args_get(args, 'd')) != NULL) { + d = strtod(delay, &end); + if (*end != '\0') { + cmdq_error(item, "invalid delay time: %s", delay); + return (CMD_RETURN_ERROR); + } + } else if (args->argc == 0) + return (CMD_RETURN_NORMAL); cdata = xcalloc(1, sizeof *cdata); if (args->argc != 0) cdata->cmd = format_single_from_target(item, args->argv[0]); + cdata->shell = !args_has(args, 'C'); + if (!cdata->shell) { + memset(&cdata->pi, 0, sizeof cdata->pi); + cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); + if (wait) + cdata->pi.item = item; + cdata->pi.c = tc; + cmd_find_copy_state(&cdata->pi.fs, target); + } + if (args_has(args, 't') && wp != NULL) cdata->wp_id = wp->id; else cdata->wp_id = -1; - if (!args_has(args, 'b')) + if (wait) { + cdata->client = cmdq_get_client(item); cdata->item = item; - else + } else { + cdata->client = tc; cdata->flags |= JOB_NOWAIT; + } + if (cdata->client != NULL) + cdata->client->references++; cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s)); + cdata->s = s; if (s != NULL) session_add_ref(s, __func__); evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata); - - if ((delay = args_get(args, 'd')) != NULL) { - d = strtod(delay, &end); - if (*end != '\0') { - cmdq_error(item, "invalid delay time: %s", delay); - cmd_run_shell_free(cdata); - return (CMD_RETURN_ERROR); - } + if (delay != NULL) { timerclear(&tv); tv.tv_sec = (time_t)d; tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U; evtimer_add(&cdata->timer, &tv); } else - cmd_run_shell_timer(-1, 0, cdata); + event_active(&cdata->timer, EV_TIMEOUT, 1); - if (args_has(args, 'b')) + if (!wait) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } @@ -144,17 +167,37 @@ static void cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) { struct cmd_run_shell_data *cdata = arg; + struct client *c = cdata->client; + const char *cmd = cdata->cmd; + char *error; + struct cmdq_item *item = cdata->item; + enum cmd_parse_status status; - if (cdata->cmd != NULL) { - if (job_run(cdata->cmd, cdata->s, cdata->cwd, NULL, + if (cmd != NULL && cdata->shell) { + if (job_run(cmd, cdata->s, cdata->cwd, NULL, cmd_run_shell_callback, cmd_run_shell_free, cdata, cdata->flags, -1, -1) == NULL) cmd_run_shell_free(cdata); - } else { - if (cdata->item != NULL) - cmdq_continue(cdata->item); - cmd_run_shell_free(cdata); + return; } + + if (cmd != NULL) { + if (item != NULL) { + status = cmd_parse_and_insert(cmd, &cdata->pi, item, + cmdq_get_state(item), &error); + } else { + status = cmd_parse_and_append(cmd, &cdata->pi, c, NULL, + &error); + } + if (status == CMD_PARSE_ERROR) { + cmdq_error(cdata->item, "%s", error); + free(error); + } + } + + if (cdata->item != NULL) + cmdq_continue(cdata->item); + cmd_run_shell_free(cdata); } static void @@ -215,6 +258,8 @@ cmd_run_shell_free(void *data) evtimer_del(&cdata->timer); if (cdata->s != NULL) session_remove_ref(cdata->s, __func__); + if (cdata->client != NULL) + server_client_unref(cdata->client); free(cdata->cwd); free(cdata->cmd); free(cdata); diff --git a/tmux.1 b/tmux.1 index 344f3b72..1376df95 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5827,7 +5827,7 @@ Lock each client individually by running the command specified by the .Ic lock-command option. .It Xo Ic run-shell -.Op Fl b +.Op Fl bC .Op Fl d Ar delay .Op Fl t Ar target-pane .Op Ar shell-command @@ -5835,9 +5835,14 @@ option. .D1 (alias: Ic run ) Execute .Ar shell-command -in the background without creating a window. -Before being executed, shell-command is expanded using the rules specified in -the +or (with +.Fl C ) +a +.Nm +command in the background without creating a window. +Before being executed, +.Ar shell-command +is expanded using the rules specified in the .Sx FORMATS section. With @@ -5847,11 +5852,13 @@ the command is run in the background. waits for .Ar delay seconds before starting the command. -After the command finishes, any output to stdout is displayed in view mode (in -the pane specified by +If +.Fl C +is not given, any output to stdout is displayed in view mode (in the pane +specified by .Fl t -or the current pane if omitted). -If the command doesn't return success, the exit status is also displayed. +or the current pane if omitted) after the command finishes. +If the command fails, the exit status is also displayed. .It Xo Ic wait-for .Op Fl L | S | U .Ar channel From bd0fb22f0a206a73ec3f41322571d56d9d7d7c5d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 4 Jan 2021 08:43:16 +0000 Subject: [PATCH 0658/1006] Add a variant of remain-on-exit that only keeps the pane if the program failed, GitHub issue 2513. --- options-table.c | 8 ++++++-- server-fn.c | 16 ++++++++++++---- tmux.1 | 5 ++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/options-table.c b/options-table.c index 34a4c57b..a8276c86 100644 --- a/options-table.c +++ b/options-table.c @@ -69,6 +69,9 @@ static const char *options_table_set_clipboard_list[] = { static const char *options_table_window_size_list[] = { "largest", "smallest", "manual", "latest", NULL }; +static const char *options_table_remain_on_exit_list[] = { + "off", "on", "failed", NULL +}; /* Status line format. */ #define OPTIONS_TABLE_STATUS_FORMAT1 \ @@ -949,11 +952,12 @@ const struct options_table_entry options_table[] = { }, { .name = "remain-on-exit", - .type = OPTIONS_TABLE_FLAG, + .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .choices = options_table_remain_on_exit_list, .default_num = 0, .text = "Whether panes should remain ('on') or be automatically " - "killed ('off') when the program inside exits." + "killed ('off' or 'failed') when the program inside exits." }, { .name = "synchronize-panes", diff --git a/server-fn.c b/server-fn.c index d2465569..d0e06c0f 100644 --- a/server-fn.c +++ b/server-fn.c @@ -314,6 +314,7 @@ server_destroy_pane(struct window_pane *wp, int notify) struct grid_cell gc; time_t t; char tim[26]; + int remain_on_exit; if (wp->fd != -1) { bufferevent_free(wp->event); @@ -322,10 +323,17 @@ server_destroy_pane(struct window_pane *wp, int notify) wp->fd = -1; } - if (options_get_number(wp->options, "remain-on-exit")) { - if (~wp->flags & PANE_STATUSREADY) - return; - + remain_on_exit = options_get_number(wp->options, "remain-on-exit"); + if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY)) + return; + switch (remain_on_exit) { + case 0: + break; + case 2: + if (WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0) + break; + /* FALLTHROUGH */ + case 1: if (wp->flags & PANE_STATUSDRAWN) return; wp->flags |= PANE_STATUSDRAWN; diff --git a/tmux.1 b/tmux.1 index 1376df95..941305e3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4181,10 +4181,13 @@ interactive application starts and restores it on exit, so that any output visible before the application starts reappears unchanged after it exits. .Pp .It Xo Ic remain-on-exit -.Op Ic on | off +.Op Ic on | off | failed .Xc A pane with this flag set is not destroyed when the program running in it exits. +If set to +.Ic failed , +then only when the program exit status is not zero. The pane may be reactivated with the .Ic respawn-pane command. From ccb8b9eb2a32fef1c28d45147ccc8a3ff10e656c Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 Jan 2021 07:29:49 +0000 Subject: [PATCH 0659/1006] Remove unused variable, from Ben Boeckel. --- window-copy.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/window-copy.c b/window-copy.c index d6af397f..4c81cb1f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3009,7 +3009,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; - int found, cis, which = -1, stopped = 0; + int found, cis, stopped = 0; int cflags = REG_EXTENDED; u_int px, py, i, b, nfound = 0, width; u_int ssize = 1, start, end; @@ -3072,11 +3072,7 @@ again: if (!found) break; } - nfound++; - if (px == data->cx && - py == gd->hsize + data->cy - data->oy) - which = nfound; if (window_copy_search_mark_at(data, px, py, &b) == 0) { if (b + width > gd->sx * gd->sy) @@ -3088,7 +3084,6 @@ again: else data->searchgen++; } - px += width; } From 199689954b310a1915f573156f94cb93457c71f9 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 Jan 2021 07:32:23 +0000 Subject: [PATCH 0660/1006] Insert joined pane before the target pane with -b, like for split. From Takeshi Banse. --- cmd-join-pane.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 904d2c29..fc95a6b8 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -143,7 +143,10 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) src_wp->window = dst_w; options_set_parent(src_wp->options, dst_w->options); src_wp->flags |= PANE_STYLECHANGED; - TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); + if (flags & SPAWN_BEFORE) + TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry); + else + TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); layout_assign_pane(lc, src_wp); recalculate_sizes(); From b96c5e3687d36cea7b575a7e151326f1b82824d2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 Jan 2021 08:22:10 +0000 Subject: [PATCH 0661/1006] With incremental search, start empty and only repeat the previous search if the user tries to search again with an empty prompt. This matches emacs behaviour more closely. --- status.c | 46 +++++++++++++++++++++++++++++++--------------- window-copy.c | 4 ++++ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/status.c b/status.c index 668c0a3b..82107c58 100644 --- a/status.c +++ b/status.c @@ -543,7 +543,7 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, prompt_free_cb freecb, void *data, int flags) { struct format_tree *ft; - char *tmp, *cp; + char *tmp; if (fs != NULL) ft = format_create_from_state(NULL, c, fs); @@ -563,7 +563,13 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, c->prompt_string = format_expand_time(ft, msg); - c->prompt_buffer = utf8_fromcstr(tmp); + if (flags & PROMPT_INCREMENTAL) { + c->prompt_last = xstrdup(tmp); + c->prompt_buffer = utf8_fromcstr(""); + } else { + c->prompt_last = NULL; + c->prompt_buffer = utf8_fromcstr(tmp); + } c->prompt_index = utf8_strlen(c->prompt_buffer); c->prompt_inputcb = inputcb; @@ -579,11 +585,8 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_REDRAWSTATUS; - if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') { - xasprintf(&cp, "=%s", tmp); - c->prompt_inputcb(c, c->prompt_data, cp, 0); - free(cp); - } + if (flags & PROMPT_INCREMENTAL) + c->prompt_inputcb(c, c->prompt_data, "=", 0); free(tmp); format_free(ft); @@ -599,6 +602,9 @@ status_prompt_clear(struct client *c) if (c->prompt_freecb != NULL && c->prompt_data != NULL) c->prompt_freecb(c->prompt_data); + free(c->prompt_last); + c->prompt_last = NULL; + free(c->prompt_string); c->prompt_string = NULL; @@ -1260,17 +1266,27 @@ process_key: status_prompt_clear(c); break; case '\022': /* C-r */ - if (c->prompt_flags & PROMPT_INCREMENTAL) { + if (~c->prompt_flags & PROMPT_INCREMENTAL) + break; + if (c->prompt_buffer[0].size == 0) { + prefix = '='; + free (c->prompt_buffer); + c->prompt_buffer = utf8_fromcstr(c->prompt_last); + c->prompt_index = utf8_strlen(c->prompt_buffer); + } else prefix = '-'; - goto changed; - } - break; + goto changed; case '\023': /* C-s */ - if (c->prompt_flags & PROMPT_INCREMENTAL) { + if (~c->prompt_flags & PROMPT_INCREMENTAL) + break; + if (c->prompt_buffer[0].size == 0) { + prefix = '='; + free (c->prompt_buffer); + c->prompt_buffer = utf8_fromcstr(c->prompt_last); + c->prompt_index = utf8_strlen(c->prompt_buffer); + } else prefix = '+'; - goto changed; - } - break; + goto changed; default: goto append_key; } diff --git a/window-copy.c b/window-copy.c index 4c81cb1f..bfa94aed 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2030,6 +2030,8 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->timeout = 0; + log_debug ("%s: %s", __func__, argument); + prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; @@ -2083,6 +2085,8 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->timeout = 0; + log_debug ("%s: %s", __func__, argument); + prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; From a75aca4d6ad71e45601b2b45f79036fae4f0d2a6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 Jan 2021 10:09:44 +0000 Subject: [PATCH 0662/1006] Missed from last commit. --- tmux.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.h b/tmux.h index 0defff34..826d8ea4 100644 --- a/tmux.h +++ b/tmux.h @@ -1693,6 +1693,7 @@ struct client { char *prompt_string; struct utf8_data *prompt_buffer; + char *prompt_last; size_t prompt_index; prompt_input_cb prompt_inputcb; prompt_free_cb prompt_freecb; From 71c590a37f98d82c72279eddae74f9b8be146202 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 17 Jan 2021 16:17:41 +0000 Subject: [PATCH 0663/1006] Add -N flag to never start server even if command would normally do so, GitHub issue 2523. --- client.c | 2 ++ tmux.1 | 5 +++++ tmux.c | 7 +++++-- tmux.h | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/client.c b/client.c index dcbc0d18..a3e300cd 100644 --- a/client.c +++ b/client.c @@ -127,6 +127,8 @@ retry: log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; + if (flags & CLIENT_NOSTARTSERVER) + goto failed; if (~flags & CLIENT_STARTSERVER) goto failed; close(fd); diff --git a/tmux.1 b/tmux.1 index 941305e3..93842482 100644 --- a/tmux.1 +++ b/tmux.1 @@ -191,6 +191,11 @@ directories are missing). Behave as a login shell. This flag currently has no effect and is for compatibility with other shells when using tmux as a login shell. +.It Fl N +Do not start the server even if the command would normally do so (for example +.Ic new-session +or +.Ic start-server ) . .It Fl S Ar socket-path Specify a full alternative path to the server socket. If diff --git a/tmux.c b/tmux.c index b7fc5121..5861e66b 100644 --- a/tmux.c +++ b/tmux.c @@ -57,7 +57,7 @@ static __dead void usage(void) { fprintf(stderr, - "usage: %s [-2CDluvV] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2CDlNuvV] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [-T features] [command [flags]]\n", getprogname()); exit(1); @@ -350,7 +350,7 @@ main(int argc, char **argv) if (**argv == '-') flags = CLIENT_LOGIN; - while ((opt = getopt(argc, argv, "2c:CDdf:lL:qS:T:uUvV")) != -1) { + while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) { switch (opt) { case '2': tty_add_features(&feat, "256", ":,"); @@ -380,6 +380,9 @@ main(int argc, char **argv) free(label); label = xstrdup(optarg); break; + case 'N': + flags |= CLIENT_NOSTARTSERVER; + break; case 'q': break; case 'S': diff --git a/tmux.h b/tmux.h index 826d8ea4..832918e2 100644 --- a/tmux.h +++ b/tmux.h @@ -1635,7 +1635,7 @@ struct client { #define CLIENT_DEAD 0x200 #define CLIENT_REDRAWBORDERS 0x400 #define CLIENT_READONLY 0x800 -/* 0x1000 unused */ +#define CLIENT_NOSTARTSERVER 0x1000 #define CLIENT_CONTROL 0x2000 #define CLIENT_CONTROLCONTROL 0x4000 #define CLIENT_FOCUSED 0x8000 From a3011be0d267a090c8bfa01a4ebe093bc203a1c4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 17 Jan 2021 17:21:51 +0000 Subject: [PATCH 0664/1006] Look for libevent2 differently from libevent for platforms with both. --- alerts.c | 1 - client.c | 1 - compat.h | 13 ++++++++ configure.ac | 81 +++++++++++++++++++---------------------------- control.c | 1 - osdep-cygwin.c | 1 - osdep-darwin.c | 1 - osdep-dragonfly.c | 1 - osdep-freebsd.c | 1 - osdep-haiku.c | 1 - osdep-hpux.c | 2 -- osdep-linux.c | 1 - osdep-netbsd.c | 1 - osdep-openbsd.c | 3 +- osdep-sunos.c | 1 - osdep-unknown.c | 2 -- proc.c | 1 - screen-write.c | 1 - server-client.c | 1 - server.c | 1 - tmux.c | 1 - tmux.h | 1 - 22 files changed, 48 insertions(+), 70 deletions(-) diff --git a/alerts.c b/alerts.c index 0f2eb179..4cc5c3eb 100644 --- a/alerts.c +++ b/alerts.c @@ -18,7 +18,6 @@ #include -#include #include #include "tmux.h" diff --git a/client.c b/client.c index 757e4aa8..8b17aa22 100644 --- a/client.c +++ b/client.c @@ -24,7 +24,6 @@ #include #include -#include #include #include #include diff --git a/compat.h b/compat.h index 6e26a02c..828c956d 100644 --- a/compat.h +++ b/compat.h @@ -27,6 +27,19 @@ #include #include +#ifdef HAVE_EVENT2_EVENT_H +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + #ifdef HAVE_MALLOC_TRIM #include #endif diff --git a/configure.ac b/configure.ac index 8dd00d79..80e8be9e 100644 --- a/configure.ac +++ b/configure.ac @@ -182,88 +182,72 @@ AC_SEARCH_LIBS(clock_gettime, rt) # implementations. AC_LIBOBJ(getopt) -# Look for libevent. +# Look for libevent. Try libevent_core or libevent with pkg-config first then +# look for the library. PKG_CHECK_MODULES( LIBEVENT, - libevent, + [libevent_core >= 2 libevent >= 2], [ - AM_CFLAGS="$LIBEVENT_CFLAGS $AM_CFLAGS" - CFLAGS="$AM_CFLAGS $SAVED_CFLAGS" + AM_CPPFLAGS="$LIBEVENT_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" LIBS="$LIBEVENT_LIBS $LIBS" found_libevent=yes ], + found_libevent=no +) +if test x$found_libevent = xno; then + AC_SEARCH_LIBS( + event_init, + [event_core event event-1.4], + found_libevent=yes, + found_libevent=no + ) +fi +AC_CHECK_HEADER( + event2/event.h, + AC_DEFINE(HAVE_EVENT2_EVENT_H), [ - AC_SEARCH_LIBS( - event_init, - [event event-1.4 event2], - found_libevent=yes, + AC_CHECK_HEADER( + event.h, + AC_DEFINE(HAVE_EVENT_H), found_libevent=no ) ] ) -AC_CHECK_HEADER( - event.h, - , - found_libevent=no -) if test "x$found_libevent" = xno; then AC_MSG_ERROR("libevent not found") fi -# Look for ncurses. +# Look for ncurses or curses. Try pkg-config first then directly for the +# library. PKG_CHECK_MODULES( - LIBTINFO, - tinfo, + LIBNCURSES, + [tinfo ncurses ncursesw], found_ncurses=yes, found_ncurses=no ) -if test "x$found_ncurses" = xno; then - PKG_CHECK_MODULES( - LIBNCURSES, - ncurses, - found_ncurses=yes, - found_ncurses=no - ) -fi -if test "x$found_ncurses" = xno; then - PKG_CHECK_MODULES( - LIBNCURSES, - ncursesw, - found_ncurses=yes, - found_ncurses=no - ) -fi if test "x$found_ncurses" = xyes; then - AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS" - CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CFLAGS" + AM_CPPFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $SAVED_CPPFLAGS" LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS" else - # pkg-config didn't work, try ncurses. - AC_CHECK_LIB( - tinfo, + AC_SEARCH_LIBS( + [tinfo ncurses ncursesw], setupterm, found_ncurses=yes, found_ncurses=no ) - if test "x$found_ncurses" = xno; then - AC_CHECK_LIB( - ncurses, - setupterm, - found_ncurses=yes, - found_ncurses=no - ) - fi if test "x$found_ncurses" = xyes; then AC_CHECK_HEADER( ncurses.h, LIBS="$LIBS -lncurses", - found_ncurses=no) + found_ncurses=no + ) fi fi if test "x$found_ncurses" = xyes; then AC_DEFINE(HAVE_NCURSES_H) else - # No ncurses, try curses. AC_CHECK_LIB( curses, setupterm, @@ -273,7 +257,8 @@ else AC_CHECK_HEADER( curses.h, , - found_curses=no) + found_curses=no + ) if test "x$found_curses" = xyes; then LIBS="$LIBS -lcurses" AC_DEFINE(HAVE_CURSES_H) diff --git a/control.c b/control.c index e86429cf..7a5b9eb2 100644 --- a/control.c +++ b/control.c @@ -19,7 +19,6 @@ #include -#include #include #include #include diff --git a/osdep-cygwin.c b/osdep-cygwin.c index 60630b33..4346373c 100644 --- a/osdep-cygwin.c +++ b/osdep-cygwin.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include diff --git a/osdep-darwin.c b/osdep-darwin.c index 6b2b1d72..85e5470b 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include diff --git a/osdep-dragonfly.c b/osdep-dragonfly.c index 879034e8..02a4d18e 100644 --- a/osdep-dragonfly.c +++ b/osdep-dragonfly.c @@ -23,7 +23,6 @@ #include #include -#include #include #include #include diff --git a/osdep-freebsd.c b/osdep-freebsd.c index a7d02930..0f347f9a 100644 --- a/osdep-freebsd.c +++ b/osdep-freebsd.c @@ -24,7 +24,6 @@ #include #include -#include #include #include #include diff --git a/osdep-haiku.c b/osdep-haiku.c index 298dc05e..7b1f800a 100644 --- a/osdep-haiku.c +++ b/osdep-haiku.c @@ -18,7 +18,6 @@ #include -#include #include #include diff --git a/osdep-hpux.c b/osdep-hpux.c index 16993b93..64e33784 100644 --- a/osdep-hpux.c +++ b/osdep-hpux.c @@ -18,8 +18,6 @@ #include -#include - #include "tmux.h" char * diff --git a/osdep-linux.c b/osdep-linux.c index 5f0d9352..7dbab1f0 100644 --- a/osdep-linux.c +++ b/osdep-linux.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include diff --git a/osdep-netbsd.c b/osdep-netbsd.c index 67894175..b473e017 100644 --- a/osdep-netbsd.c +++ b/osdep-netbsd.c @@ -22,7 +22,6 @@ #include #include -#include #include #include #include diff --git a/osdep-openbsd.c b/osdep-openbsd.c index b21a6628..f5c61372 100644 --- a/osdep-openbsd.c +++ b/osdep-openbsd.c @@ -23,11 +23,12 @@ #include #include -#include #include #include #include +#include "compat.h" + #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif diff --git a/osdep-sunos.c b/osdep-sunos.c index 07043a9b..138e6bad 100644 --- a/osdep-sunos.c +++ b/osdep-sunos.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include diff --git a/osdep-unknown.c b/osdep-unknown.c index bc59f569..440f619e 100644 --- a/osdep-unknown.c +++ b/osdep-unknown.c @@ -18,8 +18,6 @@ #include -#include - #include "tmux.h" char * diff --git a/proc.c b/proc.c index 592d8e05..6d64b0bb 100644 --- a/proc.c +++ b/proc.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/screen-write.c b/screen-write.c index 92f1aa39..f374630c 100644 --- a/screen-write.c +++ b/screen-write.c @@ -259,7 +259,6 @@ screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s, } } - /* Initialize writing. */ void screen_write_start(struct screen_write_ctx *ctx, struct screen *s) diff --git a/server-client.c b/server-client.c index 451fe2eb..75a5719c 100644 --- a/server-client.c +++ b/server-client.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/server.c b/server.c index 9a1875f4..6aee61fc 100644 --- a/server.c +++ b/server.c @@ -24,7 +24,6 @@ #include #include -#include #include #include #include diff --git a/tmux.c b/tmux.c index 066714df..96b94e01 100644 --- a/tmux.c +++ b/tmux.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/tmux.h b/tmux.h index 2fdf7eb6..0d7d8f48 100644 --- a/tmux.h +++ b/tmux.h @@ -22,7 +22,6 @@ #include #include -#include #include #include #include From 603280cb286350c628ac2dfc06d5b8de135aa2af Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 17 Jan 2021 17:52:10 +0000 Subject: [PATCH 0665/1006] +compat.h --- osdep-darwin.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osdep-darwin.c b/osdep-darwin.c index 85e5470b..a2b125ad 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -25,6 +25,8 @@ #include #include +#include "compat.h" + char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); From d4866d5fe6214064882244ddb32f05480e9d8d91 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 17 Jan 2021 17:55:14 +0000 Subject: [PATCH 0666/1006] Fix SEARCH_LIBS. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 80e8be9e..1bd91eb4 100644 --- a/configure.ac +++ b/configure.ac @@ -232,8 +232,8 @@ if test "x$found_ncurses" = xyes; then LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS" else AC_SEARCH_LIBS( - [tinfo ncurses ncursesw], setupterm, + [tinfo ncurses ncursesw], found_ncurses=yes, found_ncurses=no ) From c6bcf3dba52fe0f5e161a9a7cedaae27c7a30845 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 17 Jan 2021 18:19:50 +0000 Subject: [PATCH 0667/1006] Fix yes/no for b64_ntop check. --- configure.ac | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 1bd91eb4..e70e3147 100644 --- a/configure.ac +++ b/configure.ac @@ -324,6 +324,7 @@ AC_TRY_LINK( found_b64_ntop=yes, found_b64_ntop=no ) +AC_MSG_RESULT($found_b64_ntop) OLD_LIBS="$LIBS" if test "x$found_b64_ntop" = xno; then AC_MSG_RESULT(no) @@ -339,6 +340,7 @@ if test "x$found_b64_ntop" = xno; then found_b64_ntop=yes, found_b64_ntop=no ) + AC_MSG_RESULT($found_b64_ntop) fi if test "x$found_b64_ntop" = xno; then AC_MSG_CHECKING(for b64_ntop with -lnetwork) @@ -353,14 +355,13 @@ if test "x$found_b64_ntop" = xno; then found_b64_ntop=yes, found_b64_ntop=no ) + AC_MSG_RESULT($found_b64_ntop) fi if test "x$found_b64_ntop" = xyes; then AC_DEFINE(HAVE_B64_NTOP) - AC_MSG_RESULT(yes) else LIBS="$OLD_LIBS" AC_LIBOBJ(base64) - AC_MSG_RESULT(no) fi # Look for networking libraries. From 032723c8740710cd34bdf6e7a0124f8fb18f6d70 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 17 Jan 2021 18:21:54 +0000 Subject: [PATCH 0668/1006] Set CFLAGS also. --- configure.ac | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index e70e3147..d18a584d 100644 --- a/configure.ac +++ b/configure.ac @@ -190,6 +190,8 @@ PKG_CHECK_MODULES( [ AM_CPPFLAGS="$LIBEVENT_CFLAGS $AM_CPPFLAGS" CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" + AM_CFLAGS="$LIBEVENT_CFLAGS $AM_CFLAGS" + CFLAGS="$AM_CFLAGS $SAVED_CFLAGS" LIBS="$LIBEVENT_LIBS $LIBS" found_libevent=yes ], @@ -229,6 +231,8 @@ PKG_CHECK_MODULES( if test "x$found_ncurses" = xyes; then AM_CPPFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CPPFLAGS" CPPFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $SAVED_CPPFLAGS" + AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS" + CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $SAVED_CFLAGS" LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS" else AC_SEARCH_LIBS( From b18834be8aadbf133206e5db256d76acac398da7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 17 Jan 2021 18:24:52 +0000 Subject: [PATCH 0669/1006] Revert "Set CFLAGS also." This reverts commit 032723c8740710cd34bdf6e7a0124f8fb18f6d70. --- configure.ac | 4 ---- 1 file changed, 4 deletions(-) diff --git a/configure.ac b/configure.ac index d18a584d..e70e3147 100644 --- a/configure.ac +++ b/configure.ac @@ -190,8 +190,6 @@ PKG_CHECK_MODULES( [ AM_CPPFLAGS="$LIBEVENT_CFLAGS $AM_CPPFLAGS" CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" - AM_CFLAGS="$LIBEVENT_CFLAGS $AM_CFLAGS" - CFLAGS="$AM_CFLAGS $SAVED_CFLAGS" LIBS="$LIBEVENT_LIBS $LIBS" found_libevent=yes ], @@ -231,8 +229,6 @@ PKG_CHECK_MODULES( if test "x$found_ncurses" = xyes; then AM_CPPFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CPPFLAGS" CPPFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $SAVED_CPPFLAGS" - AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS" - CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $SAVED_CFLAGS" LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS" else AC_SEARCH_LIBS( From 607594f6e5836f2060aec690b49791ea1ef982d9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 17 Jan 2021 18:47:14 +0000 Subject: [PATCH 0670/1006] Show config.log on failure. --- .github/travis/build-all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/travis/build-all.sh b/.github/travis/build-all.sh index 00f8f522..883868e8 100644 --- a/.github/travis/build-all.sh +++ b/.github/travis/build-all.sh @@ -35,4 +35,4 @@ tar -zxf ncurses-*.tar.gz || exit 1 sh autogen.sh || exit 1 PKG_CONFIG_PATH=$BUILD/lib/pkgconfig ./configure --prefix=$BUILD "$@" -make && make install || exit 1 +make && make install || (cat config.log; exit 1) From 4148417a2a0fdf2cc839568185150bb4a9203d5d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 17 Jan 2021 19:03:18 +0000 Subject: [PATCH 0671/1006] PKG_CHECK_MODULES needs to be separate. --- configure.ac | 64 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index e70e3147..a2591857 100644 --- a/configure.ac +++ b/configure.ac @@ -185,16 +185,29 @@ AC_LIBOBJ(getopt) # Look for libevent. Try libevent_core or libevent with pkg-config first then # look for the library. PKG_CHECK_MODULES( - LIBEVENT, - [libevent_core >= 2 libevent >= 2], + LIBEVENT_CORE, + [libevent_core >= 2], [ - AM_CPPFLAGS="$LIBEVENT_CFLAGS $AM_CPPFLAGS" + AM_CPPFLAGS="$LIBEVENT_CORE_CFLAGS $AM_CPPFLAGS" CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" - LIBS="$LIBEVENT_LIBS $LIBS" + LIBS="$LIBEVENT_CORE_LIBS $LIBS" found_libevent=yes ], found_libevent=no ) +if test x$found_libevent = xno; then + PKG_CHECK_MODULES( + LIBEVENT, + [libevent >= 2], + [ + AM_CPPFLAGS="$LIBEVENT_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" + LIBS="$LIBEVENT_LIBS $LIBS" + found_libevent=yes + ], + found_libevent=no + ) +fi if test x$found_libevent = xno; then AC_SEARCH_LIBS( event_init, @@ -221,16 +234,43 @@ fi # Look for ncurses or curses. Try pkg-config first then directly for the # library. PKG_CHECK_MODULES( - LIBNCURSES, - [tinfo ncurses ncursesw], - found_ncurses=yes, + LIBTINFO, + tinfo, + [ + AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS" + LIBS="$LIBTINFO_LIBS $LIBS" + found_ncurses=yes + ], found_ncurses=no ) -if test "x$found_ncurses" = xyes; then - AM_CPPFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CPPFLAGS" - CPPFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $SAVED_CPPFLAGS" - LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS" -else +if test "x$found_ncurses" = xno; then + PKG_CHECK_MODULES( + LIBNCURSES, + ncurses, + [ + AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS" + LIBS="$LIBNCURSES_LIBS $LIBS" + found_ncurses=yes + ], + found_ncurses=no + ) +fi +if test "x$found_ncurses" = xno; then + PKG_CHECK_MODULES( + LIBNCURSESW, + ncursesw, + [ + AM_CPPFLAGS="$LIBNCURSESW_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$LIBNCURSESW_CFLAGS $SAVED_CPPFLAGS" + LIBS="$LIBNCURSESW_LIBS $LIBS" + found_ncurses=yes + ], + found_ncurses=no + ) +fi +if test "x$found_ncurses" = xno; then AC_SEARCH_LIBS( setupterm, [tinfo ncurses ncursesw], From 91d112bf12789da07e25ed001f7961b1d6bd7a76 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 18 Jan 2021 10:27:54 +0000 Subject: [PATCH 0672/1006] There is no need to clear every line entirely before drawing to it, this means moving the cursor and messes up wrapping. Better to just clear the sections that aren't written over. GitHub issue 2537. --- grid.c | 1 - screen-write.c | 32 +++++++++++++++++++++++++------- tmux.h | 1 - tty.c | 13 +------------ 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/grid.c b/grid.c index 96302fc3..cce304c9 100644 --- a/grid.c +++ b/grid.c @@ -698,7 +698,6 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg) gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; } - /* Move a group of cells. */ void grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx, diff --git a/screen-write.c b/screen-write.c index 92f1aa39..7df5cd92 100644 --- a/screen-write.c +++ b/screen-write.c @@ -259,7 +259,6 @@ screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s, } } - /* Initialize writing. */ void screen_write_start(struct screen_write_ctx *ctx, struct screen *s) @@ -1517,6 +1516,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, struct screen_write_collect_item *ci, *tmp; struct screen_write_collect_line *cl; u_int y, cx, cy, items = 0; + int clear = 0; struct tty_ctx ttyctx; size_t written = 0; @@ -1540,22 +1540,29 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, cx = s->cx; cy = s->cy; for (y = 0; y < screen_size_y(s); y++) { cl = &ctx->s->write_list[y]; - if (cl->bg != 0) { - screen_write_set_cursor(ctx, 0, y); - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = cl->bg - 1; - tty_write(tty_cmd_clearline, &ttyctx); - } TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { + if (clear != -1 && + (u_int)clear != ci->x && + cl->bg != 0) { + screen_write_set_cursor(ctx, clear, y); + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.bg = cl->bg - 1; + ttyctx.num = ci->x - clear; + log_debug("clear %u at %u", ttyctx.num, clear); + tty_write(tty_cmd_clearcharacter, &ttyctx); + } + screen_write_set_cursor(ctx, ci->x, y); if (ci->type == CLEAR_END) { screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; tty_write(tty_cmd_clearendofline, &ttyctx); + clear = -1; } else if (ci->type == CLEAR_START) { screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; tty_write(tty_cmd_clearstartofline, &ttyctx); + clear = ci->x + 1; } else { screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = &ci->gc; @@ -1563,6 +1570,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, ttyctx.ptr = cl->data + ci->x; ttyctx.num = ci->used; tty_write(tty_cmd_cells, &ttyctx); + clear = ci->x + ci->used; } items++; @@ -1571,6 +1579,16 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, TAILQ_REMOVE(&cl->items, ci, entry); free(ci); } + if (clear != -1 && + (u_int)clear != screen_size_x(s) - 1 && + cl->bg != 0) { + screen_write_set_cursor(ctx, clear, y); + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.bg = cl->bg - 1; + log_debug("clear to end at %u", clear); + tty_write(tty_cmd_clearendofline, &ttyctx); + } + clear = 0; cl->bg = 0; } s->cx = cx; s->cy = cy; diff --git a/tmux.h b/tmux.h index 832918e2..32f7fec7 100644 --- a/tmux.h +++ b/tmux.h @@ -1890,7 +1890,6 @@ const char *find_home(void); const char *getversion(void); void expand_paths(const char *, char ***, u_int *); - /* proc.c */ struct imsg; int proc_send(struct tmuxpeer *, enum msgtype, int, const void *, size_t); diff --git a/tty.c b/tty.c index fac7a99e..279bafaf 100644 --- a/tty.c +++ b/tty.c @@ -1531,20 +1531,9 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) { - if (ctx->bigger) { - tty_draw_pane(tty, ctx, ctx->ocy); - return; - } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); - tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - - if (tty_term_has(tty->term, TTYC_ECH) && - !tty_fake_bce(tty, &ctx->defaults, 8)) - tty_putcode1(tty, TTYC_ECH, ctx->num); - else - tty_repeat_space(tty, ctx->num); + tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg); } void From 63f4a3c4e53b7d6aeb09917c0971af2df6549f31 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 18 Jan 2021 10:48:49 +0000 Subject: [PATCH 0673/1006] Extra result message. --- configure.ac | 1 - 1 file changed, 1 deletion(-) diff --git a/configure.ac b/configure.ac index a2591857..269dd5aa 100644 --- a/configure.ac +++ b/configure.ac @@ -367,7 +367,6 @@ AC_TRY_LINK( AC_MSG_RESULT($found_b64_ntop) OLD_LIBS="$LIBS" if test "x$found_b64_ntop" = xno; then - AC_MSG_RESULT(no) AC_MSG_CHECKING(for b64_ntop with -lresolv) LIBS="$OLD_LIBS -lresolv" AC_TRY_LINK( From 0730dce5abf5e43f8e3820a1d4e8754e61874a3d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 18 Jan 2021 11:14:23 +0000 Subject: [PATCH 0674/1006] Hide some warnings on newer GCC versions, GitHUb issue 2525. --- options.c | 7 ++++--- server-client.c | 8 ++++---- window-copy.c | 37 +++++++++++++++++-------------------- window-customize.c | 10 +++------- 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/options.c b/options.c index 09850f7e..9bc89db3 100644 --- a/options.c +++ b/options.c @@ -157,8 +157,7 @@ options_value_to_string(struct options_entry *o, union options_value *ov, case OPTIONS_TABLE_CHOICE: s = xstrdup(o->tableentry->choices[ov->number]); break; - case OPTIONS_TABLE_STRING: - case OPTIONS_TABLE_COMMAND: + default: fatalx("not a number option type"); } return (s); @@ -311,6 +310,8 @@ options_default_to_string(const struct options_table_entry *oe) case OPTIONS_TABLE_CHOICE: s = xstrdup(oe->choices[oe->default_num]); break; + default: + fatalx("unknown option type"); } return (s); } @@ -703,7 +704,7 @@ options_get_number(struct options *oo, const char *name) if (o == NULL) fatalx("missing option %s", name); if (!OPTIONS_IS_NUMBER(o)) - fatalx("option %s is not a number", name); + fatalx("option %s is not a number", name); return (o->value.number); } diff --git a/server-client.c b/server-client.c index f85304a6..a55188d1 100644 --- a/server-client.c +++ b/server-client.c @@ -1781,11 +1781,11 @@ server_client_check_exit(struct client *c) switch (c->exit_type) { case CLIENT_EXIT_RETURN: - if (c->exit_message != NULL) { + if (c->exit_message != NULL) msize = strlen(c->exit_message) + 1; - size = (sizeof c->retval) + msize; - } else - size = (sizeof c->retval); + else + msize = 0; + size = (sizeof c->retval) + msize; data = xmalloc(size); memcpy(data, &c->retval, sizeof c->retval); if (c->exit_message != NULL) diff --git a/window-copy.c b/window-copy.c index bfa94aed..889a536d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3453,10 +3453,10 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, struct window_copy_mode_data *data = wme->data; u_int xx, yy; + xx = data->cx; yy = screen_hsize(data->backing) + data->cy - data->oy; switch (data->selflag) { case SEL_WORD: - xx = data->cx; if (no_reset) break; begin = 0; @@ -3482,10 +3482,8 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, } break; case SEL_LINE: - if (no_reset) { - xx = data->cx; + if (no_reset) break; - } begin = 0; if (data->dy > yy) { /* Right to left selection. */ @@ -3505,7 +3503,6 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, } break; case SEL_CHAR: - xx = data->cx; break; } if (begin) { @@ -4784,22 +4781,22 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) if (x < data->selrx || x > data->endselrx || yg != data->selry) data->selflag = SEL_CHAR; switch (data->selflag) { - case SEL_WORD: - if (data->ws != NULL) { - window_copy_update_cursor(wme, x, y); - window_copy_cursor_previous_word_pos(wme, - data->ws, 0, &x, &y); - y -= screen_hsize(data->backing) - data->oy; - } + case SEL_WORD: + if (data->ws != NULL) { window_copy_update_cursor(wme, x, y); - break; - case SEL_LINE: - window_copy_update_cursor(wme, 0, y); - break; - case SEL_CHAR: - window_copy_update_cursor(wme, x, y); - window_copy_start_selection(wme); - break; + window_copy_cursor_previous_word_pos(wme, data->ws, 0, + &x, &y); + y -= screen_hsize(data->backing) - data->oy; + } + window_copy_update_cursor(wme, x, y); + break; + case SEL_LINE: + window_copy_update_cursor(wme, 0, y); + break; + case SEL_CHAR: + window_copy_update_cursor(wme, x, y); + window_copy_start_selection(wme); + break; } window_copy_redraw_screen(wme); diff --git a/window-customize.c b/window-customize.c index 1dad07cd..a1f191b5 100644 --- a/window-customize.c +++ b/window-customize.c @@ -190,13 +190,6 @@ window_customize_scope_text(enum window_customize_scope scope, u_int idx; switch (scope) { - case WINDOW_CUSTOMIZE_NONE: - case WINDOW_CUSTOMIZE_KEY: - case WINDOW_CUSTOMIZE_SERVER: - case WINDOW_CUSTOMIZE_GLOBAL_SESSION: - case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: - s = xstrdup(""); - break; case WINDOW_CUSTOMIZE_PANE: window_pane_index(fs->wp, &idx); xasprintf(&s, "pane %u", idx); @@ -207,6 +200,9 @@ window_customize_scope_text(enum window_customize_scope scope, case WINDOW_CUSTOMIZE_WINDOW: xasprintf(&s, "window %u", fs->wl->idx); break; + default: + s = xstrdup(""); + break; } return (s); } From 3c86fa2ad0b7815c8f26618aefee24cb6d17cddb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 18 Jan 2021 11:14:37 +0000 Subject: [PATCH 0675/1006] Add -Wno-format-y2k. --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index e9b1cb15..3e15204f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,7 +28,7 @@ AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes -AM_CFLAGS += -Wno-unused-result +AM_CFLAGS += -Wno-unused-result -Wno-format-y2k AM_CPPFLAGS += -DDEBUG endif AM_CPPFLAGS += -iquote. From fb774b77d0f5ccb988b508b8a794633d4c9a5962 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 20 Jan 2021 07:16:54 +0000 Subject: [PATCH 0676/1006] Change so that window_flags escapes # automatically which means configs will not have to change. A new format window_raw_flags contains the old unescaped version. --- cmd-list-windows.c | 4 ++-- control-notify.c | 2 +- format.c | 30 ++++++++++++++++-------------- options-table.c | 4 ++-- tmux.1 | 7 ++++--- tmux.h | 2 +- window.c | 7 +++++-- 7 files changed, 31 insertions(+), 25 deletions(-) diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 9c33c2d0..d6cc0b7a 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -28,14 +28,14 @@ */ #define LIST_WINDOWS_TEMPLATE \ - "#{window_index}: #{window_name}#{window_flags} " \ + "#{window_index}: #{window_name}#{window_raw_flags} " \ "(#{window_panes} panes) " \ "[#{window_width}x#{window_height}] " \ "[layout #{window_layout}] #{window_id}" \ "#{?window_active, (active),}"; #define LIST_WINDOWS_WITH_SESSION_TEMPLATE \ "#{session_name}:" \ - "#{window_index}: #{window_name}#{window_flags} " \ + "#{window_index}: #{window_name}#{window_raw_flags} " \ "(#{window_panes} panes) " \ "[#{window_width}x#{window_height}] " diff --git a/control-notify.c b/control-notify.c index a1735d57..cc706ac2 100644 --- a/control-notify.c +++ b/control-notify.c @@ -49,7 +49,7 @@ control_notify_window_layout_changed(struct window *w) char *cp; template = "%layout-change #{window_id} #{window_layout} " - "#{window_visible_layout} #{window_flags}"; + "#{window_visible_layout} #{window_raw_flags}"; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) diff --git a/format.c b/format.c index 2d7402af..df1898a6 100644 --- a/format.c +++ b/format.c @@ -89,7 +89,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_TIMESTRING 0x1 #define FORMAT_BASENAME 0x2 #define FORMAT_DIRNAME 0x4 -#define FORMAT_QUOTE 0x8 +#define FORMAT_QUOTE_SHELL 0x8 #define FORMAT_LITERAL 0x10 #define FORMAT_EXPAND 0x20 #define FORMAT_EXPANDTIME 0x40 @@ -99,7 +99,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_PRETTY 0x400 #define FORMAT_LENGTH 0x800 #define FORMAT_WIDTH 0x1000 -#define FORMAT_ESCAPE 0x2000 +#define FORMAT_QUOTE_STYLE 0x2000 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 @@ -1378,9 +1378,9 @@ format_add_cb(struct format_tree *ft, const char *key, format_cb cb) fe->value = NULL; } -/* Quote special characters in string. */ +/* Quote shell special characters in string. */ static char * -format_quote(const char *s) +format_quote_shell(const char *s) { const char *cp; char *out, *at; @@ -1395,9 +1395,9 @@ format_quote(const char *s) return (out); } -/* Escape #s in string. */ +/* Quote #s in string. */ static char * -format_escape(const char *s) +format_quote_style(const char *s) { const char *cp; char *out, *at; @@ -1552,14 +1552,14 @@ found: found = xstrdup(dirname(saved)); free(saved); } - if (modifiers & FORMAT_QUOTE) { + if (modifiers & FORMAT_QUOTE_SHELL) { saved = found; - found = xstrdup(format_quote(saved)); + found = xstrdup(format_quote_shell(saved)); free(saved); } - if (modifiers & FORMAT_ESCAPE) { + if (modifiers & FORMAT_QUOTE_STYLE) { saved = found; - found = xstrdup(format_escape(saved)); + found = xstrdup(format_quote_style(saved)); free(saved); } return (found); @@ -2240,9 +2240,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, break; case 'q': if (fm->argc < 1) - modifiers |= FORMAT_QUOTE; - else if (strchr(fm->argv[0], 'e') != NULL) - modifiers |= FORMAT_ESCAPE; + modifiers |= FORMAT_QUOTE_SHELL; + else if (strchr(fm->argv[0], 'e') != NULL || + strchr(fm->argv[0], 'h') != NULL) + modifiers |= FORMAT_QUOTE_STYLE; break; case 'E': modifiers |= FORMAT_EXPAND; @@ -2980,7 +2981,8 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl) format_add(ft, "window_index", "%d", wl->idx); format_add_cb(ft, "window_stack_index", format_cb_window_stack_index); - format_add(ft, "window_flags", "%s", window_printable_flags(wl)); + format_add(ft, "window_flags", "%s", window_printable_flags(wl, 1)); + format_add(ft, "window_raw_flags", "%s", window_printable_flags(wl, 0)); format_add(ft, "window_active", "%d", wl == s->curw); format_add_cb(ft, "window_active_sessions", format_cb_window_active_sessions); diff --git a/options-table.c b/options-table.c index a8276c86..74a59134 100644 --- a/options-table.c +++ b/options-table.c @@ -1018,7 +1018,7 @@ const struct options_table_entry options_table[] = { { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "#I:#W#{?window_flags,#{q/e:window_flags}, }", + .default_str = "#I:#W#{?window_flags,#{window_flags}, }", .text = "Format of the current window in the status line." }, @@ -1034,7 +1034,7 @@ const struct options_table_entry options_table[] = { { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .default_str = "#I:#W#{?window_flags,#{q/e:window_flags}, }", + .default_str = "#I:#W#{?window_flags,#{window_flags}, }", .text = "Format of windows in the status line, except the current " "window." }, diff --git a/tmux.1 b/tmux.1 index 93842482..8c812f38 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4651,8 +4651,8 @@ of the variable respectively. .Ql q:\& will escape .Xr sh 1 -special characters or with an -.Ql e +special characters or with a +.Ql h suffix, escape hash characters (so .Ql # becomes @@ -4882,7 +4882,8 @@ The following variables are available, where appropriate: .It Li "window_cell_height" Ta "" Ta "Height of each cell in pixels" .It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels" .It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" -.It Li "window_flags" Ta "#F" Ta "Window flags" +.It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##" +.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped" .It Li "window_format" Ta "" Ta "1 if format is for a window" .It Li "window_height" Ta "" Ta "Height of window" .It Li "window_id" Ta "" Ta "Unique window ID" diff --git a/tmux.h b/tmux.h index 32f7fec7..c593a175 100644 --- a/tmux.h +++ b/tmux.h @@ -2761,7 +2761,7 @@ int window_pane_key(struct window_pane *, struct client *, int window_pane_visible(struct window_pane *); u_int window_pane_search(struct window_pane *, const char *, int, int); -const char *window_printable_flags(struct winlink *); +const char *window_printable_flags(struct winlink *, int); struct window_pane *window_pane_find_up(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *); struct window_pane *window_pane_find_left(struct window_pane *); diff --git a/window.c b/window.c index 22986a04..6bfdb1cd 100644 --- a/window.c +++ b/window.c @@ -803,15 +803,18 @@ window_destroy_panes(struct window *w) } const char * -window_printable_flags(struct winlink *wl) +window_printable_flags(struct winlink *wl, int escape) { struct session *s = wl->session; static char flags[32]; int pos; pos = 0; - if (wl->flags & WINLINK_ACTIVITY) + if (wl->flags & WINLINK_ACTIVITY) { flags[pos++] = '#'; + if (escape) + flags[pos++] = '#'; + } if (wl->flags & WINLINK_BELL) flags[pos++] = '!'; if (wl->flags & WINLINK_SILENCE) From 8d185395e479d8e7792a56bc61415bef640cf4b7 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 22 Jan 2021 10:21:24 +0000 Subject: [PATCH 0677/1006] Fix some cursor movement commands, from Anindya Mukherjee. --- window-copy.c | 98 +++++++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 42 deletions(-) diff --git a/window-copy.c b/window-copy.c index 889a536d..a2f4b2e5 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3981,11 +3981,12 @@ window_copy_cursor_start_of_line(struct window_mode_entry *wme) struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, yy, ny, hsize; + u_int px, py, cy, oldy, yy, ny, nd, hsize; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; + oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); grid_reader_cursor_start_of_line(&gr, 1); @@ -3996,9 +3997,11 @@ window_copy_cursor_start_of_line(struct window_mode_entry *wme) if (py < yy) { ny = yy - py; cy = 0; + nd = 1; } else { ny = 0; cy = py - yy; + nd = oldy - cy + 1; } while (ny > 0) { window_copy_cursor_up(wme, 1); @@ -4006,7 +4009,7 @@ window_copy_cursor_start_of_line(struct window_mode_entry *wme) } window_copy_update_cursor(wme, px, cy); if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); + window_copy_redraw_lines(wme, data->cy, nd); } static void @@ -4038,11 +4041,12 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme) struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, yy, ny, hsize; + u_int px, py, cy, oldy, yy, ny, nd, hsize; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; + oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); if (data->screen.sel != NULL && data->rectflag) @@ -4054,10 +4058,14 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme) /* Scroll down if we went off the visible screen. */ cy = py - hsize + data->oy; yy = screen_size_y(back_s) - 1; - if (cy > yy) + if (cy > yy) { ny = cy - yy; - else + oldy = yy; + nd = 1; + } else { ny = 0; + nd = cy - oldy + 1; + } while (ny > 0) { window_copy_cursor_down(wme, 1); ny--; @@ -4067,7 +4075,7 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme) else window_copy_update_cursor(wme, px, cy); if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); + window_copy_redraw_lines(wme, oldy, nd); } static void @@ -4433,41 +4441,39 @@ window_copy_cursor_next_word(struct window_mode_entry *wme, { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; - u_int px, py, xx, yy; - int expected = 0; + struct grid_reader gr; + u_int px, py, cy, oldy, yy, ny, nd, hsize; px = data->cx; - py = screen_hsize(back_s) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); - yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + oldy = data->cy; - /* - * First skip past any nonword characters and then any word characters. - * - * expected is initially set to 0 for the former and then 1 for the - * latter. - */ - do { - while (px > xx || - window_copy_in_set(wme, px, py, separators) == expected) { - /* Move down if we're past the end of the line. */ - if (px > xx) { - if (py == yy) - return; - window_copy_cursor_down(wme, 0); - px = 0; + grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_next_word(&gr, separators); + grid_reader_get_cursor(&gr, &px, &py); - py = screen_hsize(back_s) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); - } else - px++; - } - expected = !expected; - } while (expected == 1); - - window_copy_update_cursor(wme, px, data->cy); + /* Scroll down if we went off the visible screen. */ + cy = py - hsize + data->oy; + yy = screen_size_y(back_s) - 1; + if (cy > yy) { + ny = cy - yy; + oldy = yy; + nd = 1; + } else { + ny = 0; + nd = cy - oldy + 1; + } + while (ny > 0) { + window_copy_cursor_down(wme, 1); + ny--; + } + if (cy > yy) + window_copy_update_cursor(wme, px, yy); + else + window_copy_update_cursor(wme, px, cy); if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); + window_copy_redraw_lines(wme, oldy, nd); } static void @@ -4528,12 +4534,13 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, struct options *oo = wp->window->options; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, yy, ny, hsize; + u_int px, py, cy, oldy, yy, ny, nd, hsize; int keys; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; + oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); keys = options_get_number(oo, "mode-keys"); @@ -4547,10 +4554,14 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, /* Scroll down if we went off the visible screen. */ cy = py - hsize + data->oy; yy = screen_size_y(back_s) - 1; - if (cy > yy) + if (cy > yy) { ny = cy - yy; - else + oldy = yy; + nd = 1; + } else { ny = 0; + nd = cy - oldy + 1; + } while (ny > 0) { window_copy_cursor_down(wme, 1); ny--; @@ -4560,7 +4571,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, else window_copy_update_cursor(wme, px, cy); if (window_copy_update_selection(wme, 1, no_reset)) - window_copy_redraw_lines(wme, data->cy, 1); + window_copy_redraw_lines(wme, oldy, nd); } /* Compute the previous place where a word begins. */ @@ -4617,11 +4628,12 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, yy, ny, hsize; + u_int px, py, cy, oldy, yy, ny, nd, hsize; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; + oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); grid_reader_cursor_previous_word(&gr, separators, already); @@ -4632,9 +4644,11 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, if (py < yy) { ny = yy - py; cy = 0; + nd = 1; } else { ny = 0; cy = py - yy; + nd = oldy - cy + 1; } while (ny > 0) { window_copy_cursor_up(wme, 1); @@ -4642,7 +4656,7 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, } window_copy_update_cursor(wme, px, cy); if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); + window_copy_redraw_lines(wme, data->cy, nd); } static void From bba71f696f49cdd3f70eaea12fd3a34c407a5aa3 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 22 Jan 2021 10:24:52 +0000 Subject: [PATCH 0678/1006] Add rectangle-on and rectangle-off copy mode commands, GitHub isse 2546 from author at will dot party. --- tmux.1 | 2 ++ window-copy.c | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/tmux.1 b/tmux.1 index 8c812f38..160e6a92 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1657,6 +1657,8 @@ The following commands are supported in copy mode: .It Li "previous-paragraph" Ta "{" Ta "M-{" .It Li "previous-space" Ta "B" Ta "" .It Li "previous-word" Ta "b" Ta "M-b" +.It Li "rectangle-on" Ta "" Ta "" +.It Li "rectangle-off" Ta "" Ta "" .It Li "rectangle-toggle" Ta "v" Ta "R" .It Li "refresh-from-pane" Ta "r" Ta "r" .It Li "scroll-down" Ta "C-e" Ta "C-Down" diff --git a/window-copy.c b/window-copy.c index a2f4b2e5..89b2a9af 100644 --- a/window-copy.c +++ b/window-copy.c @@ -128,7 +128,7 @@ static void window_copy_cursor_previous_word(struct window_mode_entry *, const char *, int); static void window_copy_scroll_up(struct window_mode_entry *, u_int); static void window_copy_scroll_down(struct window_mode_entry *, u_int); -static void window_copy_rectangle_toggle(struct window_mode_entry *); +static void window_copy_rectangle_set(struct window_mode_entry *, int); static void window_copy_move_mouse(struct mouse_event *); static void window_copy_drag_update(struct client *, struct mouse_event *); static void window_copy_drag_release(struct client *, struct mouse_event *); @@ -1625,6 +1625,30 @@ window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_rectangle_on(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + + data->lineflag = LINE_SEL_NONE; + window_copy_rectangle_set(wme, 1); + + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_rectangle_off(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + + data->lineflag = LINE_SEL_NONE; + window_copy_rectangle_set(wme, 0); + + return (WINDOW_COPY_CMD_NOTHING); +} + static enum window_copy_cmd_action window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs) { @@ -1632,7 +1656,7 @@ window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs) struct window_copy_mode_data *data = wme->data; data->lineflag = LINE_SEL_NONE; - window_copy_rectangle_toggle(wme); + window_copy_rectangle_set(wme, !data->rectflag); return (WINDOW_COPY_CMD_NOTHING); } @@ -2251,6 +2275,10 @@ static const struct { window_copy_cmd_previous_space }, { "previous-word", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_previous_word }, + { "rectangle-on", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, + window_copy_cmd_rectangle_on }, + { "rectangle-off", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, + window_copy_cmd_rectangle_off }, { "rectangle-toggle", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_rectangle_toggle }, { "refresh-from-pane", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, @@ -4726,12 +4754,12 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) } static void -window_copy_rectangle_toggle(struct window_mode_entry *wme) +window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag) { struct window_copy_mode_data *data = wme->data; u_int px, py; - data->rectflag = !data->rectflag; + data->rectflag = rectflag; py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); From 9fcf413d877731bdd8ac75fe756a5f80f97aa52e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 22 Jan 2021 11:28:33 +0000 Subject: [PATCH 0679/1006] Revert clear changes to writing as they don't work properly, better change to come. --- screen-write.c | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/screen-write.c b/screen-write.c index 7df5cd92..f374630c 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1516,7 +1516,6 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, struct screen_write_collect_item *ci, *tmp; struct screen_write_collect_line *cl; u_int y, cx, cy, items = 0; - int clear = 0; struct tty_ctx ttyctx; size_t written = 0; @@ -1540,29 +1539,22 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, cx = s->cx; cy = s->cy; for (y = 0; y < screen_size_y(s); y++) { cl = &ctx->s->write_list[y]; + if (cl->bg != 0) { + screen_write_set_cursor(ctx, 0, y); + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.bg = cl->bg - 1; + tty_write(tty_cmd_clearline, &ttyctx); + } TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { - if (clear != -1 && - (u_int)clear != ci->x && - cl->bg != 0) { - screen_write_set_cursor(ctx, clear, y); - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = cl->bg - 1; - ttyctx.num = ci->x - clear; - log_debug("clear %u at %u", ttyctx.num, clear); - tty_write(tty_cmd_clearcharacter, &ttyctx); - } - screen_write_set_cursor(ctx, ci->x, y); if (ci->type == CLEAR_END) { screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; tty_write(tty_cmd_clearendofline, &ttyctx); - clear = -1; } else if (ci->type == CLEAR_START) { screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; tty_write(tty_cmd_clearstartofline, &ttyctx); - clear = ci->x + 1; } else { screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = &ci->gc; @@ -1570,7 +1562,6 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, ttyctx.ptr = cl->data + ci->x; ttyctx.num = ci->used; tty_write(tty_cmd_cells, &ttyctx); - clear = ci->x + ci->used; } items++; @@ -1579,16 +1570,6 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, TAILQ_REMOVE(&cl->items, ci, entry); free(ci); } - if (clear != -1 && - (u_int)clear != screen_size_x(s) - 1 && - cl->bg != 0) { - screen_write_set_cursor(ctx, clear, y); - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = cl->bg - 1; - log_debug("clear to end at %u", clear); - tty_write(tty_cmd_clearendofline, &ttyctx); - } - clear = 0; cl->bg = 0; } s->cx = cx; s->cy = cy; From d6542c333d92aeee74c0a70939e5976de015bb0c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 26 Jan 2021 09:32:52 +0000 Subject: [PATCH 0680/1006] Always resize the original screen before copying when exiting the alternate screen, GitHub issue 2536. --- screen.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/screen.c b/screen.c index d39447de..2cdbec62 100644 --- a/screen.c +++ b/screen.c @@ -574,7 +574,14 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) void screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) { - u_int sx, sy; + u_int sx = screen_size_x(s), sy = screen_size_y(s); + + /* + * If the current size is different, temporarily resize to the old size + * before copying back. + */ + if (s->saved_grid != NULL) + screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 1); /* * Restore the cursor position and cell. This happens even if not @@ -582,29 +589,23 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) */ if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) { s->cx = s->saved_cx; - if (s->cx > screen_size_x(s) - 1) - s->cx = screen_size_x(s) - 1; s->cy = s->saved_cy; - if (s->cy > screen_size_y(s) - 1) - s->cy = screen_size_y(s) - 1; if (gc != NULL) memcpy(gc, &s->saved_cell, sizeof *gc); } - if (s->saved_grid == NULL) + /* If not in the alternate screen, do nothing more. */ + if (s->saved_grid == NULL) { + if (s->cx > screen_size_x(s) - 1) + s->cx = screen_size_x(s) - 1; + if (s->cy > screen_size_y(s) - 1) + s->cy = screen_size_y(s) - 1; return; - sx = screen_size_x(s); - sy = screen_size_y(s); - - /* - * If the current size is bigger, temporarily resize to the old size - * before copying back. - */ - if (sy > s->saved_grid->sy) - screen_resize(s, sx, s->saved_grid->sy, 1); + } /* Restore the saved grid. */ - grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, sy); + grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, + s->saved_grid->sy); /* * Turn history back on (so resize can use it) and then resize back to @@ -612,9 +613,13 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) */ if (s->saved_flags & GRID_HISTORY) s->grid->flags |= GRID_HISTORY; - if (sy > s->saved_grid->sy || sx != s->saved_grid->sx) - screen_resize(s, sx, sy, 1); + screen_resize(s, sx, sy, 1); grid_destroy(s->saved_grid); s->saved_grid = NULL; + + if (s->cx > screen_size_x(s) - 1) + s->cx = screen_size_x(s) - 1; + if (s->cy > screen_size_y(s) - 1) + s->cy = screen_size_y(s) - 1; } From 8156d9ba416c28d76b445e5c74537c254a3bc110 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 27 Jan 2021 10:42:52 +0000 Subject: [PATCH 0681/1006] Flush pending output before entering or exiting alternate screen rather than leaking it, oss-fuzz issue 29959. --- screen-write.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/screen-write.c b/screen-write.c index f374630c..4811b0a1 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1970,6 +1970,8 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) return; + + screen_write_collect_flush(ctx, 0, __func__); screen_alternate_on(ctx->s, gc, cursor); screen_write_initctx(ctx, &ttyctx, 1); @@ -1986,6 +1988,8 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc, if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) return; + + screen_write_collect_flush(ctx, 0, __func__); screen_alternate_off(ctx->s, gc, cursor); screen_write_initctx(ctx, &ttyctx, 1); From 255802d8d7357bf985bf2e4221eac8ab64b348ea Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 Jan 2021 09:48:43 +0000 Subject: [PATCH 0682/1006] Trim output overwritten by later text or clears completely rather than only in a few cases. This means we can better track when a line should wrap. GitHub issue 2537. --- format.c | 3 - screen-write.c | 369 +++++++++++++++++++++++++++---------------------- tmux.1 | 2 - tmux.h | 65 ++++----- 4 files changed, 230 insertions(+), 209 deletions(-) diff --git a/format.c b/format.c index df1898a6..25b80249 100644 --- a/format.c +++ b/format.c @@ -3038,9 +3038,6 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add_cb(ft, "history_bytes", format_cb_history_bytes); format_add_cb(ft, "history_all_bytes", format_cb_history_all_bytes); - format_add(ft, "pane_written", "%zu", wp->written); - format_add(ft, "pane_skipped", "%zu", wp->skipped); - if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); format_add(ft, "pane_index", "%u", idx); diff --git a/screen-write.c b/screen-write.c index 4811b0a1..8e9ec582 100644 --- a/screen-write.c +++ b/screen-write.c @@ -23,13 +23,11 @@ #include "tmux.h" +static struct screen_write_citem *screen_write_collect_trim( + struct screen_write_ctx *, u_int, u_int, u_int, int *); static void screen_write_collect_clear(struct screen_write_ctx *, u_int, u_int); -static void screen_write_collect_clear_end(struct screen_write_ctx *, u_int, - u_int); -static void screen_write_collect_clear_start(struct screen_write_ctx *, - u_int, u_int); -static void screen_write_collect_scroll(struct screen_write_ctx *); +static void screen_write_collect_scroll(struct screen_write_ctx *, u_int); static void screen_write_collect_flush(struct screen_write_ctx *, int, const char *); @@ -38,23 +36,44 @@ static int screen_write_overwrite(struct screen_write_ctx *, static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, const struct utf8_data *, u_int *); -struct screen_write_collect_item { - u_int x; - int wrapped; +struct screen_write_citem { + u_int x; + int wrapped; - enum { TEXT, CLEAR_END, CLEAR_START } type; - u_int used; - u_int bg; + enum { TEXT, CLEAR } type; + u_int used; + u_int bg; - struct grid_cell gc; + struct grid_cell gc; - TAILQ_ENTRY(screen_write_collect_item) entry; + TAILQ_ENTRY(screen_write_citem) entry; }; -struct screen_write_collect_line { - u_int bg; - char *data; - TAILQ_HEAD(, screen_write_collect_item) items; +struct screen_write_cline { + char *data; + TAILQ_HEAD(, screen_write_citem) items; }; +TAILQ_HEAD(, screen_write_citem) screen_write_citem_freelist = + TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist); + +static struct screen_write_citem * +screen_write_get_citem(void) +{ + struct screen_write_citem *ci; + + ci = TAILQ_FIRST(&screen_write_citem_freelist); + if (ci != NULL) { + TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry); + memset(ci, 0, sizeof *ci); + return (ci); + } + return (xcalloc(1, sizeof *ci)); +} + +static void +screen_write_free_citem(struct screen_write_citem *ci) +{ + TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry); +} static void screen_write_offset_timer(__unused int fd, __unused short events, void *data) @@ -125,7 +144,8 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c) * Redraw is already deferred to redraw another pane - redraw * this one also when that happens. */ - log_debug("adding %%%u to deferred redraw", wp->id); + log_debug("%s: adding %%%u to deferred redraw", __func__, + wp->id); wp->flags |= PANE_REDRAW; return (-1); } @@ -220,7 +240,7 @@ screen_write_init(struct screen_write_ctx *ctx, struct screen *s) if (ctx->s->write_list == NULL) screen_write_make_list(ctx->s); - ctx->item = xcalloc(1, sizeof *ctx->item); + ctx->item = screen_write_get_citem(); ctx->scrolled = 0; ctx->bg = 8; @@ -278,14 +298,7 @@ screen_write_stop(struct screen_write_ctx *ctx) screen_write_collect_end(ctx); screen_write_collect_flush(ctx, 0, __func__); - log_debug("%s: %u cells (%u written, %u skipped)", __func__, - ctx->cells, ctx->written, ctx->skipped); - if (ctx->wp != NULL) { - ctx->wp->written += ctx->written; - ctx->wp->skipped += ctx->skipped; - } - - free(ctx->item); + screen_write_free_citem(ctx->item); } /* Reset screen state. */ @@ -1094,9 +1107,10 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) void screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) { - struct screen *s = ctx->s; - struct grid_line *gl; - u_int sx = screen_size_x(s); + struct screen *s = ctx->s; + struct grid_line *gl; + u_int sx = screen_size_x(s); + struct screen_write_citem *ci = ctx->item; gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) @@ -1105,18 +1119,22 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); screen_write_collect_clear(ctx, s->cy, 1); - ctx->s->write_list[s->cy].bg = 1 + bg; - ctx->item->used = 0; + ci->x = 0; + ci->used = sx; + ci->type = CLEAR; + ci->bg = bg; + TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); + ctx->item = screen_write_get_citem(); } /* Clear to end of line from cursor. */ void screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) { - struct screen *s = ctx->s; - struct grid_line *gl; - u_int sx = screen_size_x(s); - struct screen_write_collect_item *ci = ctx->item; + struct screen *s = ctx->s; + struct grid_line *gl; + u_int sx = screen_size_x(s); + struct screen_write_citem *ci = ctx->item, *before; if (s->cx == 0) { screen_write_clearline(ctx, bg); @@ -1129,12 +1147,16 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); - screen_write_collect_clear_end(ctx, s->cy, s->cx); + before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL); ci->x = s->cx; - ci->type = CLEAR_END; + ci->used = sx - s->cx; + ci->type = CLEAR; ci->bg = bg; - TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); - ctx->item = xcalloc(1, sizeof *ctx->item); + if (before == NULL) + TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); + else + TAILQ_INSERT_BEFORE(before, ci, entry); + ctx->item = screen_write_get_citem(); } /* Clear to start of line from cursor. */ @@ -1142,8 +1164,8 @@ void screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; - u_int sx = screen_size_x(s); - struct screen_write_collect_item *ci = ctx->item; + u_int sx = screen_size_x(s); + struct screen_write_citem *ci = ctx->item, *before; if (s->cx >= sx - 1) { screen_write_clearline(ctx, bg); @@ -1155,12 +1177,16 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) else grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); - screen_write_collect_clear_start(ctx, s->cy, s->cx); - ci->x = s->cx; - ci->type = CLEAR_START; + before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL); + ci->x = 0; + ci->used = s->cx + 1; + ci->type = CLEAR; ci->bg = bg; - TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); - ctx->item = xcalloc(1, sizeof *ctx->item); + if (before == NULL) + TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); + else + TAILQ_INSERT_BEFORE(before, ci, entry); + ctx->item = screen_write_get_citem(); } /* Move cursor to px,py. */ @@ -1182,6 +1208,7 @@ screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py, if (py != -1 && (u_int)py > screen_size_y(s) - 1) py = screen_size_y(s) - 1; + log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py); screen_write_set_cursor(ctx, px, py); } @@ -1250,7 +1277,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) if (s->cy == s->rlower) { grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); - screen_write_collect_scroll(ctx); + screen_write_collect_scroll(ctx, bg); ctx->scrolled++; } else if (s->cy < screen_size_y(s) - 1) screen_write_set_cursor(ctx, -1, s->cy + 1); @@ -1276,7 +1303,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) for (i = 0; i < lines; i++) { grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); - screen_write_collect_scroll(ctx); + screen_write_collect_scroll(ctx, bg); } ctx->scrolled += lines; } @@ -1390,107 +1417,114 @@ screen_write_clearhistory(struct screen_write_ctx *ctx) grid_clear_history(ctx->s->grid); } -/* Clear to start of a collected line. */ -static void -screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x) +/* Trim collected items. */ +static struct screen_write_citem * +screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x, + u_int used, int *wrapped) { - struct screen_write_collect_item *ci, *tmp; - size_t size = 0; - u_int items = 0; + struct screen_write_cline *cl = &ctx->s->write_list[y]; + struct screen_write_citem *ci, *ci2, *tmp, *before = NULL; + u_int sx = x, ex = x + used - 1; + u_int csx, cex; - if (TAILQ_EMPTY(&ctx->s->write_list[y].items)) - return; - TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) { - switch (ci->type) { - case CLEAR_START: - break; - case CLEAR_END: - if (ci->x <= x) - ci->x = x; + if (TAILQ_EMPTY(&cl->items)) + return (NULL); + TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { + csx = ci->x; + cex = ci->x + ci->used - 1; + + /* Item is entirely before. */ + if (cex < sx) { + log_debug("%s: %p %u-%u before %u-%u", __func__, ci, + csx, cex, sx, ex); continue; - case TEXT: - if (ci->x > x) - continue; + } + + /* Item is entirely after. */ + if (csx > ex) { + log_debug("%s: %p %u-%u after %u-%u", __func__, ci, + csx, cex, sx, ex); + before = ci; break; } - items++; - size += ci->used; - TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry); - free(ci); - } - ctx->skipped += size; - log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items, - size, y); -} -/* Clear to end of a collected line. */ -static void -screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x) -{ - struct screen_write_collect_item *ci, *tmp; - size_t size = 0; - u_int items = 0; - - if (TAILQ_EMPTY(&ctx->s->write_list[y].items)) - return; - TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) { - switch (ci->type) { - case CLEAR_START: - if (ci->x >= x) - ci->x = x; + /* Item is entirely inside. */ + if (csx >= sx && cex <= ex) { + log_debug("%s: %p %u-%u inside %u-%u", __func__, ci, + csx, cex, sx, ex); + TAILQ_REMOVE(&cl->items, ci, entry); + screen_write_free_citem(ci); + if (csx == 0 && ci->wrapped && wrapped != NULL) + *wrapped = 1; continue; - case CLEAR_END: - break; - case TEXT: - if (ci->x < x) - continue; + } + + /* Item under the start. */ + if (csx < sx && cex >= sx && cex <= ex) { + log_debug("%s: %p %u-%u start %u-%u", __func__, ci, + csx, cex, sx, ex); + ci->used = sx - csx; + log_debug("%s: %p now %u-%u", __func__, ci, ci->x, + ci->x + ci->used + 1); + continue; + } + + /* Item covers the end. */ + if (cex > ex && csx >= sx && csx <= ex) { + log_debug("%s: %p %u-%u end %u-%u", __func__, ci, + csx, cex, sx, ex); + ci->x = ex + 1; + ci->used = cex - ex; + log_debug("%s: %p now %u-%u", __func__, ci, ci->x, + ci->x + ci->used + 1); + before = ci; break; } - items++; - size += ci->used; - TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry); - free(ci); + + /* Item must cover both sides. */ + log_debug("%s: %p %u-%u under %u-%u", __func__, ci, + csx, cex, sx, ex); + ci2 = screen_write_get_citem(); + ci2->type = ci->type; + ci2->bg = ci->bg; + memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc); + TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry); + + ci->used = sx - csx; + ci2->x = ex + 1; + ci2->used = cex - ex; + + log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci, + ci->x, ci->x + ci->used - 1, ci, ci2->x, + ci2->x + ci2->used - 1, ci2); + before = ci2; + break; } - ctx->skipped += size; - log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items, - size, y); + return (before); } /* Clear collected lines. */ static void screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) { - struct screen_write_collect_item *ci, *tmp; - struct screen_write_collect_line *cl; - u_int i, items; - size_t size; + struct screen_write_cline *cl; + u_int i; for (i = y; i < y + n; i++) { - if (TAILQ_EMPTY(&ctx->s->write_list[i].items)) - continue; - items = 0; - size = 0; cl = &ctx->s->write_list[i]; - TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { - items++; - size += ci->used; - TAILQ_REMOVE(&cl->items, ci, entry); - free(ci); - } - ctx->skipped += size; - log_debug("%s: dropped %u items (%zu bytes) (line %u)", - __func__, items, size, y); + TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry); } } /* Scroll collected lines up. */ static void -screen_write_collect_scroll(struct screen_write_ctx *ctx) +screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg) { - struct screen *s = ctx->s; - struct screen_write_collect_line *cl; - u_int y; - char *saved; + struct screen *s = ctx->s; + struct screen_write_cline *cl; + u_int y; + char *saved; + struct screen_write_citem *ci; log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, s->rupper, s->rlower); @@ -1500,11 +1534,16 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx) for (y = s->rupper; y < s->rlower; y++) { cl = &ctx->s->write_list[y + 1]; TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry); - ctx->s->write_list[y].bg = cl->bg; ctx->s->write_list[y].data = cl->data; } - ctx->s->write_list[s->rlower].bg = 1 + 8; ctx->s->write_list[s->rlower].data = saved; + + ci = screen_write_get_citem(); + ci->x = 0; + ci->used = screen_size_x(s); + ci->type = CLEAR; + ci->bg = bg; + TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry); } /* Flush collected lines. */ @@ -1512,12 +1551,11 @@ static void screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, const char *from) { - struct screen *s = ctx->s; - struct screen_write_collect_item *ci, *tmp; - struct screen_write_collect_line *cl; - u_int y, cx, cy, items = 0; - struct tty_ctx ttyctx; - size_t written = 0; + struct screen *s = ctx->s; + struct screen_write_citem *ci, *tmp; + struct screen_write_cline *cl; + u_int y, cx, cy, last, items = 0; + struct tty_ctx ttyctx; if (ctx->scrolled != 0) { log_debug("%s: scrolled %u (region %u-%u)", __func__, @@ -1539,22 +1577,18 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, cx = s->cx; cy = s->cy; for (y = 0; y < screen_size_y(s); y++) { cl = &ctx->s->write_list[y]; - if (cl->bg != 0) { - screen_write_set_cursor(ctx, 0, y); - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = cl->bg - 1; - tty_write(tty_cmd_clearline, &ttyctx); - } + last = UINT_MAX; TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { + if (last != UINT_MAX && ci->x <= last) { + fatalx("collect list not in order: %u <= %u", + ci->x, last); + } screen_write_set_cursor(ctx, ci->x, y); - if (ci->type == CLEAR_END) { + if (ci->type == CLEAR) { screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = ci->bg; - tty_write(tty_cmd_clearendofline, &ttyctx); - } else if (ci->type == CLEAR_START) { - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.bg = ci->bg; - tty_write(tty_cmd_clearstartofline, &ttyctx); + ttyctx.num = ci->used; + tty_write(tty_cmd_clearcharacter, &ttyctx); } else { screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = &ci->gc; @@ -1563,38 +1597,41 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, ttyctx.num = ci->used; tty_write(tty_cmd_cells, &ttyctx); } - items++; - written += ci->used; TAILQ_REMOVE(&cl->items, ci, entry); - free(ci); + screen_write_free_citem(ci); + last = ci->x; } - cl->bg = 0; } s->cx = cx; s->cy = cy; - log_debug("%s: flushed %u items (%zu bytes) (%s)", __func__, items, - written, from); - ctx->written += written; + log_debug("%s: flushed %u items (%s)", __func__, items, from); } /* Finish and store collected cells. */ void screen_write_collect_end(struct screen_write_ctx *ctx) { - struct screen *s = ctx->s; - struct screen_write_collect_item *ci = ctx->item; - struct screen_write_collect_line *cl = &s->write_list[s->cy]; - struct grid_cell gc; - u_int xx; + struct screen *s = ctx->s; + struct screen_write_citem *ci = ctx->item, *before; + struct screen_write_cline *cl = &s->write_list[s->cy]; + struct grid_cell gc; + u_int xx; + int wrapped = ci->wrapped; if (ci->used == 0) return; + before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used, + &wrapped); ci->x = s->cx; - TAILQ_INSERT_TAIL(&cl->items, ci, entry); - ctx->item = xcalloc(1, sizeof *ctx->item); + ci->wrapped = wrapped; + if (before == NULL) + TAILQ_INSERT_TAIL(&cl->items, ci, entry); + else + TAILQ_INSERT_BEFORE(before, ci, entry); + ctx->item = screen_write_get_citem(); log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used, (int)ci->used, cl->data + ci->x, s->cx, s->cy); @@ -1630,10 +1667,10 @@ void screen_write_collect_add(struct screen_write_ctx *ctx, const struct grid_cell *gc) { - struct screen *s = ctx->s; - struct screen_write_collect_item *ci; - u_int sx = screen_size_x(s); - int collect; + struct screen *s = ctx->s; + struct screen_write_citem *ci; + u_int sx = screen_size_x(s); + int collect; /* * Don't need to check that the attributes and whatnot are still the @@ -1658,7 +1695,6 @@ screen_write_collect_add(struct screen_write_ctx *ctx, screen_write_cell(ctx, gc); return; } - ctx->cells++; if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx) screen_write_collect_end(ctx); @@ -1695,7 +1731,6 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) /* Ignore padding cells. */ if (gc->flags & GRID_FLAG_PADDING) return; - ctx->cells++; /* If the width is zero, combine onto the previous character. */ if (width == 0) { @@ -1822,9 +1857,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) } else ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); - ctx->written++; - } else - ctx->skipped++; + } } /* Combine a UTF-8 zero-width character onto the previous. */ diff --git a/tmux.1 b/tmux.1 index 160e6a92..247f0fb6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4823,7 +4823,6 @@ The following variables are available, where appropriate: .It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" .It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" -.It Li "pane_skipped" Ta "" Ta "Bytes skipped as not visible in pane" .It Li "pane_start_command" Ta "" Ta "Command pane started with" .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" @@ -4831,7 +4830,6 @@ The following variables are available, where appropriate: .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" -.It Li "pane_written" Ta "" Ta "Bytes written by pane (aside from redrawing)" .It Li "pid" Ta "" Ta "Server PID" .It Li "popup_key" Ta "" Ta "Key pressed in popup" .It Li "popup_mouse_x" Ta "" Ta "Mouse X position in popup" diff --git a/tmux.h b/tmux.h index c593a175..ce45cece 100644 --- a/tmux.h +++ b/tmux.h @@ -55,8 +55,8 @@ struct mouse_event; struct options; struct options_array_item; struct options_entry; -struct screen_write_collect_item; -struct screen_write_collect_line; +struct screen_write_citem; +struct screen_write_cline; struct screen_write_ctx; struct session; struct tty_ctx; @@ -794,55 +794,51 @@ struct style { struct screen_sel; struct screen_titles; struct screen { - char *title; - char *path; - struct screen_titles *titles; + char *title; + char *path; + struct screen_titles *titles; - struct grid *grid; /* grid data */ + struct grid *grid; /* grid data */ - u_int cx; /* cursor x */ - u_int cy; /* cursor y */ + u_int cx; /* cursor x */ + u_int cy; /* cursor y */ - u_int cstyle; /* cursor style */ - char *ccolour; /* cursor colour string */ + u_int cstyle; /* cursor style */ + char *ccolour; /* cursor colour string */ - u_int rupper; /* scroll region top */ - u_int rlower; /* scroll region bottom */ + u_int rupper; /* scroll region top */ + u_int rlower; /* scroll region bottom */ - int mode; + int mode; - u_int saved_cx; - u_int saved_cy; - struct grid *saved_grid; - struct grid_cell saved_cell; - int saved_flags; + u_int saved_cx; + u_int saved_cy; + struct grid *saved_grid; + struct grid_cell saved_cell; + int saved_flags; - bitstr_t *tabs; - struct screen_sel *sel; + bitstr_t *tabs; + struct screen_sel *sel; - struct screen_write_collect_line *write_list; + struct screen_write_cline *write_list; }; /* Screen write context. */ typedef void (*screen_write_init_ctx_cb)(struct screen_write_ctx *, struct tty_ctx *); struct screen_write_ctx { - struct window_pane *wp; - struct screen *s; + struct window_pane *wp; + struct screen *s; - int flags; + int flags; #define SCREEN_WRITE_SYNC 0x1 - screen_write_init_ctx_cb init_ctx_cb; - void *arg; + screen_write_init_ctx_cb init_ctx_cb; + void *arg; - struct screen_write_collect_item *item; - u_int scrolled; - u_int bg; - - u_int cells; - u_int written; - u_int skipped; + struct screen_write_citem *item; + u_int scrolled; + u_int bg; }; /* Screen redraw context. */ @@ -1001,9 +997,6 @@ struct window_pane { char *searchstr; int searchregex; - size_t written; - size_t skipped; - int border_gc_set; struct grid_cell border_gc; From 509221520c87510016f5c90aeea0d4dcc4b74a98 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Feb 2021 08:01:14 +0000 Subject: [PATCH 0683/1006] Add a no-detached choice to detach-on-destroy which detaches only if there are no other detached sessions to switch to, from Sencer Selcuk in GitHub issue 2553. --- options-table.c | 6 +++++- server-fn.c | 25 +++++++++++++++++++++---- tmux.1 | 6 +++++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/options-table.c b/options-table.c index 74a59134..9fc69db5 100644 --- a/options-table.c +++ b/options-table.c @@ -72,6 +72,9 @@ static const char *options_table_window_size_list[] = { static const char *options_table_remain_on_exit_list[] = { "off", "on", "failed", NULL }; +static const char *options_table_detach_on_destroy_list[] = { + "off", "on", "no-detached", NULL +}; /* Status line format. */ #define OPTIONS_TABLE_STATUS_FORMAT1 \ @@ -405,8 +408,9 @@ const struct options_table_entry options_table[] = { }, { .name = "detach-on-destroy", - .type = OPTIONS_TABLE_FLAG, + .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, + .choices = options_table_detach_on_destroy_list, .default_num = 1, .text = "Whether to detach when a session is destroyed, or switch " "the client to another session if any exist." diff --git a/server-fn.c b/server-fn.c index d0e06c0f..4358fa5c 100644 --- a/server-fn.c +++ b/server-fn.c @@ -401,9 +401,8 @@ server_destroy_session_group(struct session *s) static struct session * server_next_session(struct session *s) { - struct session *s_loop, *s_out; + struct session *s_loop, *s_out = NULL; - s_out = NULL; RB_FOREACH(s_loop, sessions, &sessions) { if (s_loop == s) continue; @@ -414,17 +413,35 @@ server_next_session(struct session *s) return (s_out); } +static struct session * +server_next_detached_session(struct session *s) +{ + struct session *s_loop, *s_out = NULL; + + RB_FOREACH(s_loop, sessions, &sessions) { + if (s_loop == s || s_loop->attached) + continue; + if (s_out == NULL || + timercmp(&s_loop->activity_time, &s_out->activity_time, <)) + s_out = s_loop; + } + return (s_out); +} + void server_destroy_session(struct session *s) { struct client *c; struct session *s_new; + int detach_on_destroy; - if (!options_get_number(s->options, "detach-on-destroy")) + detach_on_destroy = options_get_number(s->options, "detach-on-destroy"); + if (detach_on_destroy == 0) s_new = server_next_session(s); + else if (detach_on_destroy == 2) + s_new = server_next_detached_session(s); else s_new = NULL; - TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; diff --git a/tmux.1 b/tmux.1 index 247f0fb6..12d555e5 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3575,12 +3575,16 @@ The default is 80x24. If enabled and the session is no longer attached to any clients, it is destroyed. .It Xo Ic detach-on-destroy -.Op Ic on | off +.Op Ic off | on | no-detached .Xc If on (the default), the client is detached when the session it is attached to is destroyed. If off, the client is switched to the most recently active of the remaining sessions. +If +.Ic no-detached , +the client is detached only if there are no detached sessions; if detached +sessions exist, the client is switched to the most recently active. .It Ic display-panes-active-colour Ar colour Set the colour used by the .Ic display-panes From 5c48086e5c8e4ea9633fd6b913d33dcc2b47f2d7 Mon Sep 17 00:00:00 2001 From: jmc Date: Tue, 2 Feb 2021 07:33:29 +0000 Subject: [PATCH 0684/1006] article fixes; from eddie youseph --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 12d555e5..c076f0cb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1362,7 +1362,7 @@ a pane ID such as .Ql %0 ; .Ql %* for all panes in the attached session; -an window ID such as +a window ID such as .Ql @0 ; or .Ql @* From f0546b0ff816d1ca8199fc726f06639535cf526e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Feb 2021 13:03:03 +0000 Subject: [PATCH 0685/1006] Fix popup mouse position. --- popup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/popup.c b/popup.c index 97acebfd..675bb2f9 100644 --- a/popup.c +++ b/popup.c @@ -323,8 +323,8 @@ popup_key_cb(struct client *c, struct key_event *event) return (0); if (KEYC_IS_MOUSE(event->key)) { /* Must be inside, checked already. */ - if (!input_key_get_mouse(&pd->s, m, m->x - pd->px, - m->y - pd->py, &buf, &len)) + if (!input_key_get_mouse(&pd->s, m, m->x - pd->px - 1, + m->y - pd->py - 1, &buf, &len)) return (0); bufferevent_write(job_get_event(pd->job), buf, len); return (0); From c13f2e1135df1f8be78262eb6f5ccb251a7e1d61 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Feb 2021 14:02:24 +0000 Subject: [PATCH 0686/1006] Redraw status line and borders on pane enable/disable, GitHub issue 2554. --- cmd-select-pane.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 30529722..fa388548 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -108,11 +108,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "no last pane"); return (CMD_RETURN_ERROR); } - if (args_has(args, 'e')) + if (args_has(args, 'e')) { lastwp->flags &= ~PANE_INPUTOFF; - else if (args_has(args, 'd')) + server_redraw_window_borders(lastwp->window); + server_status_window(lastwp->window); + } else if (args_has(args, 'd')) { lastwp->flags |= PANE_INPUTOFF; - else { + server_redraw_window_borders(lastwp->window); + server_status_window(lastwp->window); + } else { if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, lastwp); @@ -188,10 +192,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'e')) { wp->flags &= ~PANE_INPUTOFF; + server_redraw_window_borders(wp->window); + server_status_window(wp->window); return (CMD_RETURN_NORMAL); } if (args_has(args, 'd')) { wp->flags |= PANE_INPUTOFF; + server_redraw_window_borders(wp->window); + server_status_window(wp->window); return (CMD_RETURN_NORMAL); } From e3d71d9bdfa31fb658794759f07af43d53253e5f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Feb 2021 11:00:45 +0000 Subject: [PATCH 0687/1006] Add compat clock_gettime for older macOS. GitHub issue 2555. --- compat.h | 12 ++++++++++++ compat/clock_gettime.c | 37 +++++++++++++++++++++++++++++++++++++ configure.ac | 1 + 3 files changed, 50 insertions(+) create mode 100644 compat/clock_gettime.c diff --git a/compat.h b/compat.h index 828c956d..13334ad7 100644 --- a/compat.h +++ b/compat.h @@ -265,6 +265,13 @@ void warnx(const char *, ...); #define HOST_NAME_MAX 255 #endif +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC CLOCK_REALTIME +#endif + #ifndef HAVE_FLOCK #define LOCK_SH 0 #define LOCK_EX 0 @@ -342,6 +349,11 @@ const char *getprogname(void); void setproctitle(const char *, ...); #endif +#ifndef HAVE_CLOCK_GETTIME +/* clock_gettime.c */ +int clock_gettime(int, struct timespec *); +#endif + #ifndef HAVE_B64_NTOP /* base64.c */ #undef b64_ntop diff --git a/compat/clock_gettime.c b/compat/clock_gettime.c new file mode 100644 index 00000000..8290e75c --- /dev/null +++ b/compat/clock_gettime.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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" + +#ifndef TIMEVAL_TO_TIMESPEC +#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ +} while (0) +#endif + +int +clock_gettime(int clock, struct timespec *ts) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, ts); + return 0; +} diff --git a/configure.ac b/configure.ac index 269dd5aa..4175f5c8 100644 --- a/configure.ac +++ b/configure.ac @@ -133,6 +133,7 @@ AC_CHECK_FUNCS([ \ AC_REPLACE_FUNCS([ \ asprintf \ cfmakeraw \ + clock_gettime \ closefrom \ explicit_bzero \ fgetln \ From be471c328ea0ae04026e4ff32fda7b7f11c74255 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Feb 2021 12:23:49 +0000 Subject: [PATCH 0688/1006] Add a -S flag to new-window to make it select the existing window if one with the given name already exists rather than failing with an error. Also add a format to check if a window or session name exists which allows the same with other commands. Requested by and discussed with kn@. --- cmd-new-window.c | 38 ++++++++++++++++++++++++++---- format.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++- tmux.1 | 21 ++++++++++++++++- 3 files changed, 114 insertions(+), 6 deletions(-) diff --git a/cmd-new-window.c b/cmd-new-window.c index ca3e66c4..712e2a79 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -38,8 +38,8 @@ const struct cmd_entry cmd_new_window_entry = { .name = "new-window", .alias = "neww", - .args = { "abc:de:F:kn:Pt:", 0, -1 }, - .usage = "[-abdkP] [-c start-directory] [-e environment] [-F format] " + .args = { "abc:de:F:kn:PSt:", 0, -1 }, + .usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] " "[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX }, @@ -52,6 +52,7 @@ static enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *c = cmdq_get_client(item); struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; @@ -59,12 +60,41 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) struct session *s = target->s; struct winlink *wl = target->wl; int idx = target->idx, before; - struct winlink *new_wl; + struct winlink *new_wl = NULL; char *cause = NULL, *cp; - const char *template, *add; + const char *template, *add, *name; struct cmd_find_state fs; struct args_value *value; + /* + * If -S and -n are given and -t is not and a single window with this + * name already exists, select it. + */ + name = args_get(args, 'n'); + if (args_has(args, 'S') && name != NULL && target->idx == -1) { + RB_FOREACH(wl, winlinks, &s->windows) { + if (strcmp(wl->window->name, name) != 0) + continue; + if (new_wl == NULL) { + new_wl = wl; + continue; + } + cmdq_error(item, "multiple windows named %s", name); + return (CMD_RETURN_ERROR); + } + if (new_wl != NULL) { + if (args_has(args, 'd')) + return (CMD_RETURN_NORMAL); + if (session_set_current(s, new_wl) == 0) + server_redraw_session(s); + if (c != NULL && c->session != NULL) + s->curw->window->latest = c; + recalculate_sizes(); + return (CMD_RETURN_NORMAL); + } + } + + before = args_has(args, 'b'); if (args_has(args, 'a') || before) { idx = winlink_shuffle_up(s, wl, before); diff --git a/format.c b/format.c index 25b80249..ecf299d1 100644 --- a/format.c +++ b/format.c @@ -100,6 +100,8 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_LENGTH 0x800 #define FORMAT_WIDTH 0x1000 #define FORMAT_QUOTE_STYLE 0x2000 +#define FORMAT_WINDOW_NAME 0x4000 +#define FORMAT_SESSION_NAME 0x8000 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 @@ -1733,7 +1735,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, } /* Now try single character with arguments. */ - if (strchr("mCst=peq", cp[0]) == NULL) + if (strchr("mCNst=peq", cp[0]) == NULL) break; c = cp[0]; @@ -1857,6 +1859,24 @@ format_search(struct format_modifier *fm, struct window_pane *wp, const char *s) return (value); } +/* Does session name exist? */ +static char * +format_session_name(struct format_expand_state *es, const char *fmt) +{ + char *name; + struct session *s; + + name = format_expand1(es, fmt); + RB_FOREACH(s, sessions, &sessions) { + if (strcmp(s->name, name) == 0) { + free(name); + return (xstrdup("1")); + } + } + free(name); + return (xstrdup("0")); +} + /* Loop over sessions. */ static char * format_loop_sessions(struct format_expand_state *es, const char *fmt) @@ -1892,6 +1912,30 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt) return (value); } +/* Does window name exist? */ +static char * +format_window_name(struct format_expand_state *es, const char *fmt) +{ + struct format_tree *ft = es->ft; + char *name; + struct winlink *wl; + + if (ft->s == NULL) { + format_log(es, "window name but no session"); + return (NULL); + } + + name = format_expand1(es, fmt); + RB_FOREACH(wl, winlinks, &ft->s->windows) { + if (strcmp(wl->window->name, name) == 0) { + free(name); + return (xstrdup("1")); + } + } + free(name); + return (xstrdup("0")); +} + /* Loop over windows. */ static char * format_loop_windows(struct format_expand_state *es, const char *fmt) @@ -2251,6 +2295,13 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'T': modifiers |= FORMAT_EXPANDTIME; break; + case 'N': + if (fm->argc < 1 || + strchr(fm->argv[0], 'w') != NULL) + modifiers |= FORMAT_WINDOW_NAME; + else if (strchr(fm->argv[0], 's') != NULL) + modifiers |= FORMAT_SESSION_NAME; + break; case 'S': modifiers |= FORMAT_SESSIONS; break; @@ -2291,6 +2342,14 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, value = format_loop_panes(es, copy); if (value == NULL) goto fail; + } else if (modifiers & FORMAT_WINDOW_NAME) { + value = format_window_name(es, copy); + if (value == NULL) + goto fail; + } else if (modifiers & FORMAT_SESSION_NAME) { + value = format_session_name(es, copy); + if (value == NULL) + goto fail; } else if (search != NULL) { /* Search in pane. */ new = format_expand1(es, copy); diff --git a/tmux.1 b/tmux.1 index c076f0cb..530cd559 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2348,7 +2348,7 @@ the .Ic base-index option. .It Xo Ic new-window -.Op Fl abdkP +.Op Fl abdkPS .Op Fl c Ar start-directory .Op Fl e Ar environment .Op Fl F Ar format @@ -2377,6 +2377,14 @@ represents the window to be created; if the target already exists an error is shown, unless the .Fl k flag is used, in which case it is destroyed. +If +.Fl S +is given and a window named +.Ar window-name +already exists, it is selected (unless +.Fl d +is also given in which case the command does nothing). +.Pp .Ar shell-command is the command to execute. If @@ -4688,6 +4696,17 @@ For example, to get a list of windows formatted like the status line: #{W:#{E:window-status-format} ,#{E:window-status-current-format} } .Ed .Pp +.Ql N:\& +checks if a window (without any suffix or with the +.Ql w +suffix) or a session (with the +.Ql s +suffix) name exists, for example +.Ql `N/w:foo` +is replaced with 1 if a window named +.Ql foo +exists. +.Pp A prefix of the form .Ql s/foo/bar/:\& will substitute From 3dddc11603b1bd66b5586b47795716e87921a096 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Feb 2021 12:29:18 +0000 Subject: [PATCH 0689/1006] Send Unicode directional isolate characters around horizontal pane borders if the terminal support UTF-8 and an extension terminfo(5) capability "Bidi" is present. On terminals with BiDi support (ie, VTE) this seems to be enough to display right-to-left text acceptably enough to be usable (with some caveats about the mouse position). Requested by and with help from Mahmoud Elagdar in GitHub issue 2425. --- grid.c | 4 ++-- screen-redraw.c | 24 +++++++++++++++++++----- tmux.1 | 4 ++++ tmux.h | 1 + tty-term.c | 1 + 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/grid.c b/grid.c index cce304c9..7744587a 100644 --- a/grid.c +++ b/grid.c @@ -1045,14 +1045,14 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, srcl->cellsize * sizeof *dstl->celldata); } else dstl->celldata = NULL; - if (srcl->extdsize != 0) { dstl->extdsize = srcl->extdsize; dstl->extddata = xreallocarray(NULL, dstl->extdsize, sizeof *dstl->extddata); memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * sizeof *dstl->extddata); - } + } else + dstl->extddata = NULL; sy++; dy++; diff --git a/screen-redraw.c b/screen-redraw.c index 47c1a838..6ddabc52 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -32,8 +32,8 @@ static void screen_redraw_set_context(struct client *, struct screen_redraw_ctx *); #define CELL_INSIDE 0 -#define CELL_LEFTRIGHT 1 -#define CELL_TOPBOTTOM 2 +#define CELL_TOPBOTTOM 1 +#define CELL_LEFTRIGHT 2 #define CELL_TOPLEFT 3 #define CELL_TOPRIGHT 4 #define CELL_BOTTOMLEFT 5 @@ -47,6 +47,9 @@ static void screen_redraw_set_context(struct client *, #define CELL_BORDERS " xqlkmjwvtun~" +#define START_ISOLATE "\342\201\246" +#define END_ISOLATE "\342\201\251" + static const struct utf8_data screen_redraw_double_borders[] = { { "", 0, 0, 0 }, { "\342\225\221", 0, 3, 1 }, /* U+2551 */ @@ -299,7 +302,7 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, case 13: /* 1101, left right bottom */ return (CELL_TOPJOIN); case 12: /* 1100, left right */ - return (CELL_TOPBOTTOM); + return (CELL_LEFTRIGHT); case 11: /* 1011, left top bottom */ return (CELL_RIGHTJOIN); case 10: /* 1010, left top */ @@ -313,7 +316,7 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, case 5: /* 0101, right bottom */ return (CELL_TOPLEFT); case 3: /* 0011, top bottom */ - return (CELL_LEFTRIGHT); + return (CELL_TOPBOTTOM); } return (CELL_OUTSIDE); } @@ -680,7 +683,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) struct tty *tty = &c->tty; struct window_pane *wp; u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; - int pane_status = ctx->pane_status; + int pane_status = ctx->pane_status, isolates; struct grid_cell gc; const struct grid_cell *tmp; @@ -705,11 +708,22 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) } screen_redraw_border_set(wp, ctx->pane_lines, cell_type, &gc); + if (cell_type == CELL_TOPBOTTOM && + (c->flags & CLIENT_UTF8) && + tty_term_has(tty->term, TTYC_BIDI)) + isolates = 1; + else + isolates = 0; + if (ctx->statustop) tty_cursor(tty, i, ctx->statuslines + j); else tty_cursor(tty, i, j); + if (isolates) + tty_puts(tty, END_ISOLATE); tty_cell(tty, &gc, &grid_default_cell, NULL); + if (isolates) + tty_puts(tty, START_ISOLATE); } /* Draw the borders. */ diff --git a/tmux.1 b/tmux.1 index 530cd559..090b9436 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5953,6 +5953,10 @@ option should be used. An existing extension that tells .Nm the terminal supports default colours. +.It Em \&Bidi +Tell +.Nm +that the terminal supports the VTE bidirectional text extensions. .It Em \&Cs , Cr Set the cursor colour. The first takes a single string argument and is used to set the colour; diff --git a/tmux.h b/tmux.h index ce45cece..1cca8c8f 100644 --- a/tmux.h +++ b/tmux.h @@ -261,6 +261,7 @@ enum tty_code_code { TTYC_AX, TTYC_BCE, TTYC_BEL, + TTYC_BIDI, TTYC_BLINK, TTYC_BOLD, TTYC_CIVIS, diff --git a/tty-term.c b/tty-term.c index b4feea60..b989328c 100644 --- a/tty-term.c +++ b/tty-term.c @@ -58,6 +58,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_AX] = { TTYCODE_FLAG, "AX" }, [TTYC_BCE] = { TTYCODE_FLAG, "bce" }, [TTYC_BEL] = { TTYCODE_STRING, "bel" }, + [TTYC_BIDI] = { TTYCODE_STRING, "Bidi" }, [TTYC_BLINK] = { TTYCODE_STRING, "blink" }, [TTYC_BOLD] = { TTYCODE_STRING, "bold" }, [TTYC_CIVIS] = { TTYCODE_STRING, "civis" }, From 1e29ebd41289543897b73354e97c654641a4e079 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 6 Feb 2021 13:02:52 +0000 Subject: [PATCH 0690/1006] In the end UTF-8 did not become a terminal feature, should not be listed in man page. --- tmux.1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 090b9436..85b3ee45 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3458,8 +3458,6 @@ Supports title setting. .It usstyle Allows underscore style and colour to be set. -.It UTF-8 -Is able to handle UTF-8 output. .El .It Ic terminal-overrides[] Ar string Allow terminal descriptions read using From c579be1f2a8205c6405f7c6fdb229b31f30274c8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 8 Feb 2021 08:33:54 +0000 Subject: [PATCH 0691/1006] Include "focused" in client flags, from Dan Aloni in GitHub issue 2558. --- server-client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server-client.c b/server-client.c index a55188d1..52df8110 100644 --- a/server-client.c +++ b/server-client.c @@ -2454,6 +2454,8 @@ server_client_get_flags(struct client *c) *s = '\0'; if (c->flags & CLIENT_ATTACHED) strlcat(s, "attached,", sizeof s); + if (c->flags & CLIENT_FOCUSED) + strlcat(s, "focused,", sizeof s); if (c->flags & CLIENT_CONTROL) strlcat(s, "control-mode,", sizeof s); if (c->flags & CLIENT_IGNORESIZE) From e3005e5ec4385d284abdeb3cecafc69c14655649 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 8 Feb 2021 14:46:53 +0000 Subject: [PATCH 0692/1006] Add "pipe" variants of the "copy-pipe" commands which do not copy, from Christian Zangl. --- tmux.1 | 7 ++++- window-copy.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/tmux.1 b/tmux.1 index 85b3ee45..775d0e28 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1653,6 +1653,9 @@ The following commands are supported in copy mode: .It Li "page-down" Ta "C-f" Ta "PageDown" .It Li "page-down-and-cancel" Ta "" Ta "" .It Li "page-up" Ta "C-b" Ta "PageUp" +.It Li "pipe [] []" Ta "" Ta "" +.It Li "pipe-no-clear [] []" Ta "" Ta "" +.It Li "pipe-and-cancel [] []" Ta "" Ta "" .It Li "previous-matching-bracket" Ta "" Ta "M-C-b" .It Li "previous-paragraph" Ta "{" Ta "M-{" .It Li "previous-space" Ta "B" Ta "" @@ -1708,7 +1711,9 @@ so buffers are named .Ql buffer1 and so on). Pipe commands take a command argument which is the command to which the -copied text is piped. +selected text is piped. +.Ql copy-pipe +variants also copy the selection. The .Ql -and-cancel variants of some commands exit copy mode after they have completed (for copy diff --git a/window-copy.c b/window-copy.c index 89b2a9af..c6b25413 100644 --- a/window-copy.c +++ b/window-copy.c @@ -92,6 +92,8 @@ static void window_copy_synchronize_cursor(struct window_mode_entry *, int); static void *window_copy_get_selection(struct window_mode_entry *, size_t *); static void window_copy_copy_buffer(struct window_mode_entry *, const char *, void *, size_t); +static void window_copy_pipe(struct window_mode_entry *, + struct session *, const char *); static void window_copy_copy_pipe(struct window_mode_entry *, struct session *, const char *, const char *); static void window_copy_copy_selection(struct window_mode_entry *, @@ -1874,6 +1876,44 @@ window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_CANCEL); } +static enum window_copy_cmd_action +window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct client *c = cs->c; + struct session *s = cs->s; + struct winlink *wl = cs->wl; + struct window_pane *wp = wme->wp; + char *command = NULL; + + if (s != NULL && cs->args->argc > 1 && *cs->args->argv[1] != '\0') + command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + window_copy_pipe(wme, s, command); + free(command); + + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_pipe(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cmd_pipe_no_clear(cs); + window_copy_clear_selection(wme); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cmd_pipe_no_clear(cs); + window_copy_clear_selection(wme); + return (WINDOW_COPY_CMD_CANCEL); +} + static enum window_copy_cmd_action window_copy_cmd_goto_line(struct window_copy_cmd_state *cs) { @@ -2267,6 +2307,12 @@ static const struct { window_copy_cmd_page_down_and_cancel }, { "page-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, window_copy_cmd_page_up }, + { "pipe-no-clear", 0, 1, WINDOW_COPY_CMD_CLEAR_NEVER, + window_copy_cmd_pipe_no_clear }, + { "pipe", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, + window_copy_cmd_pipe }, + { "pipe-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, + window_copy_cmd_pipe_and_cancel }, { "previous-matching-bracket", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_previous_matching_bracket }, { "previous-paragraph", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, @@ -3840,22 +3886,41 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, paste_add(prefix, buf, len); } -static void -window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, - const char *prefix, const char *cmd) +static void * +window_copy_pipe_run(struct window_mode_entry *wme, struct session *s, + const char *cmd, size_t *len) { void *buf; - size_t len; struct job *job; - buf = window_copy_get_selection(wme, &len); + buf = window_copy_get_selection(wme, len); if (cmd == NULL || *cmd == '\0') cmd = options_get_string(global_options, "copy-command"); if (cmd != NULL && *cmd != '\0') { job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT, -1, -1); - bufferevent_write(job_get_event(job), buf, len); + bufferevent_write(job_get_event(job), buf, *len); } + return (buf); +} + +static void +window_copy_pipe(struct window_mode_entry *wme, struct session *s, + const char *cmd) +{ + size_t len; + + window_copy_pipe_run(wme, s, cmd, &len); +} + +static void +window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, + const char *prefix, const char *cmd) +{ + void *buf; + size_t len; + + buf = window_copy_pipe_run(wme, s, cmd, &len); if (buf != NULL) window_copy_copy_buffer(wme, prefix, buf, len); } From 1492ae11a5c4f29e783f1d49f3580ee7f4d276e4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Feb 2021 14:25:40 +0000 Subject: [PATCH 0693/1006] Do not expand times and #() inside #(). --- format.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index ecf299d1..0e773498 100644 --- a/format.c +++ b/format.c @@ -367,7 +367,10 @@ format_job_get(struct format_expand_state *es, const char *cmd) RB_INSERT(format_job_tree, jobs, fj); } - expanded = format_expand1(es, cmd); + format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS); + next.flags &= ~FORMAT_EXPAND_TIME; + + expanded = format_expand1(&next, cmd); if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) { free((void *)fj->expanded); fj->expanded = xstrdup(expanded); @@ -393,7 +396,6 @@ format_job_get(struct format_expand_state *es, const char *cmd) if (ft->flags & FORMAT_STATUS) fj->status = 1; - format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS); return (format_expand1(&next, fj->out)); } From 32186950f5ae3e3517f39b2723cc247a9da93d8c Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 10 Feb 2021 07:17:07 +0000 Subject: [PATCH 0694/1006] Use ~/.tmux.conf as an example rather than /etc/passwd, suggested by deraadt@. --- tmux.1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tmux.1 b/tmux.1 index 775d0e28..e1891009 100644 --- a/tmux.1 +++ b/tmux.1 @@ -872,12 +872,12 @@ arguments are commands. This may be a single argument passed to the shell, for example: .Bd -literal -offset indent -new-window 'vi /etc/passwd' +new-window 'vi ~/.tmux.conf' .Ed .Pp Will run: .Bd -literal -offset indent -/bin/sh -c 'vi /etc/passwd' +/bin/sh -c 'vi ~/.tmux.conf' .Ed .Pp Additionally, the @@ -894,7 +894,7 @@ to be given as multiple arguments and executed directly (without This can avoid issues with shell quoting. For example: .Bd -literal -offset indent -$ tmux new-window vi /etc/passwd +$ tmux new-window vi ~/.tmux.conf .Ed .Pp Will run @@ -940,7 +940,7 @@ $ tmux kill-window -t :1 $ tmux new-window \e; split-window -d -$ tmux new-session -d 'vi /etc/passwd' \e; split-window -d \e; attach +$ tmux new-session -d 'vi ~/.tmux.conf' \e; split-window -d \e; attach .Ed .Sh CLIENTS AND SESSIONS The From 679b2288e81a0aa1c375a5ae16c23866114ed766 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Feb 2021 17:18:37 +0000 Subject: [PATCH 0695/1006] Restore utf8proc bits that went missing, GitHub issue 2564. --- utf8.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/utf8.c b/utf8.c index 458363b8..f43945e6 100644 --- a/utf8.c +++ b/utf8.c @@ -216,7 +216,11 @@ utf8_width(struct utf8_data *ud, int *width) { wchar_t wc; +#ifdef HAVE_UTF8PROC + switch (utf8proc_mbtowc(&wc, ud->data, ud->size)) { +#else switch (mbtowc(&wc, ud->data, ud->size)) { +#endif case -1: log_debug("UTF-8 %.*s, mbtowc() %d", (int)ud->size, ud->data, errno); @@ -225,7 +229,11 @@ utf8_width(struct utf8_data *ud, int *width) case 0: return (UTF8_ERROR); } +#ifdef HAVE_UTF8PROC + *width = utf8proc_wcwidth(wc); +#else *width = wcwidth(wc); +#endif if (*width >= 0 && *width <= 0xff) return (UTF8_DONE); log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, *width); From e40831a0023705885643284b574913e08a59f496 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Feb 2021 08:28:45 +0000 Subject: [PATCH 0696/1006] Move file handling protocol stuff all into file.c so it can be reused more easily. --- client.c | 273 ++---------------------------- file.c | 434 +++++++++++++++++++++++++++++++++++++++++++++--- server-client.c | 77 +-------- tmux.h | 17 +- 4 files changed, 445 insertions(+), 356 deletions(-) diff --git a/client.c b/client.c index a3e300cd..e164e930 100644 --- a/client.c +++ b/client.c @@ -477,257 +477,6 @@ client_send_identify(const char *ttynam, const char *cwd, int feat) proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); } -/* File write error callback. */ -static void -client_write_error_callback(__unused struct bufferevent *bev, - __unused short what, void *arg) -{ - struct client_file *cf = arg; - - log_debug("write error file %d", cf->stream); - - bufferevent_free(cf->event); - cf->event = NULL; - - close(cf->fd); - cf->fd = -1; - - if (client_exitflag) - client_exit(); -} - -/* File write callback. */ -static void -client_write_callback(__unused struct bufferevent *bev, void *arg) -{ - struct client_file *cf = arg; - - if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) { - bufferevent_free(cf->event); - close(cf->fd); - RB_REMOVE(client_files, &client_files, cf); - file_free(cf); - } - - if (client_exitflag) - client_exit(); -} - -/* Open write file. */ -static void -client_write_open(void *data, size_t datalen) -{ - struct msg_write_open *msg = data; - const char *path; - struct msg_write_ready reply; - struct client_file find, *cf; - const int flags = O_NONBLOCK|O_WRONLY|O_CREAT; - int error = 0; - - if (datalen < sizeof *msg) - fatalx("bad MSG_WRITE_OPEN size"); - if (datalen == sizeof *msg) - path = "-"; - else - path = (const char *)(msg + 1); - log_debug("open write file %d %s", msg->stream, path); - - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) { - cf = file_create(NULL, msg->stream, NULL, NULL); - RB_INSERT(client_files, &client_files, cf); - } else { - error = EBADF; - goto reply; - } - if (cf->closed) { - error = EBADF; - goto reply; - } - - cf->fd = -1; - if (msg->fd == -1) - cf->fd = open(path, msg->flags|flags, 0644); - else { - if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO) - errno = EBADF; - else { - cf->fd = dup(msg->fd); - if (~client_flags & CLIENT_CONTROL) - close(msg->fd); /* can only be used once */ - } - } - if (cf->fd == -1) { - error = errno; - goto reply; - } - - cf->event = bufferevent_new(cf->fd, NULL, client_write_callback, - client_write_error_callback, cf); - bufferevent_enable(cf->event, EV_WRITE); - goto reply; - -reply: - reply.stream = msg->stream; - reply.error = error; - proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply); -} - -/* Write to client file. */ -static void -client_write_data(void *data, size_t datalen) -{ - struct msg_write_data *msg = data; - struct client_file find, *cf; - size_t size = datalen - sizeof *msg; - - if (datalen < sizeof *msg) - fatalx("bad MSG_WRITE size"); - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) - fatalx("unknown stream number"); - log_debug("write %zu to file %d", size, cf->stream); - - if (cf->event != NULL) - bufferevent_write(cf->event, msg + 1, size); -} - -/* Close client file. */ -static void -client_write_close(void *data, size_t datalen) -{ - struct msg_write_close *msg = data; - struct client_file find, *cf; - - if (datalen != sizeof *msg) - fatalx("bad MSG_WRITE_CLOSE size"); - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) - fatalx("unknown stream number"); - log_debug("close file %d", cf->stream); - - if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) { - if (cf->event != NULL) - bufferevent_free(cf->event); - if (cf->fd != -1) - close(cf->fd); - RB_REMOVE(client_files, &client_files, cf); - file_free(cf); - } -} - -/* File read callback. */ -static void -client_read_callback(__unused struct bufferevent *bev, void *arg) -{ - struct client_file *cf = arg; - void *bdata; - size_t bsize; - struct msg_read_data *msg; - size_t msglen; - - msg = xmalloc(sizeof *msg); - for (;;) { - bdata = EVBUFFER_DATA(cf->event->input); - bsize = EVBUFFER_LENGTH(cf->event->input); - - if (bsize == 0) - break; - if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) - bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; - log_debug("read %zu from file %d", bsize, cf->stream); - - msglen = (sizeof *msg) + bsize; - msg = xrealloc(msg, msglen); - msg->stream = cf->stream; - memcpy(msg + 1, bdata, bsize); - proc_send(client_peer, MSG_READ, -1, msg, msglen); - - evbuffer_drain(cf->event->input, bsize); - } - free(msg); -} - -/* File read error callback. */ -static void -client_read_error_callback(__unused struct bufferevent *bev, - __unused short what, void *arg) -{ - struct client_file *cf = arg; - struct msg_read_done msg; - - log_debug("read error file %d", cf->stream); - - msg.stream = cf->stream; - msg.error = 0; - proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg); - - bufferevent_free(cf->event); - close(cf->fd); - RB_REMOVE(client_files, &client_files, cf); - file_free(cf); -} - -/* Open read file. */ -static void -client_read_open(void *data, size_t datalen) -{ - struct msg_read_open *msg = data; - const char *path; - struct msg_read_done reply; - struct client_file find, *cf; - const int flags = O_NONBLOCK|O_RDONLY; - int error; - - if (datalen < sizeof *msg) - fatalx("bad MSG_READ_OPEN size"); - if (datalen == sizeof *msg) - path = "-"; - else - path = (const char *)(msg + 1); - log_debug("open read file %d %s", msg->stream, path); - - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) { - cf = file_create(NULL, msg->stream, NULL, NULL); - RB_INSERT(client_files, &client_files, cf); - } else { - error = EBADF; - goto reply; - } - if (cf->closed) { - error = EBADF; - goto reply; - } - - cf->fd = -1; - if (msg->fd == -1) - cf->fd = open(path, flags); - else { - if (msg->fd != STDIN_FILENO) - errno = EBADF; - else { - cf->fd = dup(msg->fd); - if (~client_flags & CLIENT_CONTROL) - close(msg->fd); /* can only be used once */ - } - } - if (cf->fd == -1) { - error = errno; - goto reply; - } - - cf->event = bufferevent_new(cf->fd, client_read_callback, NULL, - client_read_error_callback, cf); - bufferevent_enable(cf->event, EV_READ); - return; - -reply: - reply.stream = msg->stream; - reply.error = error; - proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply); -} - /* Run command in shell; used for -c. */ static __dead void client_exec(const char *shell, const char *shellcmd) @@ -802,6 +551,16 @@ client_signal(int sig) } } +/* Callback for file write error or close. */ +static void +client_file_check_cb(__unused struct client *c, __unused const char *path, + __unused int error, __unused int closed, __unused struct evbuffer *buffer, + __unused void *data) +{ + if (client_exitflag) + client_exit(); +} + /* Callback for client read events. */ static void client_dispatch(struct imsg *imsg, __unused void *arg) @@ -916,16 +675,20 @@ client_dispatch_wait(struct imsg *imsg) proc_exit(client_proc); break; case MSG_READ_OPEN: - client_read_open(data, datalen); + file_read_open(&client_files, client_peer, imsg, 1, + !(client_flags & CLIENT_CONTROL), client_file_check_cb, + NULL); break; case MSG_WRITE_OPEN: - client_write_open(data, datalen); + file_write_open(&client_files, client_peer, imsg, 1, + !(client_flags & CLIENT_CONTROL), client_file_check_cb, + NULL); break; case MSG_WRITE: - client_write_data(data, datalen); + file_write_data(&client_files, imsg); break; case MSG_WRITE_CLOSE: - client_write_close(data, datalen); + file_write_close(&client_files, imsg); break; case MSG_OLDSTDERR: case MSG_OLDSTDIN: diff --git a/file.c b/file.c index 0d25db03..495568fa 100644 --- a/file.c +++ b/file.c @@ -30,10 +30,17 @@ #include "tmux.h" +/* + * IPC file handling. Both client and server use the same data structures + * (client_file and client_files) to store list of active files. Most functions + * are for use either in client or server but not both. + */ + static int file_next_stream = 3; RB_GENERATE(client_files, client_file, entry, file_cmp); +/* Get path for file, either as given or from working directory. */ static char * file_get_path(struct client *c, const char *file) { @@ -46,6 +53,7 @@ file_get_path(struct client *c, const char *file) return (path); } +/* Tree comparison function. */ int file_cmp(struct client_file *cf1, struct client_file *cf2) { @@ -56,11 +64,47 @@ file_cmp(struct client_file *cf1, struct client_file *cf2) return (0); } +/* + * Create a file object in the client process - the peer is the server to send + * messages to. Check callback is fired when the file is finished with so the + * process can decide if it needs to exit (if it is waiting for files to + * flush). + */ struct client_file * -file_create(struct client *c, int stream, client_file_cb cb, void *cbdata) +file_create_with_peer(struct tmuxpeer *peer, struct client_files *files, + int stream, client_file_cb cb, void *cbdata) { struct client_file *cf; + cf = xcalloc(1, sizeof *cf); + cf->c = NULL; + cf->references = 1; + cf->stream = stream; + + cf->buffer = evbuffer_new(); + if (cf->buffer == NULL) + fatalx("out of memory"); + + cf->cb = cb; + cf->data = cbdata; + + cf->peer = peer; + cf->tree = files; + RB_INSERT(client_files, files, cf); + + return (cf); +} + +/* Create a file object in the server, communicating with the given client. */ +struct client_file * +file_create_with_client(struct client *c, int stream, client_file_cb cb, + void *cbdata) +{ + struct client_file *cf; + + if (c != NULL && (c->flags & CLIENT_ATTACHED)) + c = NULL; + cf = xcalloc(1, sizeof *cf); cf->c = c; cf->references = 1; @@ -74,6 +118,8 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata) cf->data = cbdata; if (cf->c != NULL) { + cf->peer = cf->c->peer; + cf->tree = &cf->c->files; RB_INSERT(client_files, &cf->c->files, cf); cf->c->references++; } @@ -81,6 +127,7 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata) return (cf); } +/* Free a file. */ void file_free(struct client_file *cf) { @@ -90,13 +137,15 @@ file_free(struct client_file *cf) evbuffer_free(cf->buffer); free(cf->path); - if (cf->c != NULL) { - RB_REMOVE(client_files, &cf->c->files, cf); + if (cf->tree != NULL) + RB_REMOVE(client_files, cf->tree, cf); + if (cf->c != NULL) server_client_unref(cf->c); - } + free(cf); } +/* Event to fire the done callback. */ static void file_fire_done_cb(__unused int fd, __unused short events, void *arg) { @@ -108,21 +157,22 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg) file_free(cf); } +/* Add an event to fire the done callback (used by the server). */ void file_fire_done(struct client_file *cf) { event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL); } +/* Fire the read callback. */ void file_fire_read(struct client_file *cf) { - struct client *c = cf->c; - if (cf->cb != NULL) - cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data); + cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data); } +/* Can this file be printed to? */ int file_can_print(struct client *c) { @@ -133,6 +183,7 @@ file_can_print(struct client *c) return (1); } +/* Print a message to a file. */ void file_print(struct client *c, const char *fmt, ...) { @@ -143,6 +194,7 @@ file_print(struct client *c, const char *fmt, ...) va_end(ap); } +/* Print a message to a file. */ void file_vprint(struct client *c, const char *fmt, va_list ap) { @@ -154,7 +206,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap) find.stream = 1; if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { - cf = file_create(c, 1, NULL, NULL); + cf = file_create_with_client(c, 1, NULL, NULL); cf->path = xstrdup("-"); evbuffer_add_vprintf(cf->buffer, fmt, ap); @@ -169,6 +221,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap) } } +/* Print a buffer to a file. */ void file_print_buffer(struct client *c, void *data, size_t size) { @@ -180,7 +233,7 @@ file_print_buffer(struct client *c, void *data, size_t size) find.stream = 1; if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { - cf = file_create(c, 1, NULL, NULL); + cf = file_create_with_client(c, 1, NULL, NULL); cf->path = xstrdup("-"); evbuffer_add(cf->buffer, data, size); @@ -195,6 +248,7 @@ file_print_buffer(struct client *c, void *data, size_t size) } } +/* Report an error to a file. */ void file_error(struct client *c, const char *fmt, ...) { @@ -209,7 +263,7 @@ file_error(struct client *c, const char *fmt, ...) find.stream = 2; if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { - cf = file_create(c, 2, NULL, NULL); + cf = file_create_with_client(c, 2, NULL, NULL); cf->path = xstrdup("-"); evbuffer_add_vprintf(cf->buffer, fmt, ap); @@ -226,19 +280,21 @@ file_error(struct client *c, const char *fmt, ...) va_end(ap); } +/* Write data to a file. */ void file_write(struct client *c, const char *path, int flags, const void *bdata, size_t bsize, client_file_cb cb, void *cbdata) { struct client_file *cf; - FILE *f; struct msg_write_open *msg; size_t msglen; int fd = -1; + u_int stream = file_next_stream++; + FILE *f; const char *mode; if (strcmp(path, "-") == 0) { - cf = file_create(c, file_next_stream++, cb, cbdata); + cf = file_create_with_client(c, stream, cb, cbdata); cf->path = xstrdup("-"); fd = STDOUT_FILENO; @@ -251,7 +307,7 @@ file_write(struct client *c, const char *path, int flags, const void *bdata, goto skip; } - cf = file_create(c, file_next_stream++, cb, cbdata); + cf = file_create_with_client(c, stream, cb, cbdata); cf->path = file_get_path(c, path); if (c == NULL || c->flags & CLIENT_ATTACHED) { @@ -286,7 +342,7 @@ skip: msg->fd = fd; msg->flags = flags; memcpy(msg + 1, cf->path, msglen - sizeof *msg); - if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) { + if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) { free(msg); cf->error = EINVAL; goto done; @@ -298,18 +354,21 @@ done: file_fire_done(cf); } +/* Read a file. */ void file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) { struct client_file *cf; - FILE *f; struct msg_read_open *msg; - size_t msglen, size; + size_t msglen; int fd = -1; + u_int stream = file_next_stream++; + FILE *f; + size_t size; char buffer[BUFSIZ]; if (strcmp(path, "-") == 0) { - cf = file_create(c, file_next_stream++, cb, cbdata); + cf = file_create_with_client(c, stream, cb, cbdata); cf->path = xstrdup("-"); fd = STDIN_FILENO; @@ -322,7 +381,7 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) goto skip; } - cf = file_create(c, file_next_stream++, cb, cbdata); + cf = file_create_with_client(c, stream, cb, cbdata); cf->path = file_get_path(c, path); if (c == NULL || c->flags & CLIENT_ATTACHED) { @@ -358,7 +417,7 @@ skip: msg->stream = cf->stream; msg->fd = fd; memcpy(msg + 1, cf->path, msglen - sizeof *msg); - if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) { + if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) { free(msg); cf->error = EINVAL; goto done; @@ -370,21 +429,21 @@ done: file_fire_done(cf); } +/* Push event, fired if there is more writing to be done. */ static void file_push_cb(__unused int fd, __unused short events, void *arg) { struct client_file *cf = arg; - struct client *c = cf->c; - if (~c->flags & CLIENT_DEAD) + if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD) file_push(cf); file_free(cf); } +/* Push uwritten data to the client for a file, if it will accept it. */ void file_push(struct client_file *cf) { - struct client *c = cf->c; struct msg_write_data *msg; size_t msglen, sent, left; struct msg_write_close close; @@ -400,21 +459,344 @@ file_push(struct client_file *cf) msg = xrealloc(msg, msglen); msg->stream = cf->stream; memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent); - if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0) + if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0) break; evbuffer_drain(cf->buffer, sent); left = EVBUFFER_LENGTH(cf->buffer); - log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream, - sent, left); + log_debug("file %d sent %zu, left %zu", cf->stream, sent, left); } if (left != 0) { cf->references++; event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL); } else if (cf->stream > 2) { close.stream = cf->stream; - proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close); + proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close); file_fire_done(cf); } free(msg); } + +/* Client file write error callback. */ +static void +file_write_error_callback(__unused struct bufferevent *bev, __unused short what, + void *arg) +{ + struct client_file *cf = arg; + + log_debug("write error file %d", cf->stream); + + if (cf->cb != NULL) + cf->cb(NULL, NULL, 0, -1, NULL, cf->data); + + bufferevent_free(cf->event); + cf->event = NULL; + + close(cf->fd); + cf->fd = -1; +} + +/* Client file write callback. */ +static void +file_write_callback(__unused struct bufferevent *bev, void *arg) +{ + struct client_file *cf = arg; + + log_debug("write check file %d", cf->stream); + + if (cf->cb != NULL) + cf->cb(NULL, NULL, 0, -1, NULL, cf->data); + + if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) { + bufferevent_free(cf->event); + close(cf->fd); + RB_REMOVE(client_files, cf->tree, cf); + file_free(cf); + } +} + +/* Handle a file write open message (client). */ +void +file_write_open(struct client_files *files, struct tmuxpeer *peer, + struct imsg *imsg, int allow_streams, int close_received, + client_file_cb cb, void *cbdata) +{ + struct msg_write_open *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + const char *path; + struct msg_write_ready reply; + struct client_file find, *cf; + const int flags = O_NONBLOCK|O_WRONLY|O_CREAT; + int error = 0; + + if (msglen < sizeof *msg) + fatalx("bad MSG_WRITE_OPEN size"); + if (msglen == sizeof *msg) + path = "-"; + else + path = (const char *)(msg + 1); + log_debug("open write file %d %s", msg->stream, path); + + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, files, &find)) != NULL) { + error = EBADF; + goto reply; + } + cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata); + if (cf->closed) { + error = EBADF; + goto reply; + } + + cf->fd = -1; + if (msg->fd == -1) + cf->fd = open(path, msg->flags|flags, 0644); + else if (allow_streams) { + if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO) + errno = EBADF; + else { + cf->fd = dup(msg->fd); + if (close_received) + close(msg->fd); /* can only be used once */ + } + } else + errno = EBADF; + if (cf->fd == -1) { + error = errno; + goto reply; + } + + cf->event = bufferevent_new(cf->fd, NULL, file_write_callback, + file_write_error_callback, cf); + bufferevent_enable(cf->event, EV_WRITE); + goto reply; + +reply: + reply.stream = msg->stream; + reply.error = error; + proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply); +} + +/* Handle a file write data message (client). */ +void +file_write_data(struct client_files *files, struct imsg *imsg) +{ + struct msg_write_data *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + struct client_file find, *cf; + size_t size = msglen - sizeof *msg; + + if (msglen < sizeof *msg) + fatalx("bad MSG_WRITE size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, files, &find)) == NULL) + fatalx("unknown stream number"); + log_debug("write %zu to file %d", size, cf->stream); + + if (cf->event != NULL) + bufferevent_write(cf->event, msg + 1, size); +} + +/* Handle a file write close message (client). */ +void +file_write_close(struct client_files *files, struct imsg *imsg) +{ + struct msg_write_close *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + struct client_file find, *cf; + + if (msglen != sizeof *msg) + fatalx("bad MSG_WRITE_CLOSE size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, files, &find)) == NULL) + fatalx("unknown stream number"); + log_debug("close file %d", cf->stream); + + if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) { + if (cf->event != NULL) + bufferevent_free(cf->event); + if (cf->fd != -1) + close(cf->fd); + RB_REMOVE(client_files, files, cf); + file_free(cf); + } +} + +/* Client file read error callback. */ +static void +file_read_error_callback(__unused struct bufferevent *bev, __unused short what, + void *arg) +{ + struct client_file *cf = arg; + struct msg_read_done msg; + + log_debug("read error file %d", cf->stream); + + msg.stream = cf->stream; + msg.error = 0; + proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg); + + bufferevent_free(cf->event); + close(cf->fd); + RB_REMOVE(client_files, cf->tree, cf); + file_free(cf); +} + +/* Client file read callback. */ +static void +file_read_callback(__unused struct bufferevent *bev, void *arg) +{ + struct client_file *cf = arg; + void *bdata; + size_t bsize; + struct msg_read_data *msg; + size_t msglen; + + msg = xmalloc(sizeof *msg); + for (;;) { + bdata = EVBUFFER_DATA(cf->event->input); + bsize = EVBUFFER_LENGTH(cf->event->input); + + if (bsize == 0) + break; + if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) + bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; + log_debug("read %zu from file %d", bsize, cf->stream); + + msglen = (sizeof *msg) + bsize; + msg = xrealloc(msg, msglen); + msg->stream = cf->stream; + memcpy(msg + 1, bdata, bsize); + proc_send(cf->peer, MSG_READ, -1, msg, msglen); + + evbuffer_drain(cf->event->input, bsize); + } + free(msg); +} + +/* Handle a file read open message (client). */ +void +file_read_open(struct client_files *files, struct tmuxpeer *peer, + struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb, + void *cbdata) +{ + struct msg_read_open *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + const char *path; + struct msg_read_done reply; + struct client_file find, *cf; + const int flags = O_NONBLOCK|O_RDONLY; + int error; + + if (msglen < sizeof *msg) + fatalx("bad MSG_READ_OPEN size"); + if (msglen == sizeof *msg) + path = "-"; + else + path = (const char *)(msg + 1); + log_debug("open read file %d %s", msg->stream, path); + + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, files, &find)) != NULL) { + error = EBADF; + goto reply; + } + cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata); + if (cf->closed) { + error = EBADF; + goto reply; + } + + cf->fd = -1; + if (msg->fd == -1) + cf->fd = open(path, flags); + else if (allow_streams) { + if (msg->fd != STDIN_FILENO) + errno = EBADF; + else { + cf->fd = dup(msg->fd); + if (close_received) + close(msg->fd); /* can only be used once */ + } + } else + errno = EBADF; + if (cf->fd == -1) { + error = errno; + goto reply; + } + + cf->event = bufferevent_new(cf->fd, file_read_callback, NULL, + file_read_error_callback, cf); + bufferevent_enable(cf->event, EV_READ); + return; + +reply: + reply.stream = msg->stream; + reply.error = error; + proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply); +} + +/* Handle a write ready message (server). */ +void +file_write_ready(struct client_files *files, struct imsg *imsg) +{ + struct msg_write_ready *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + struct client_file find, *cf; + + if (msglen != sizeof *msg) + fatalx("bad MSG_WRITE_READY size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, files, &find)) == NULL) + return; + if (msg->error != 0) { + cf->error = msg->error; + file_fire_done(cf); + } else + file_push(cf); +} + +/* Handle read data message (server). */ +void +file_read_data(struct client_files *files, struct imsg *imsg) +{ + struct msg_read_data *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + struct client_file find, *cf; + void *bdata = msg + 1; + size_t bsize = msglen - sizeof *msg; + + if (msglen < sizeof *msg) + fatalx("bad MSG_READ_DATA size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, files, &find)) == NULL) + return; + + log_debug("file %d read %zu bytes", cf->stream, bsize); + if (cf->error == 0) { + if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { + cf->error = ENOMEM; + file_fire_done(cf); + } else + file_fire_read(cf); + } +} + +/* Handle a read done message (server). */ +void +file_read_done(struct client_files *files, struct imsg *imsg) +{ + struct msg_read_done *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + struct client_file find, *cf; + + if (msglen != sizeof *msg) + fatalx("bad MSG_READ_DONE size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, files, &find)) == NULL) + return; + + log_debug("file %d read done", cf->stream); + cf->error = msg->error; + file_fire_done(cf); +} diff --git a/server-client.c b/server-client.c index 52df8110..7d9cc0e6 100644 --- a/server-client.c +++ b/server-client.c @@ -51,12 +51,6 @@ static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch_command(struct client *, struct imsg *); static void server_client_dispatch_identify(struct client *, struct imsg *); static void server_client_dispatch_shell(struct client *); -static void server_client_dispatch_write_ready(struct client *, - struct imsg *); -static void server_client_dispatch_read_data(struct client *, - struct imsg *); -static void server_client_dispatch_read_done(struct client *, - struct imsg *); /* Compare client windows. */ static int @@ -2070,13 +2064,13 @@ server_client_dispatch(struct imsg *imsg, void *arg) server_client_dispatch_shell(c); break; case MSG_WRITE_READY: - server_client_dispatch_write_ready(c, imsg); + file_write_ready(&c->files, imsg); break; case MSG_READ: - server_client_dispatch_read_data(c, imsg); + file_read_data(&c->files, imsg); break; case MSG_READ_DONE: - server_client_dispatch_read_done(c, imsg); + file_read_done(&c->files, imsg); break; } } @@ -2302,71 +2296,6 @@ server_client_dispatch_shell(struct client *c) proc_kill_peer(c->peer); } -/* Handle write ready message. */ -static void -server_client_dispatch_write_ready(struct client *c, struct imsg *imsg) -{ - struct msg_write_ready *msg = imsg->data; - size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; - struct client_file find, *cf; - - if (msglen != sizeof *msg) - fatalx("bad MSG_WRITE_READY size"); - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) - return; - if (msg->error != 0) { - cf->error = msg->error; - file_fire_done(cf); - } else - file_push(cf); -} - -/* Handle read data message. */ -static void -server_client_dispatch_read_data(struct client *c, struct imsg *imsg) -{ - struct msg_read_data *msg = imsg->data; - size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; - struct client_file find, *cf; - void *bdata = msg + 1; - size_t bsize = msglen - sizeof *msg; - - if (msglen < sizeof *msg) - fatalx("bad MSG_READ_DATA size"); - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) - return; - - log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize); - if (cf->error == 0) { - if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { - cf->error = ENOMEM; - file_fire_done(cf); - } else - file_fire_read(cf); - } -} - -/* Handle read done message. */ -static void -server_client_dispatch_read_done(struct client *c, struct imsg *imsg) -{ - struct msg_read_done *msg = imsg->data; - size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; - struct client_file find, *cf; - - if (msglen != sizeof *msg) - fatalx("bad MSG_READ_DONE size"); - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) - return; - - log_debug("%s: file %d read done", c->name, cf->stream); - cf->error = msg->error; - file_fire_done(cf); -} - /* Get client working directory. */ const char * server_client_get_cwd(struct client *c, struct session *s) diff --git a/tmux.h b/tmux.h index 1cca8c8f..985b8858 100644 --- a/tmux.h +++ b/tmux.h @@ -1539,6 +1539,8 @@ typedef void (*client_file_cb) (struct client *, const char *, int, int, struct evbuffer *, void *); struct client_file { struct client *c; + struct tmuxpeer *peer; + struct client_files *tree; int references; int stream; @@ -1907,6 +1909,7 @@ int load_cfg(const char *, struct client *, struct cmdq_item *, int, int load_cfg_from_buffer(const void *, size_t, const char *, struct client *, struct cmdq_item *, int, struct cmdq_item **); void set_cfg_file(const char *); +const char *get_cfg_file(void); void printflike(1, 2) cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmdq_item *); void cfg_show_causes(struct session *); @@ -2370,7 +2373,10 @@ void alerts_check_session(struct session *); /* file.c */ int file_cmp(struct client_file *, struct client_file *); RB_PROTOTYPE(client_files, client_file, entry, file_cmp); -struct client_file *file_create(struct client *, int, client_file_cb, void *); +struct client_file *file_create_with_peer(struct tmuxpeer *, + struct client_files *, int, client_file_cb, void *); +struct client_file *file_create_with_client(struct client *, int, + client_file_cb, void *); void file_free(struct client_file *); void file_fire_done(struct client_file *); void file_fire_read(struct client_file *); @@ -2383,6 +2389,15 @@ void file_write(struct client *, const char *, int, const void *, size_t, client_file_cb, void *); void file_read(struct client *, const char *, client_file_cb, void *); void file_push(struct client_file *); +void file_write_open(struct client_files *, struct tmuxpeer *, + struct imsg *, int, int, client_file_cb, void *); +void file_write_data(struct client_files *, struct imsg *); +void file_write_close(struct client_files *, struct imsg *); +void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *, + int, int, client_file_cb, void *); +void file_write_ready(struct client_files *, struct imsg *); +void file_read_data(struct client_files *, struct imsg *); +void file_read_done(struct client_files *, struct imsg *); /* server.c */ extern struct tmuxproc *server_proc; From 79e1984962281d94b25ff14ac3de31bc63358ead Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Feb 2021 09:03:38 +0000 Subject: [PATCH 0697/1006] O_TRUNC is needed in case file exists. --- cmd-save-buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 5e45e279..7f161a91 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -109,7 +109,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'a')) flags = O_APPEND; else - flags = 0; + flags = O_TRUNC; file_write(cmdq_get_client(item), path, flags, bufdata, bufsize, cmd_save_buffer_done, item); free(path); From 2b58c226db055eff4bbb971fa00938b42690f4ac Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Feb 2021 09:39:29 +0000 Subject: [PATCH 0698/1006] Add a couple of helper functions, and flush imsgs on exit. --- client.c | 15 +-------------- file.c | 20 ++++++++++++++++++++ proc.c | 36 ++++++++++++++++++++++++++++++++++++ server.c | 29 ++++++++--------------------- tmux.h | 2 ++ 5 files changed, 67 insertions(+), 35 deletions(-) diff --git a/client.c b/client.c index e164e930..74dc4602 100644 --- a/client.c +++ b/client.c @@ -223,20 +223,7 @@ client_exit_message(void) static void client_exit(void) { - struct client_file *cf; - size_t left; - int waiting = 0; - - RB_FOREACH (cf, client_files, &client_files) { - if (cf->event == NULL) - continue; - left = EVBUFFER_LENGTH(cf->event->output); - if (left != 0) { - waiting++; - log_debug("file %u %zu bytes left", cf->stream, left); - } - } - if (waiting == 0) + if (!file_write_left(&client_files)) proc_exit(client_proc); } diff --git a/file.c b/file.c index 495568fa..8eb4e178 100644 --- a/file.c +++ b/file.c @@ -477,6 +477,26 @@ file_push(struct client_file *cf) free(msg); } +/* Check if any files have data left to write. */ +int +file_write_left(struct client_files *files) +{ + struct client_file *cf; + size_t left; + int waiting = 0; + + RB_FOREACH (cf, client_files, files) { + if (cf->event == NULL) + continue; + left = EVBUFFER_LENGTH(cf->event->output); + if (left != 0) { + waiting++; + log_debug("file %u %zu bytes left", cf->stream, left); + } + } + return (waiting != 0); +} + /* Client file write error callback. */ static void file_write_error_callback(__unused struct bufferevent *bev, __unused short what, diff --git a/proc.c b/proc.c index ff011565..9412cec0 100644 --- a/proc.c +++ b/proc.c @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -45,6 +46,8 @@ struct tmuxproc { struct event ev_sigusr1; struct event ev_sigusr2; struct event ev_sigwinch; + + TAILQ_HEAD(, tmuxpeer) peers; }; struct tmuxpeer { @@ -58,6 +61,8 @@ struct tmuxpeer { void (*dispatchcb)(struct imsg *, void *); void *arg; + + TAILQ_ENTRY(tmuxpeer) entry; }; static int peer_check_version(struct tmuxpeer *, struct imsg *); @@ -190,6 +195,7 @@ proc_start(const char *name) tp = xcalloc(1, sizeof *tp); tp->name = xstrdup(name); + TAILQ_INIT(&tp->peers); return (tp); } @@ -207,6 +213,10 @@ proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) void proc_exit(struct tmuxproc *tp) { + struct tmuxpeer *peer; + + TAILQ_FOREACH(peer, &tp->peers, entry) + imsg_flush(&peer->ibuf); tp->exit = 1; } @@ -297,6 +307,7 @@ proc_add_peer(struct tmuxproc *tp, int fd, event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); log_debug("add peer %p: %d (%p)", peer, fd, arg); + TAILQ_INSERT_TAIL(&tp->peers, peer, entry); proc_update_event(peer); return (peer); @@ -305,6 +316,7 @@ proc_add_peer(struct tmuxproc *tp, int fd, void proc_remove_peer(struct tmuxpeer *peer) { + TAILQ_REMOVE(&peer->parent->peers, peer, entry); log_debug("remove peer %p", peer); event_del(&peer->event); @@ -325,3 +337,27 @@ proc_toggle_log(struct tmuxproc *tp) { log_toggle(tp->name); } + +pid_t +proc_fork_and_daemon(int *fd) +{ + pid_t pid; + int pair[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) + fatal("socketpair failed"); + switch (pid = fork()) { + case -1: + fatal("fork failed"); + case 0: + close(pair[0]); + *fd = pair[1]; + if (daemon(1, 0) != 0) + fatal("daemon failed"); + return (0); + default: + close(pair[1]); + *fd = pair[0]; + return (pid); + } +} diff --git a/server.c b/server.c index b2c75e11..a286e779 100644 --- a/server.c +++ b/server.c @@ -156,35 +156,22 @@ int server_start(struct tmuxproc *client, int flags, struct event_base *base, int lockfd, char *lockfile) { - int pair[2]; - sigset_t set, oldset; - struct client *c = NULL; - char *cause = NULL; + int fd; + sigset_t set, oldset; + struct client *c = NULL; + char *cause = NULL; sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); if (~flags & CLIENT_NOFORK) { - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) - fatal("socketpair failed"); - - switch (fork()) { - case -1: - fatal("fork failed"); - case 0: - break; - default: + if (proc_fork_and_daemon(&fd) != 0) { sigprocmask(SIG_SETMASK, &oldset, NULL); - close(pair[1]); - return (pair[0]); + return (fd); } - close(pair[0]); - if (daemon(1, 0) != 0) - fatal("daemon failed"); } - - server_client_flags = flags; proc_clear_signals(client, 0); + server_client_flags = flags; if (event_reinit(base) != 0) fatalx("event_reinit failed"); @@ -213,7 +200,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, if (server_fd != -1) server_update_socket(); if (~flags & CLIENT_NOFORK) - c = server_client_create(pair[1]); + c = server_client_create(fd); else options_set_number(global_options, "exit-empty", 0); diff --git a/tmux.h b/tmux.h index 985b8858..8f83c218 100644 --- a/tmux.h +++ b/tmux.h @@ -1899,6 +1899,7 @@ struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, void proc_remove_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *); void proc_toggle_log(struct tmuxproc *); +pid_t proc_fork_and_daemon(int *); /* cfg.c */ extern int cfg_finished; @@ -2389,6 +2390,7 @@ void file_write(struct client *, const char *, int, const void *, size_t, client_file_cb, void *); void file_read(struct client *, const char *, client_file_cb, void *); void file_push(struct client_file *); +int file_write_left(struct client_files *); void file_write_open(struct client_files *, struct tmuxpeer *, struct imsg *, int, int, client_file_cb, void *); void file_write_data(struct client_files *, struct imsg *); From 632636dba535468d8266ad44c099f1217f1e3ea5 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 12 Feb 2021 06:52:48 +0000 Subject: [PATCH 0699/1006] Do not care about the server socket closing if exiting anyway. --- client.c | 6 ++++-- file.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client.c b/client.c index 74dc4602..5a454ffe 100644 --- a/client.c +++ b/client.c @@ -553,8 +553,10 @@ static void client_dispatch(struct imsg *imsg, __unused void *arg) { if (imsg == NULL) { - client_exitreason = CLIENT_EXIT_LOST_SERVER; - client_exitval = 1; + if (!client_exitflag) { + client_exitreason = CLIENT_EXIT_LOST_SERVER; + client_exitval = 1; + } proc_exit(client_proc); return; } diff --git a/file.c b/file.c index 8eb4e178..8f497b38 100644 --- a/file.c +++ b/file.c @@ -485,7 +485,7 @@ file_write_left(struct client_files *files) size_t left; int waiting = 0; - RB_FOREACH (cf, client_files, files) { + RB_FOREACH(cf, client_files, files) { if (cf->event == NULL) continue; left = EVBUFFER_LENGTH(cf->event->output); From 6642706f7baab2d7501ca8ac16659ddab1d729ca Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Feb 2021 09:39:37 +0000 Subject: [PATCH 0700/1006] Support X11 colour names and some other variations for OSC 10/11, also add OSC 110 and 111. GitHub issue 2567. --- colour.c | 611 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- format.c | 24 +++ input.c | 135 +++++++----- tmux.1 | 4 +- tmux.h | 1 + 5 files changed, 721 insertions(+), 54 deletions(-) diff --git a/colour.c b/colour.c index ee4b95db..1d1729ca 100644 --- a/colour.c +++ b/colour.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "tmux.h" @@ -111,6 +112,9 @@ colour_tostring(int c) static char s[32]; u_char r, g, b; + if (c == -1) + return ("invalid"); + if (c & COLOUR_FLAG_RGB) { colour_split_rgb(c, &r, &g, &b); xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b); @@ -233,7 +237,7 @@ colour_fromstring(const char *s) return (96); if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0) return (97); - return (-1); + return (colour_byname(s)); } /* Convert 256 colour to RGB colour. */ @@ -335,3 +339,608 @@ colour_256to16(int c) return (table[c & 0xff]); } + +/* Get colour by X11 colour name. */ +int +colour_byname(const char *name) +{ + static const struct { + const char *name; + int c; + } colours[] = { + { "AliceBlue", 0xf0f8ff }, + { "AntiqueWhite", 0xfaebd7 }, + { "AntiqueWhite1", 0xffefdb }, + { "AntiqueWhite2", 0xeedfcc }, + { "AntiqueWhite3", 0xcdc0b0 }, + { "AntiqueWhite4", 0x8b8378 }, + { "BlanchedAlmond", 0xffebcd }, + { "BlueViolet", 0x8a2be2 }, + { "CadetBlue", 0x5f9ea0 }, + { "CadetBlue1", 0x98f5ff }, + { "CadetBlue2", 0x8ee5ee }, + { "CadetBlue3", 0x7ac5cd }, + { "CadetBlue4", 0x53868b }, + { "CornflowerBlue", 0x6495ed }, + { "DarkBlue", 0x00008b }, + { "DarkCyan", 0x008b8b }, + { "DarkGoldenrod", 0xb8860b }, + { "DarkGoldenrod1", 0xffb90f }, + { "DarkGoldenrod2", 0xeead0e }, + { "DarkGoldenrod3", 0xcd950c }, + { "DarkGoldenrod4", 0x8b6508 }, + { "DarkGray", 0xa9a9a9 }, + { "DarkGreen", 0x006400 }, + { "DarkGrey", 0xa9a9a9 }, + { "DarkKhaki", 0xbdb76b }, + { "DarkMagenta", 0x8b008b }, + { "DarkOliveGreen", 0x556b2f }, + { "DarkOliveGreen1", 0xcaff70 }, + { "DarkOliveGreen2", 0xbcee68 }, + { "DarkOliveGreen3", 0xa2cd5a }, + { "DarkOliveGreen4", 0x6e8b3d }, + { "DarkOrange", 0xff8c00 }, + { "DarkOrange1", 0xff7f00 }, + { "DarkOrange2", 0xee7600 }, + { "DarkOrange3", 0xcd6600 }, + { "DarkOrange4", 0x8b4500 }, + { "DarkOrchid", 0x9932cc }, + { "DarkOrchid1", 0xbf3eff }, + { "DarkOrchid2", 0xb23aee }, + { "DarkOrchid3", 0x9a32cd }, + { "DarkOrchid4", 0x68228b }, + { "DarkRed", 0x8b0000 }, + { "DarkSalmon", 0xe9967a }, + { "DarkSeaGreen", 0x8fbc8f }, + { "DarkSeaGreen1", 0xc1ffc1 }, + { "DarkSeaGreen2", 0xb4eeb4 }, + { "DarkSeaGreen3", 0x9bcd9b }, + { "DarkSeaGreen4", 0x698b69 }, + { "DarkSlateBlue", 0x483d8b }, + { "DarkSlateGray", 0x2f4f4f }, + { "DarkSlateGray1", 0x97ffff }, + { "DarkSlateGray2", 0x8deeee }, + { "DarkSlateGray3", 0x79cdcd }, + { "DarkSlateGray4", 0x528b8b }, + { "DarkSlateGrey", 0x2f4f4f }, + { "DarkTurquoise", 0x00ced1 }, + { "DarkViolet", 0x9400d3 }, + { "DeepPink", 0xff1493 }, + { "DeepPink1", 0xff1493 }, + { "DeepPink2", 0xee1289 }, + { "DeepPink3", 0xcd1076 }, + { "DeepPink4", 0x8b0a50 }, + { "DeepSkyBlue", 0x00bfff }, + { "DeepSkyBlue1", 0x00bfff }, + { "DeepSkyBlue2", 0x00b2ee }, + { "DeepSkyBlue3", 0x009acd }, + { "DeepSkyBlue4", 0x00688b }, + { "DimGray", 0x696969 }, + { "DimGrey", 0x696969 }, + { "DodgerBlue", 0x1e90ff }, + { "DodgerBlue1", 0x1e90ff }, + { "DodgerBlue2", 0x1c86ee }, + { "DodgerBlue3", 0x1874cd }, + { "DodgerBlue4", 0x104e8b }, + { "FloralWhite", 0xfffaf0 }, + { "ForestGreen", 0x228b22 }, + { "GhostWhite", 0xf8f8ff }, + { "GreenYellow", 0xadff2f }, + { "HotPink", 0xff69b4 }, + { "HotPink1", 0xff6eb4 }, + { "HotPink2", 0xee6aa7 }, + { "HotPink3", 0xcd6090 }, + { "HotPink4", 0x8b3a62 }, + { "IndianRed", 0xcd5c5c }, + { "IndianRed1", 0xff6a6a }, + { "IndianRed2", 0xee6363 }, + { "IndianRed3", 0xcd5555 }, + { "IndianRed4", 0x8b3a3a }, + { "LavenderBlush", 0xfff0f5 }, + { "LavenderBlush1", 0xfff0f5 }, + { "LavenderBlush2", 0xeee0e5 }, + { "LavenderBlush3", 0xcdc1c5 }, + { "LavenderBlush4", 0x8b8386 }, + { "LawnGreen", 0x7cfc00 }, + { "LemonChiffon", 0xfffacd }, + { "LemonChiffon1", 0xfffacd }, + { "LemonChiffon2", 0xeee9bf }, + { "LemonChiffon3", 0xcdc9a5 }, + { "LemonChiffon4", 0x8b8970 }, + { "LightBlue", 0xadd8e6 }, + { "LightBlue1", 0xbfefff }, + { "LightBlue2", 0xb2dfee }, + { "LightBlue3", 0x9ac0cd }, + { "LightBlue4", 0x68838b }, + { "LightCoral", 0xf08080 }, + { "LightCyan", 0xe0ffff }, + { "LightCyan1", 0xe0ffff }, + { "LightCyan2", 0xd1eeee }, + { "LightCyan3", 0xb4cdcd }, + { "LightCyan4", 0x7a8b8b }, + { "LightGoldenrod", 0xeedd82 }, + { "LightGoldenrod1", 0xffec8b }, + { "LightGoldenrod2", 0xeedc82 }, + { "LightGoldenrod3", 0xcdbe70 }, + { "LightGoldenrod4", 0x8b814c }, + { "LightGoldenrodYellow", 0xfafad2 }, + { "LightGray", 0xd3d3d3 }, + { "LightGreen", 0x90ee90 }, + { "LightGrey", 0xd3d3d3 }, + { "LightPink", 0xffb6c1 }, + { "LightPink1", 0xffaeb9 }, + { "LightPink2", 0xeea2ad }, + { "LightPink3", 0xcd8c95 }, + { "LightPink4", 0x8b5f65 }, + { "LightSalmon", 0xffa07a }, + { "LightSalmon1", 0xffa07a }, + { "LightSalmon2", 0xee9572 }, + { "LightSalmon3", 0xcd8162 }, + { "LightSalmon4", 0x8b5742 }, + { "LightSeaGreen", 0x20b2aa }, + { "LightSkyBlue", 0x87cefa }, + { "LightSkyBlue1", 0xb0e2ff }, + { "LightSkyBlue2", 0xa4d3ee }, + { "LightSkyBlue3", 0x8db6cd }, + { "LightSkyBlue4", 0x607b8b }, + { "LightSlateBlue", 0x8470ff }, + { "LightSlateGray", 0x778899 }, + { "LightSlateGrey", 0x778899 }, + { "LightSteelBlue", 0xb0c4de }, + { "LightSteelBlue1", 0xcae1ff }, + { "LightSteelBlue2", 0xbcd2ee }, + { "LightSteelBlue3", 0xa2b5cd }, + { "LightSteelBlue4", 0x6e7b8b }, + { "LightYellow", 0xffffe0 }, + { "LightYellow1", 0xffffe0 }, + { "LightYellow2", 0xeeeed1 }, + { "LightYellow3", 0xcdcdb4 }, + { "LightYellow4", 0x8b8b7a }, + { "LimeGreen", 0x32cd32 }, + { "MediumAquamarine", 0x66cdaa }, + { "MediumBlue", 0x0000cd }, + { "MediumOrchid", 0xba55d3 }, + { "MediumOrchid1", 0xe066ff }, + { "MediumOrchid2", 0xd15fee }, + { "MediumOrchid3", 0xb452cd }, + { "MediumOrchid4", 0x7a378b }, + { "MediumPurple", 0x9370db }, + { "MediumPurple1", 0xab82ff }, + { "MediumPurple2", 0x9f79ee }, + { "MediumPurple3", 0x8968cd }, + { "MediumPurple4", 0x5d478b }, + { "MediumSeaGreen", 0x3cb371 }, + { "MediumSlateBlue", 0x7b68ee }, + { "MediumSpringGreen", 0x00fa9a }, + { "MediumTurquoise", 0x48d1cc }, + { "MediumVioletRed", 0xc71585 }, + { "MidnightBlue", 0x191970 }, + { "MintCream", 0xf5fffa }, + { "MistyRose", 0xffe4e1 }, + { "MistyRose1", 0xffe4e1 }, + { "MistyRose2", 0xeed5d2 }, + { "MistyRose3", 0xcdb7b5 }, + { "MistyRose4", 0x8b7d7b }, + { "NavajoWhite", 0xffdead }, + { "NavajoWhite1", 0xffdead }, + { "NavajoWhite2", 0xeecfa1 }, + { "NavajoWhite3", 0xcdb38b }, + { "NavajoWhite4", 0x8b795e }, + { "NavyBlue", 0x000080 }, + { "OldLace", 0xfdf5e6 }, + { "OliveDrab", 0x6b8e23 }, + { "OliveDrab1", 0xc0ff3e }, + { "OliveDrab2", 0xb3ee3a }, + { "OliveDrab3", 0x9acd32 }, + { "OliveDrab4", 0x698b22 }, + { "OrangeRed", 0xff4500 }, + { "OrangeRed1", 0xff4500 }, + { "OrangeRed2", 0xee4000 }, + { "OrangeRed3", 0xcd3700 }, + { "OrangeRed4", 0x8b2500 }, + { "PaleGoldenrod", 0xeee8aa }, + { "PaleGreen", 0x98fb98 }, + { "PaleGreen1", 0x9aff9a }, + { "PaleGreen2", 0x90ee90 }, + { "PaleGreen3", 0x7ccd7c }, + { "PaleGreen4", 0x548b54 }, + { "PaleTurquoise", 0xafeeee }, + { "PaleTurquoise1", 0xbbffff }, + { "PaleTurquoise2", 0xaeeeee }, + { "PaleTurquoise3", 0x96cdcd }, + { "PaleTurquoise4", 0x668b8b }, + { "PaleVioletRed", 0xdb7093 }, + { "PaleVioletRed1", 0xff82ab }, + { "PaleVioletRed2", 0xee799f }, + { "PaleVioletRed3", 0xcd6889 }, + { "PaleVioletRed4", 0x8b475d }, + { "PapayaWhip", 0xffefd5 }, + { "PeachPuff", 0xffdab9 }, + { "PeachPuff1", 0xffdab9 }, + { "PeachPuff2", 0xeecbad }, + { "PeachPuff3", 0xcdaf95 }, + { "PeachPuff4", 0x8b7765 }, + { "PowderBlue", 0xb0e0e6 }, + { "RebeccaPurple", 0x663399 }, + { "RosyBrown", 0xbc8f8f }, + { "RosyBrown1", 0xffc1c1 }, + { "RosyBrown2", 0xeeb4b4 }, + { "RosyBrown3", 0xcd9b9b }, + { "RosyBrown4", 0x8b6969 }, + { "RoyalBlue", 0x4169e1 }, + { "RoyalBlue1", 0x4876ff }, + { "RoyalBlue2", 0x436eee }, + { "RoyalBlue3", 0x3a5fcd }, + { "RoyalBlue4", 0x27408b }, + { "SaddleBrown", 0x8b4513 }, + { "SandyBrown", 0xf4a460 }, + { "SeaGreen", 0x2e8b57 }, + { "SeaGreen1", 0x54ff9f }, + { "SeaGreen2", 0x4eee94 }, + { "SeaGreen3", 0x43cd80 }, + { "SeaGreen4", 0x2e8b57 }, + { "SkyBlue", 0x87ceeb }, + { "SkyBlue1", 0x87ceff }, + { "SkyBlue2", 0x7ec0ee }, + { "SkyBlue3", 0x6ca6cd }, + { "SkyBlue4", 0x4a708b }, + { "SlateBlue", 0x6a5acd }, + { "SlateBlue1", 0x836fff }, + { "SlateBlue2", 0x7a67ee }, + { "SlateBlue3", 0x6959cd }, + { "SlateBlue4", 0x473c8b }, + { "SlateGray", 0x708090 }, + { "SlateGray1", 0xc6e2ff }, + { "SlateGray2", 0xb9d3ee }, + { "SlateGray3", 0x9fb6cd }, + { "SlateGray4", 0x6c7b8b }, + { "SlateGrey", 0x708090 }, + { "SpringGreen", 0x00ff7f }, + { "SpringGreen1", 0x00ff7f }, + { "SpringGreen2", 0x00ee76 }, + { "SpringGreen3", 0x00cd66 }, + { "SpringGreen4", 0x008b45 }, + { "SteelBlue", 0x4682b4 }, + { "SteelBlue1", 0x63b8ff }, + { "SteelBlue2", 0x5cacee }, + { "SteelBlue3", 0x4f94cd }, + { "SteelBlue4", 0x36648b }, + { "VioletRed", 0xd02090 }, + { "VioletRed1", 0xff3e96 }, + { "VioletRed2", 0xee3a8c }, + { "VioletRed3", 0xcd3278 }, + { "VioletRed4", 0x8b2252 }, + { "WebGray", 0x808080 }, + { "WebGreen", 0x008000 }, + { "WebGrey", 0x808080 }, + { "WebMaroon", 0x800000 }, + { "WebPurple", 0x800080 }, + { "WhiteSmoke", 0xf5f5f5 }, + { "X11Gray", 0xbebebe }, + { "X11Green", 0x00ff00 }, + { "X11Grey", 0xbebebe }, + { "X11Maroon", 0xb03060 }, + { "X11Purple", 0xa020f0 }, + { "YellowGreen", 0x9acd32 }, + { "alice blue", 0xf0f8ff }, + { "antique white", 0xfaebd7 }, + { "aqua", 0x00ffff }, + { "aquamarine", 0x7fffd4 }, + { "aquamarine1", 0x7fffd4 }, + { "aquamarine2", 0x76eec6 }, + { "aquamarine3", 0x66cdaa }, + { "aquamarine4", 0x458b74 }, + { "azure", 0xf0ffff }, + { "azure1", 0xf0ffff }, + { "azure2", 0xe0eeee }, + { "azure3", 0xc1cdcd }, + { "azure4", 0x838b8b }, + { "beige", 0xf5f5dc }, + { "bisque", 0xffe4c4 }, + { "bisque1", 0xffe4c4 }, + { "bisque2", 0xeed5b7 }, + { "bisque3", 0xcdb79e }, + { "bisque4", 0x8b7d6b }, + { "black", 0x000000 }, + { "blanched almond", 0xffebcd }, + { "blue violet", 0x8a2be2 }, + { "blue", 0x0000ff }, + { "blue1", 0x0000ff }, + { "blue2", 0x0000ee }, + { "blue3", 0x0000cd }, + { "blue4", 0x00008b }, + { "brown", 0xa52a2a }, + { "brown1", 0xff4040 }, + { "brown2", 0xee3b3b }, + { "brown3", 0xcd3333 }, + { "brown4", 0x8b2323 }, + { "burlywood", 0xdeb887 }, + { "burlywood1", 0xffd39b }, + { "burlywood2", 0xeec591 }, + { "burlywood3", 0xcdaa7d }, + { "burlywood4", 0x8b7355 }, + { "cadet blue", 0x5f9ea0 }, + { "chartreuse", 0x7fff00 }, + { "chartreuse1", 0x7fff00 }, + { "chartreuse2", 0x76ee00 }, + { "chartreuse3", 0x66cd00 }, + { "chartreuse4", 0x458b00 }, + { "chocolate", 0xd2691e }, + { "chocolate1", 0xff7f24 }, + { "chocolate2", 0xee7621 }, + { "chocolate3", 0xcd661d }, + { "chocolate4", 0x8b4513 }, + { "coral", 0xff7f50 }, + { "coral1", 0xff7256 }, + { "coral2", 0xee6a50 }, + { "coral3", 0xcd5b45 }, + { "coral4", 0x8b3e2f }, + { "cornflower blue", 0x6495ed }, + { "cornsilk", 0xfff8dc }, + { "cornsilk1", 0xfff8dc }, + { "cornsilk2", 0xeee8cd }, + { "cornsilk3", 0xcdc8b1 }, + { "cornsilk4", 0x8b8878 }, + { "crimson", 0xdc143c }, + { "cyan", 0x00ffff }, + { "cyan1", 0x00ffff }, + { "cyan2", 0x00eeee }, + { "cyan3", 0x00cdcd }, + { "cyan4", 0x008b8b }, + { "dark blue", 0x00008b }, + { "dark cyan", 0x008b8b }, + { "dark goldenrod", 0xb8860b }, + { "dark gray", 0xa9a9a9 }, + { "dark green", 0x006400 }, + { "dark grey", 0xa9a9a9 }, + { "dark khaki", 0xbdb76b }, + { "dark magenta", 0x8b008b }, + { "dark olive green", 0x556b2f }, + { "dark orange", 0xff8c00 }, + { "dark orchid", 0x9932cc }, + { "dark red", 0x8b0000 }, + { "dark salmon", 0xe9967a }, + { "dark sea green", 0x8fbc8f }, + { "dark slate blue", 0x483d8b }, + { "dark slate gray", 0x2f4f4f }, + { "dark slate grey", 0x2f4f4f }, + { "dark turquoise", 0x00ced1 }, + { "dark violet", 0x9400d3 }, + { "deep pink", 0xff1493 }, + { "deep sky blue", 0x00bfff }, + { "dim gray", 0x696969 }, + { "dim grey", 0x696969 }, + { "dodger blue", 0x1e90ff }, + { "firebrick", 0xb22222 }, + { "firebrick1", 0xff3030 }, + { "firebrick2", 0xee2c2c }, + { "firebrick3", 0xcd2626 }, + { "firebrick4", 0x8b1a1a }, + { "floral white", 0xfffaf0 }, + { "forest green", 0x228b22 }, + { "fuchsia", 0xff00ff }, + { "gainsboro", 0xdcdcdc }, + { "ghost white", 0xf8f8ff }, + { "gold", 0xffd700 }, + { "gold1", 0xffd700 }, + { "gold2", 0xeec900 }, + { "gold3", 0xcdad00 }, + { "gold4", 0x8b7500 }, + { "goldenrod", 0xdaa520 }, + { "goldenrod1", 0xffc125 }, + { "goldenrod2", 0xeeb422 }, + { "goldenrod3", 0xcd9b1d }, + { "goldenrod4", 0x8b6914 }, + { "green yellow", 0xadff2f }, + { "green", 0x00ff00 }, + { "green1", 0x00ff00 }, + { "green2", 0x00ee00 }, + { "green3", 0x00cd00 }, + { "green4", 0x008b00 }, + { "honeydew", 0xf0fff0 }, + { "honeydew1", 0xf0fff0 }, + { "honeydew2", 0xe0eee0 }, + { "honeydew3", 0xc1cdc1 }, + { "honeydew4", 0x838b83 }, + { "hot pink", 0xff69b4 }, + { "indian red", 0xcd5c5c }, + { "indigo", 0x4b0082 }, + { "ivory", 0xfffff0 }, + { "ivory1", 0xfffff0 }, + { "ivory2", 0xeeeee0 }, + { "ivory3", 0xcdcdc1 }, + { "ivory4", 0x8b8b83 }, + { "khaki", 0xf0e68c }, + { "khaki1", 0xfff68f }, + { "khaki2", 0xeee685 }, + { "khaki3", 0xcdc673 }, + { "khaki4", 0x8b864e }, + { "lavender blush", 0xfff0f5 }, + { "lavender", 0xe6e6fa }, + { "lawn green", 0x7cfc00 }, + { "lemon chiffon", 0xfffacd }, + { "light blue", 0xadd8e6 }, + { "light coral", 0xf08080 }, + { "light cyan", 0xe0ffff }, + { "light goldenrod yellow", 0xfafad2 }, + { "light goldenrod", 0xeedd82 }, + { "light gray", 0xd3d3d3 }, + { "light green", 0x90ee90 }, + { "light grey", 0xd3d3d3 }, + { "light pink", 0xffb6c1 }, + { "light salmon", 0xffa07a }, + { "light sea green", 0x20b2aa }, + { "light sky blue", 0x87cefa }, + { "light slate blue", 0x8470ff }, + { "light slate gray", 0x778899 }, + { "light slate grey", 0x778899 }, + { "light steel blue", 0xb0c4de }, + { "light yellow", 0xffffe0 }, + { "lime green", 0x32cd32 }, + { "lime", 0x00ff00 }, + { "linen", 0xfaf0e6 }, + { "magenta", 0xff00ff }, + { "magenta1", 0xff00ff }, + { "magenta2", 0xee00ee }, + { "magenta3", 0xcd00cd }, + { "magenta4", 0x8b008b }, + { "maroon", 0xb03060 }, + { "maroon1", 0xff34b3 }, + { "maroon2", 0xee30a7 }, + { "maroon3", 0xcd2990 }, + { "maroon4", 0x8b1c62 }, + { "medium aquamarine", 0x66cdaa }, + { "medium blue", 0x0000cd }, + { "medium orchid", 0xba55d3 }, + { "medium purple", 0x9370db }, + { "medium sea green", 0x3cb371 }, + { "medium slate blue", 0x7b68ee }, + { "medium spring green", 0x00fa9a }, + { "medium turquoise", 0x48d1cc }, + { "medium violet red", 0xc71585 }, + { "midnight blue", 0x191970 }, + { "mint cream", 0xf5fffa }, + { "misty rose", 0xffe4e1 }, + { "moccasin", 0xffe4b5 }, + { "navajo white", 0xffdead }, + { "navy blue", 0x000080 }, + { "navy", 0x000080 }, + { "old lace", 0xfdf5e6 }, + { "olive drab", 0x6b8e23 }, + { "olive", 0x808000 }, + { "orange red", 0xff4500 }, + { "orange", 0xffa500 }, + { "orange1", 0xffa500 }, + { "orange2", 0xee9a00 }, + { "orange3", 0xcd8500 }, + { "orange4", 0x8b5a00 }, + { "orchid", 0xda70d6 }, + { "orchid1", 0xff83fa }, + { "orchid2", 0xee7ae9 }, + { "orchid3", 0xcd69c9 }, + { "orchid4", 0x8b4789 }, + { "pale goldenrod", 0xeee8aa }, + { "pale green", 0x98fb98 }, + { "pale turquoise", 0xafeeee }, + { "pale violet red", 0xdb7093 }, + { "papaya whip", 0xffefd5 }, + { "peach puff", 0xffdab9 }, + { "peru", 0xcd853f }, + { "pink", 0xffc0cb }, + { "pink1", 0xffb5c5 }, + { "pink2", 0xeea9b8 }, + { "pink3", 0xcd919e }, + { "pink4", 0x8b636c }, + { "plum", 0xdda0dd }, + { "plum1", 0xffbbff }, + { "plum2", 0xeeaeee }, + { "plum3", 0xcd96cd }, + { "plum4", 0x8b668b }, + { "powder blue", 0xb0e0e6 }, + { "purple", 0xa020f0 }, + { "purple1", 0x9b30ff }, + { "purple2", 0x912cee }, + { "purple3", 0x7d26cd }, + { "purple4", 0x551a8b }, + { "rebecca purple", 0x663399 }, + { "red", 0xff0000 }, + { "red1", 0xff0000 }, + { "red2", 0xee0000 }, + { "red3", 0xcd0000 }, + { "red4", 0x8b0000 }, + { "rosy brown", 0xbc8f8f }, + { "royal blue", 0x4169e1 }, + { "saddle brown", 0x8b4513 }, + { "salmon", 0xfa8072 }, + { "salmon1", 0xff8c69 }, + { "salmon2", 0xee8262 }, + { "salmon3", 0xcd7054 }, + { "salmon4", 0x8b4c39 }, + { "sandy brown", 0xf4a460 }, + { "sea green", 0x2e8b57 }, + { "seashell", 0xfff5ee }, + { "seashell1", 0xfff5ee }, + { "seashell2", 0xeee5de }, + { "seashell3", 0xcdc5bf }, + { "seashell4", 0x8b8682 }, + { "sienna", 0xa0522d }, + { "sienna1", 0xff8247 }, + { "sienna2", 0xee7942 }, + { "sienna3", 0xcd6839 }, + { "sienna4", 0x8b4726 }, + { "silver", 0xc0c0c0 }, + { "sky blue", 0x87ceeb }, + { "slate blue", 0x6a5acd }, + { "slate gray", 0x708090 }, + { "slate grey", 0x708090 }, + { "snow", 0xfffafa }, + { "snow1", 0xfffafa }, + { "snow2", 0xeee9e9 }, + { "snow3", 0xcdc9c9 }, + { "snow4", 0x8b8989 }, + { "spring green", 0x00ff7f }, + { "steel blue", 0x4682b4 }, + { "tan", 0xd2b48c }, + { "tan1", 0xffa54f }, + { "tan2", 0xee9a49 }, + { "tan3", 0xcd853f }, + { "tan4", 0x8b5a2b }, + { "teal", 0x008080 }, + { "thistle", 0xd8bfd8 }, + { "thistle1", 0xffe1ff }, + { "thistle2", 0xeed2ee }, + { "thistle3", 0xcdb5cd }, + { "thistle4", 0x8b7b8b }, + { "tomato", 0xff6347 }, + { "tomato1", 0xff6347 }, + { "tomato2", 0xee5c42 }, + { "tomato3", 0xcd4f39 }, + { "tomato4", 0x8b3626 }, + { "turquoise", 0x40e0d0 }, + { "turquoise1", 0x00f5ff }, + { "turquoise2", 0x00e5ee }, + { "turquoise3", 0x00c5cd }, + { "turquoise4", 0x00868b }, + { "violet red", 0xd02090 }, + { "violet", 0xee82ee }, + { "web gray", 0x808080 }, + { "web green", 0x008000 }, + { "web grey", 0x808080 }, + { "web maroon", 0x800000 }, + { "web purple", 0x800080 }, + { "wheat", 0xf5deb3 }, + { "wheat1", 0xffe7ba }, + { "wheat2", 0xeed8ae }, + { "wheat3", 0xcdba96 }, + { "wheat4", 0x8b7e66 }, + { "white smoke", 0xf5f5f5 }, + { "white", 0xffffff }, + { "x11 gray", 0xbebebe }, + { "x11 green", 0x00ff00 }, + { "x11 grey", 0xbebebe }, + { "x11 maroon", 0xb03060 }, + { "x11 purple", 0xa020f0 }, + { "yellow green", 0x9acd32 }, + { "yellow", 0xffff00 }, + { "yellow1", 0xffff00 }, + { "yellow2", 0xeeee00 }, + { "yellow3", 0xcdcd00 }, + { "yellow4", 0x8b8b00 } + }; + u_int i; + int c; + + if (strncmp(name, "grey", 4) == 0 || strncmp(name, "gray", 4) == 0) { + if (!isdigit((u_char)name[4])) + return (0xbebebe|COLOUR_FLAG_RGB); + c = round(2.55 * atoi(name + 4)); + if (c < 0 || c > 255) + return (-1); + return (colour_join_rgb(c, c, c)); + } + for (i = 0; i < nitems(colours); i++) { + if (strcasecmp(colours[i].name, name) == 0) + return (colours[i].c|COLOUR_FLAG_RGB); + } + return (-1); +} diff --git a/format.c b/format.c index 0e773498..47e0bb29 100644 --- a/format.c +++ b/format.c @@ -878,6 +878,28 @@ format_cb_pane_tabs(struct format_tree *ft) return (value); } +/* Callback for pane_fg. */ +static char * +format_cb_pane_fg(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + struct grid_cell gc; + + tty_default_colours(&gc, wp); + return (xstrdup(colour_tostring(gc.fg))); +} + +/* Callback for pane_bg. */ +static char * +format_cb_pane_bg(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + struct grid_cell gc; + + tty_default_colours(&gc, wp); + return (xstrdup(colour_tostring(gc.bg))); +} + /* Callback for session_group_list. */ static char * format_cb_session_group_list(struct format_tree *ft) @@ -3195,6 +3217,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) !!(wp->base.mode & MODE_MOUSE_SGR)); format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); + format_add_cb(ft, "pane_fg", format_cb_pane_fg); + format_add_cb(ft, "pane_bg", format_cb_pane_bg); } /* Set default format keys for paste buffer. */ diff --git a/input.c b/input.c index 8b7ba08a..d22ee1a8 100644 --- a/input.c +++ b/input.c @@ -138,6 +138,8 @@ static void input_osc_10(struct input_ctx *, const char *); static void input_osc_11(struct input_ctx *, const char *); static void input_osc_52(struct input_ctx *, const char *); static void input_osc_104(struct input_ctx *, const char *); +static void input_osc_110(struct input_ctx *, const char *); +static void input_osc_111(struct input_ctx *, const char *); /* Transition entry/exit handlers. */ static void input_clear(struct input_ctx *); @@ -2304,6 +2306,12 @@ input_exit_osc(struct input_ctx *ictx) case 104: input_osc_104(ictx, p); break; + case 110: + input_osc_110(ictx, p); + break; + case 111: + input_osc_111(ictx, p); + break; case 112: if (*p == '\0') /* no arguments allowed */ screen_set_cursor_colour(sctx->s, ""); @@ -2422,50 +2430,41 @@ input_top_bit_set(struct input_ctx *ictx) /* Parse colour from OSC. */ static int -input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b) +input_osc_parse_colour(const char *p) { - u_int rsize, gsize, bsize; - const char *cp, *s = p; + double c, m, y, k = 0; + u_int r, g, b; + size_t len = strlen(p); + int colour = -1; + char *copy; - if (sscanf(p, "rgb:%x/%x/%x", r, g, b) != 3) - return (0); - p += 4; - - cp = strchr(p, '/'); - rsize = cp - p; - if (rsize == 1) - (*r) = (*r) | ((*r) << 4); - else if (rsize == 3) - (*r) >>= 4; - else if (rsize == 4) - (*r) >>= 8; - else if (rsize != 2) - return (0); - - p = cp + 1; - cp = strchr(p, '/'); - gsize = cp - p; - if (gsize == 1) - (*g) = (*g) | ((*g) << 4); - else if (gsize == 3) - (*g) >>= 4; - else if (gsize == 4) - (*g) >>= 8; - else if (gsize != 2) - return (0); - - bsize = strlen(cp + 1); - if (bsize == 1) - (*b) = (*b) | ((*b) << 4); - else if (bsize == 3) - (*b) >>= 4; - else if (bsize == 4) - (*b) >>= 8; - else if (bsize != 2) - return (0); - - log_debug("%s: %s = %02x%02x%02x", __func__, s, *r, *g, *b); - return (1); + if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) || + (len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) || + sscanf(p, "%d,%d,%d", &r, &g, &b) == 3) + colour = colour_join_rgb(r, g, b); + else if ((len == 18 && + sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) || + (len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3)) + colour = colour_join_rgb(r >> 8, g >> 8, b >> 8); + else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 || + sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) && + c >= 0 && c <= 1 && m >= 0 && m <= 1 && + y >= 0 && y <= 1 && k >= 0 && k <= 1) { + colour = colour_join_rgb( + (1 - c) * (1 - k) * 255, + (1 - m) * (1 - k) * 255, + (1 - y) * (1 - k) * 255); + } else { + while (*p == ' ') + p++; + while (len != 0 && p[len - 1] == ' ') + len--; + copy = xstrndup(p, len); + colour = colour_byname(copy); + free(copy); + } + log_debug("%s: %s = %s", __func__, p, colour_tostring(colour)); + return (colour); } /* Reply to a colour request. */ @@ -2493,7 +2492,7 @@ input_osc_4(struct input_ctx *ictx, const char *p) struct window_pane *wp = ictx->wp; char *copy, *s, *next = NULL; long idx; - u_int r, g, b; + int c; if (wp == NULL) return; @@ -2507,12 +2506,12 @@ input_osc_4(struct input_ctx *ictx, const char *p) goto bad; s = strsep(&next, ";"); - if (!input_osc_parse_colour(s, &r, &g, &b)) { + if ((c = input_osc_parse_colour(s)) == -1) { s = next; continue; } - window_pane_set_palette(wp, idx, colour_join_rgb(r, g, b)); + window_pane_set_palette(wp, idx, c); s = next; } @@ -2530,7 +2529,7 @@ 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; + int c; if (wp == NULL) return; @@ -2541,9 +2540,9 @@ input_osc_10(struct input_ctx *ictx, const char *p) return; } - if (!input_osc_parse_colour(p, &r, &g, &b)) + if ((c = input_osc_parse_colour(p)) == -1) goto bad; - wp->fg = colour_join_rgb(r, g, b); + wp->fg = c; wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; @@ -2552,13 +2551,29 @@ bad: log_debug("bad OSC 10: %s", p); } +/* Handle the OSC 110 sequence for resetting background colour. */ +static void +input_osc_110(struct input_ctx *ictx, const char *p) +{ + struct window_pane *wp = ictx->wp; + + if (wp == NULL) + return; + + if (*p != '\0') + return; + + wp->fg = 8; + wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); +} + /* 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; + int c; if (wp == NULL) return; @@ -2569,9 +2584,9 @@ input_osc_11(struct input_ctx *ictx, const char *p) return; } - if (!input_osc_parse_colour(p, &r, &g, &b)) - goto bad; - wp->bg = colour_join_rgb(r, g, b); + if ((c = input_osc_parse_colour(p)) == -1) + goto bad; + wp->bg = c; wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; @@ -2580,6 +2595,22 @@ bad: log_debug("bad OSC 11: %s", p); } +/* Handle the OSC 111 sequence for resetting background colour. */ +static void +input_osc_111(struct input_ctx *ictx, const char *p) +{ + struct window_pane *wp = ictx->wp; + + if (wp == NULL) + return; + + if (*p != '\0') + return; + + wp->bg = 8; + wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); +} + /* Handle the OSC 52 sequence for setting the clipboard. */ static void input_osc_52(struct input_ctx *ictx, const char *p) diff --git a/tmux.1 b/tmux.1 index e1891009..9150c4a6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4778,9 +4778,9 @@ The following variables are available, where appropriate: .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" +.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" .It Li "client_termname" Ta "" Ta "Terminal name of client" .It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" -.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" @@ -4828,11 +4828,13 @@ The following variables are available, where appropriate: .It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window" .It Li "pane_at_right" Ta "" Ta "1 if pane is at the right of window" .It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window" +.It Li "pane_bg" Ta "" Ta "Pane background colour" .It Li "pane_bottom" Ta "" Ta "Bottom of pane" .It Li "pane_current_command" Ta "" Ta "Current command if available" .It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" .It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" +.It Li "pane_fg" Ta "" Ta "Pane foreground colour" .It Li "pane_format" Ta "" Ta "1 if format is for a pane" .It Li "pane_height" Ta "" Ta "Height of pane" .It Li "pane_id" Ta "#D" Ta "Unique pane ID" diff --git a/tmux.h b/tmux.h index 8f83c218..18921f6e 100644 --- a/tmux.h +++ b/tmux.h @@ -2526,6 +2526,7 @@ const char *colour_tostring(int); int colour_fromstring(const char *s); int colour_256toRGB(int); int colour_256to16(int); +int colour_byname(const char *); /* attributes.c */ const char *attributes_tostring(int); From 0526d074d0170ad248b06187b64f4e44a0c05dcc Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 15 Feb 2021 09:40:50 +0000 Subject: [PATCH 0701/1006] OSC 11 test. --- regress/osc-11colours.sh | 243 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 regress/osc-11colours.sh diff --git a/regress/osc-11colours.sh b/regress/osc-11colours.sh new file mode 100644 index 00000000..a0dd605d --- /dev/null +++ b/regress/osc-11colours.sh @@ -0,0 +1,243 @@ +#!/bin/sh + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest" +$TMUX kill-server 2>/dev/null + +$TMUX -vv new -d +$TMUX set -g remain-on-exit on + +do_test() { + $TMUX splitw "printf '$1'" + c="$($TMUX display -p '#{pane_bg}')" + $TMUX kill-pane + [ "$c" != "$2" ] && return 1 + return 0 +} + +do_test '\033]11;rgb:ff/ff/ff\007' '#ffffff' || exit 1 +do_test '\033]11;rgb:ff/ff/ff\007\033]111\007' 'default' || exit 1 + +do_test '\033]11;cmy:0.9373/0.6941/0.4549\007' '#0f4e8b' || exit 1 +do_test '\033]11;cmyk:0.88/0.44/0.00/0.45\007' '#104e8c' || exit 1 + +do_test '\033]11;16,78,139\007' '#104e8b' || exit 1 +do_test '\033]11;#104E8B\007' '#104e8b' || exit 1 +do_test '\033]11;#10004E008B00\007' '#104e8b' || exit 1 +do_test '\033]11;DodgerBlue4\007' '#104e8b' || exit 1 +do_test '\033]11;DodgerBlue4 \007' '#104e8b' || exit 1 +do_test '\033]11; DodgerBlue4\007' '#104e8b' || exit 1 +do_test '\033]11;rgb:10/4E/8B\007' '#104e8b' || exit 1 +do_test '\033]11;rgb:1000/4E00/8B00\007' '#104e8b' || exit 1 + +do_test '\033]11;grey\007' '#bebebe' || exit 1 +do_test '\033]11;grey0\007' '#000000' || exit 1 +do_test '\033]11;grey1\007' '#030303' || exit 1 +do_test '\033]11;grey2\007' '#050505' || exit 1 +do_test '\033]11;grey3\007' '#080808' || exit 1 +do_test '\033]11;grey4\007' '#0a0a0a' || exit 1 +do_test '\033]11;grey5\007' '#0d0d0d' || exit 1 +do_test '\033]11;grey6\007' '#0f0f0f' || exit 1 +do_test '\033]11;grey7\007' '#121212' || exit 1 +do_test '\033]11;grey8\007' '#141414' || exit 1 +do_test '\033]11;grey9\007' '#171717' || exit 1 +do_test '\033]11;grey10\007' '#1a1a1a' || exit 1 +do_test '\033]11;grey11\007' '#1c1c1c' || exit 1 +do_test '\033]11;grey12\007' '#1f1f1f' || exit 1 +do_test '\033]11;grey13\007' '#212121' || exit 1 +do_test '\033]11;grey14\007' '#242424' || exit 1 +do_test '\033]11;grey15\007' '#262626' || exit 1 +do_test '\033]11;grey16\007' '#292929' || exit 1 +do_test '\033]11;grey17\007' '#2b2b2b' || exit 1 +do_test '\033]11;grey18\007' '#2e2e2e' || exit 1 +do_test '\033]11;grey19\007' '#303030' || exit 1 +do_test '\033]11;grey20\007' '#333333' || exit 1 +do_test '\033]11;grey21\007' '#363636' || exit 1 +do_test '\033]11;grey22\007' '#383838' || exit 1 +do_test '\033]11;grey23\007' '#3b3b3b' || exit 1 +do_test '\033]11;grey24\007' '#3d3d3d' || exit 1 +do_test '\033]11;grey25\007' '#404040' || exit 1 +do_test '\033]11;grey26\007' '#424242' || exit 1 +do_test '\033]11;grey27\007' '#454545' || exit 1 +do_test '\033]11;grey28\007' '#474747' || exit 1 +do_test '\033]11;grey29\007' '#4a4a4a' || exit 1 +do_test '\033]11;grey30\007' '#4d4d4d' || exit 1 +do_test '\033]11;grey31\007' '#4f4f4f' || exit 1 +do_test '\033]11;grey32\007' '#525252' || exit 1 +do_test '\033]11;grey33\007' '#545454' || exit 1 +do_test '\033]11;grey34\007' '#575757' || exit 1 +do_test '\033]11;grey35\007' '#595959' || exit 1 +do_test '\033]11;grey36\007' '#5c5c5c' || exit 1 +do_test '\033]11;grey37\007' '#5e5e5e' || exit 1 +do_test '\033]11;grey38\007' '#616161' || exit 1 +do_test '\033]11;grey39\007' '#636363' || exit 1 +do_test '\033]11;grey40\007' '#666666' || exit 1 +do_test '\033]11;grey41\007' '#696969' || exit 1 +do_test '\033]11;grey42\007' '#6b6b6b' || exit 1 +do_test '\033]11;grey43\007' '#6e6e6e' || exit 1 +do_test '\033]11;grey44\007' '#707070' || exit 1 +do_test '\033]11;grey45\007' '#737373' || exit 1 +do_test '\033]11;grey46\007' '#757575' || exit 1 +do_test '\033]11;grey47\007' '#787878' || exit 1 +do_test '\033]11;grey48\007' '#7a7a7a' || exit 1 +do_test '\033]11;grey49\007' '#7d7d7d' || exit 1 +do_test '\033]11;grey50\007' '#7f7f7f' || exit 1 +do_test '\033]11;grey51\007' '#828282' || exit 1 +do_test '\033]11;grey52\007' '#858585' || exit 1 +do_test '\033]11;grey53\007' '#878787' || exit 1 +do_test '\033]11;grey54\007' '#8a8a8a' || exit 1 +do_test '\033]11;grey55\007' '#8c8c8c' || exit 1 +do_test '\033]11;grey56\007' '#8f8f8f' || exit 1 +do_test '\033]11;grey57\007' '#919191' || exit 1 +do_test '\033]11;grey58\007' '#949494' || exit 1 +do_test '\033]11;grey59\007' '#969696' || exit 1 +do_test '\033]11;grey60\007' '#999999' || exit 1 +do_test '\033]11;grey61\007' '#9c9c9c' || exit 1 +do_test '\033]11;grey62\007' '#9e9e9e' || exit 1 +do_test '\033]11;grey63\007' '#a1a1a1' || exit 1 +do_test '\033]11;grey64\007' '#a3a3a3' || exit 1 +do_test '\033]11;grey65\007' '#a6a6a6' || exit 1 +do_test '\033]11;grey66\007' '#a8a8a8' || exit 1 +do_test '\033]11;grey67\007' '#ababab' || exit 1 +do_test '\033]11;grey68\007' '#adadad' || exit 1 +do_test '\033]11;grey69\007' '#b0b0b0' || exit 1 +do_test '\033]11;grey70\007' '#b3b3b3' || exit 1 +do_test '\033]11;grey71\007' '#b5b5b5' || exit 1 +do_test '\033]11;grey72\007' '#b8b8b8' || exit 1 +do_test '\033]11;grey73\007' '#bababa' || exit 1 +do_test '\033]11;grey74\007' '#bdbdbd' || exit 1 +do_test '\033]11;grey75\007' '#bfbfbf' || exit 1 +do_test '\033]11;grey76\007' '#c2c2c2' || exit 1 +do_test '\033]11;grey77\007' '#c4c4c4' || exit 1 +do_test '\033]11;grey78\007' '#c7c7c7' || exit 1 +do_test '\033]11;grey79\007' '#c9c9c9' || exit 1 +do_test '\033]11;grey80\007' '#cccccc' || exit 1 +do_test '\033]11;grey81\007' '#cfcfcf' || exit 1 +do_test '\033]11;grey82\007' '#d1d1d1' || exit 1 +do_test '\033]11;grey83\007' '#d4d4d4' || exit 1 +do_test '\033]11;grey84\007' '#d6d6d6' || exit 1 +do_test '\033]11;grey85\007' '#d9d9d9' || exit 1 +do_test '\033]11;grey86\007' '#dbdbdb' || exit 1 +do_test '\033]11;grey87\007' '#dedede' || exit 1 +do_test '\033]11;grey88\007' '#e0e0e0' || exit 1 +do_test '\033]11;grey89\007' '#e3e3e3' || exit 1 +do_test '\033]11;grey90\007' '#e5e5e5' || exit 1 +do_test '\033]11;grey91\007' '#e8e8e8' || exit 1 +do_test '\033]11;grey92\007' '#ebebeb' || exit 1 +do_test '\033]11;grey93\007' '#ededed' || exit 1 +do_test '\033]11;grey94\007' '#f0f0f0' || exit 1 +do_test '\033]11;grey95\007' '#f2f2f2' || exit 1 +do_test '\033]11;grey96\007' '#f5f5f5' || exit 1 +do_test '\033]11;grey97\007' '#f7f7f7' || exit 1 +do_test '\033]11;grey98\007' '#fafafa' || exit 1 +do_test '\033]11;grey99\007' '#fcfcfc' || exit 1 +do_test '\033]11;grey100\007' '#ffffff' || exit 1 + +do_test '\033]11;gray\007' '#bebebe' || exit 1 +do_test '\033]11;gray0\007' '#000000' || exit 1 +do_test '\033]11;gray1\007' '#030303' || exit 1 +do_test '\033]11;gray2\007' '#050505' || exit 1 +do_test '\033]11;gray3\007' '#080808' || exit 1 +do_test '\033]11;gray4\007' '#0a0a0a' || exit 1 +do_test '\033]11;gray5\007' '#0d0d0d' || exit 1 +do_test '\033]11;gray6\007' '#0f0f0f' || exit 1 +do_test '\033]11;gray7\007' '#121212' || exit 1 +do_test '\033]11;gray8\007' '#141414' || exit 1 +do_test '\033]11;gray9\007' '#171717' || exit 1 +do_test '\033]11;gray10\007' '#1a1a1a' || exit 1 +do_test '\033]11;gray11\007' '#1c1c1c' || exit 1 +do_test '\033]11;gray12\007' '#1f1f1f' || exit 1 +do_test '\033]11;gray13\007' '#212121' || exit 1 +do_test '\033]11;gray14\007' '#242424' || exit 1 +do_test '\033]11;gray15\007' '#262626' || exit 1 +do_test '\033]11;gray16\007' '#292929' || exit 1 +do_test '\033]11;gray17\007' '#2b2b2b' || exit 1 +do_test '\033]11;gray18\007' '#2e2e2e' || exit 1 +do_test '\033]11;gray19\007' '#303030' || exit 1 +do_test '\033]11;gray20\007' '#333333' || exit 1 +do_test '\033]11;gray21\007' '#363636' || exit 1 +do_test '\033]11;gray22\007' '#383838' || exit 1 +do_test '\033]11;gray23\007' '#3b3b3b' || exit 1 +do_test '\033]11;gray24\007' '#3d3d3d' || exit 1 +do_test '\033]11;gray25\007' '#404040' || exit 1 +do_test '\033]11;gray26\007' '#424242' || exit 1 +do_test '\033]11;gray27\007' '#454545' || exit 1 +do_test '\033]11;gray28\007' '#474747' || exit 1 +do_test '\033]11;gray29\007' '#4a4a4a' || exit 1 +do_test '\033]11;gray30\007' '#4d4d4d' || exit 1 +do_test '\033]11;gray31\007' '#4f4f4f' || exit 1 +do_test '\033]11;gray32\007' '#525252' || exit 1 +do_test '\033]11;gray33\007' '#545454' || exit 1 +do_test '\033]11;gray34\007' '#575757' || exit 1 +do_test '\033]11;gray35\007' '#595959' || exit 1 +do_test '\033]11;gray36\007' '#5c5c5c' || exit 1 +do_test '\033]11;gray37\007' '#5e5e5e' || exit 1 +do_test '\033]11;gray38\007' '#616161' || exit 1 +do_test '\033]11;gray39\007' '#636363' || exit 1 +do_test '\033]11;gray40\007' '#666666' || exit 1 +do_test '\033]11;gray41\007' '#696969' || exit 1 +do_test '\033]11;gray42\007' '#6b6b6b' || exit 1 +do_test '\033]11;gray43\007' '#6e6e6e' || exit 1 +do_test '\033]11;gray44\007' '#707070' || exit 1 +do_test '\033]11;gray45\007' '#737373' || exit 1 +do_test '\033]11;gray46\007' '#757575' || exit 1 +do_test '\033]11;gray47\007' '#787878' || exit 1 +do_test '\033]11;gray48\007' '#7a7a7a' || exit 1 +do_test '\033]11;gray49\007' '#7d7d7d' || exit 1 +do_test '\033]11;gray50\007' '#7f7f7f' || exit 1 +do_test '\033]11;gray51\007' '#828282' || exit 1 +do_test '\033]11;gray52\007' '#858585' || exit 1 +do_test '\033]11;gray53\007' '#878787' || exit 1 +do_test '\033]11;gray54\007' '#8a8a8a' || exit 1 +do_test '\033]11;gray55\007' '#8c8c8c' || exit 1 +do_test '\033]11;gray56\007' '#8f8f8f' || exit 1 +do_test '\033]11;gray57\007' '#919191' || exit 1 +do_test '\033]11;gray58\007' '#949494' || exit 1 +do_test '\033]11;gray59\007' '#969696' || exit 1 +do_test '\033]11;gray60\007' '#999999' || exit 1 +do_test '\033]11;gray61\007' '#9c9c9c' || exit 1 +do_test '\033]11;gray62\007' '#9e9e9e' || exit 1 +do_test '\033]11;gray63\007' '#a1a1a1' || exit 1 +do_test '\033]11;gray64\007' '#a3a3a3' || exit 1 +do_test '\033]11;gray65\007' '#a6a6a6' || exit 1 +do_test '\033]11;gray66\007' '#a8a8a8' || exit 1 +do_test '\033]11;gray67\007' '#ababab' || exit 1 +do_test '\033]11;gray68\007' '#adadad' || exit 1 +do_test '\033]11;gray69\007' '#b0b0b0' || exit 1 +do_test '\033]11;gray70\007' '#b3b3b3' || exit 1 +do_test '\033]11;gray71\007' '#b5b5b5' || exit 1 +do_test '\033]11;gray72\007' '#b8b8b8' || exit 1 +do_test '\033]11;gray73\007' '#bababa' || exit 1 +do_test '\033]11;gray74\007' '#bdbdbd' || exit 1 +do_test '\033]11;gray75\007' '#bfbfbf' || exit 1 +do_test '\033]11;gray76\007' '#c2c2c2' || exit 1 +do_test '\033]11;gray77\007' '#c4c4c4' || exit 1 +do_test '\033]11;gray78\007' '#c7c7c7' || exit 1 +do_test '\033]11;gray79\007' '#c9c9c9' || exit 1 +do_test '\033]11;gray80\007' '#cccccc' || exit 1 +do_test '\033]11;gray81\007' '#cfcfcf' || exit 1 +do_test '\033]11;gray82\007' '#d1d1d1' || exit 1 +do_test '\033]11;gray83\007' '#d4d4d4' || exit 1 +do_test '\033]11;gray84\007' '#d6d6d6' || exit 1 +do_test '\033]11;gray85\007' '#d9d9d9' || exit 1 +do_test '\033]11;gray86\007' '#dbdbdb' || exit 1 +do_test '\033]11;gray87\007' '#dedede' || exit 1 +do_test '\033]11;gray88\007' '#e0e0e0' || exit 1 +do_test '\033]11;gray89\007' '#e3e3e3' || exit 1 +do_test '\033]11;gray90\007' '#e5e5e5' || exit 1 +do_test '\033]11;gray91\007' '#e8e8e8' || exit 1 +do_test '\033]11;gray92\007' '#ebebeb' || exit 1 +do_test '\033]11;gray93\007' '#ededed' || exit 1 +do_test '\033]11;gray94\007' '#f0f0f0' || exit 1 +do_test '\033]11;gray95\007' '#f2f2f2' || exit 1 +do_test '\033]11;gray96\007' '#f5f5f5' || exit 1 +do_test '\033]11;gray97\007' '#f7f7f7' || exit 1 +do_test '\033]11;gray98\007' '#fafafa' || exit 1 +do_test '\033]11;gray99\007' '#fcfcfc' || exit 1 +do_test '\033]11;gray100\007' '#ffffff' || exit 1 + +$TMUX -f/dev/null kill-server 2>/dev/null +exit 0 From d768fc2553c2bdec6bb7b026ffffdaee0dd102f4 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Feb 2021 14:22:35 +0000 Subject: [PATCH 0702/1006] Make SGR 6 (rapid blink) the same as SGR 5 (blink) and make SGR 21 to the same as SGR 4:2, it is an old alternative. GitHub issue 2567. --- input.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/input.c b/input.c index d22ee1a8..590a157d 100644 --- a/input.c +++ b/input.c @@ -2101,6 +2101,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) gc->attr |= GRID_ATTR_UNDERSCORE; break; case 5: + case 6: gc->attr |= GRID_ATTR_BLINK; break; case 7: @@ -2112,6 +2113,10 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 9: gc->attr |= GRID_ATTR_STRIKETHROUGH; break; + case 21: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE_2; + break; case 22: gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM); break; From 5df9b3650a5d4c1dd074897f8e206f7d1a5f6de2 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 6 Feb 2021 13:02:52 +0000 Subject: [PATCH 0703/1006] In the end UTF-8 did not become a terminal feature, should not be listed in man page. --- tmux.1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 53687194..10d1fa92 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3436,8 +3436,6 @@ Supports title setting. .It usstyle Allows underscore style and colour to be set. -.It UTF-8 -Is able to handle UTF-8 output. .El .It Ic terminal-overrides[] Ar string Allow terminal descriptions read using From 5c275c2a1a963876d4ac392067e42120417dbf43 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Nov 2020 08:13:35 +0000 Subject: [PATCH 0704/1006] Log missing keys when extended keys is on rather than fatal(). --- input-keys.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/input-keys.c b/input-keys.c index 32ca4b97..39a72cdc 100644 --- a/input-keys.c +++ b/input-keys.c @@ -329,7 +329,8 @@ static struct input_key_entry input_key_defaults[] = { .data = "\033[2;_~" }, { .key = KEYC_DC|KEYC_BUILD_MODIFIERS, - .data = "\033[3;_~" } + .data = "\033[3;_~" + } }; static const key_code input_key_modifiers[] = { 0, @@ -556,7 +557,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) modifier = '8'; break; default: - fatalx("invalid key modifiers: %llx", key); + goto missing; } xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier); bufferevent_write(bev, tmp, strlen(tmp)); From af3ffa9c41936078d27b5ba1f96cec67850f98cb Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Feb 2021 07:18:36 +0000 Subject: [PATCH 0705/1006] Move the call to setupterm() into the client and have it pass the results to the server over imsg, means the server does not need to enter ncurses or read terminfo db. Old clients will not work with a new server. --- client.c | 46 ++++++++++---- server-client.c | 20 ++++-- tmux.h | 9 ++- tty-term.c | 162 +++++++++++++++++++++++++++++++++--------------- tty.c | 4 +- 5 files changed, 169 insertions(+), 72 deletions(-) diff --git a/client.c b/client.c index 5a454ffe..002cc6c7 100644 --- a/client.c +++ b/client.c @@ -62,7 +62,8 @@ static __dead void client_exec(const char *,const char *); static int client_get_lock(char *); static int client_connect(struct event_base *, const char *, uint64_t); -static void client_send_identify(const char *, const char *, int); +static void client_send_identify(const char *, const char *, + char **, u_int, const char *, int); static void client_signal(int); static void client_dispatch(struct imsg *, void *); static void client_dispatch_attached(struct imsg *); @@ -235,13 +236,14 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, struct cmd_parse_result *pr; struct msg_command *data; int fd, i; - const char *ttynam, *cwd; + const char *ttynam, *termname, *cwd; pid_t ppid; enum msgtype msg; struct termios tio, saved_tio; size_t size, linesize = 0; ssize_t linelen; - char *line = NULL; + char *line = NULL, **caps = NULL, *cause; + u_int ncaps = 0; /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ signal(SIGCHLD, SIG_IGN); @@ -297,6 +299,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, cwd = "/"; if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; + if ((termname = getenv("TERM")) == NULL) + termname = ""; /* * Drop privileges for client. "proc exec" is needed for -c and for @@ -312,6 +316,16 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, NULL) != 0) fatal("pledge failed"); + /* Load terminfo entry if any. */ + if (isatty(STDIN_FILENO) && + *termname != '\0' && + tty_term_read_list(termname, STDIN_FILENO, &caps, &ncaps, + &cause) != 0) { + fprintf(stderr, "%s\n", cause); + free(cause); + return (1); + } + /* Free stuff that is not used in the client. */ if (ptm_fd != -1) close(ptm_fd); @@ -340,7 +354,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, } /* Send identify messages. */ - client_send_identify(ttynam, cwd, feat); + client_send_identify(ttynam, termname, caps, ncaps, cwd, feat); + tty_term_free_list(caps, ncaps); /* Send first command. */ if (msg == MSG_COMMAND) { @@ -423,27 +438,32 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, /* Send identify messages to server. */ static void -client_send_identify(const char *ttynam, const char *cwd, int feat) +client_send_identify(const char *ttynam, const char *termname, char **caps, + u_int ncaps, const char *cwd, int feat) { - const char *s; - char **ss; - size_t sslen; - int fd, flags = client_flags; - pid_t pid; + char **ss; + size_t sslen; + int fd, flags = client_flags; + pid_t pid; + u_int i; 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 = ""; - proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); + proc_send(client_peer, MSG_IDENTIFY_TERM, -1, termname, + strlen(termname) + 1); proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat); proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); + for (i = 0; i < ncaps; i++) { + proc_send(client_peer, MSG_IDENTIFY_TERMINFO, -1, + caps[i], strlen(caps[i]) + 1); + } + if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); diff --git a/server-client.c b/server-client.c index 7d9cc0e6..adeea5dd 100644 --- a/server-client.c +++ b/server-client.c @@ -307,6 +307,7 @@ server_client_lost(struct client *c) free(c->term_name); free(c->term_type); + tty_term_free_list(c->term_caps, c->term_ncaps); status_free(c); @@ -1997,16 +1998,17 @@ server_client_dispatch(struct imsg *imsg, void *arg) datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { + case MSG_IDENTIFY_CLIENTPID: + case MSG_IDENTIFY_CWD: + case MSG_IDENTIFY_ENVIRON: case MSG_IDENTIFY_FEATURES: case MSG_IDENTIFY_FLAGS: case MSG_IDENTIFY_LONGFLAGS: - case MSG_IDENTIFY_TERM: - case MSG_IDENTIFY_TTYNAME: - case MSG_IDENTIFY_CWD: case MSG_IDENTIFY_STDIN: case MSG_IDENTIFY_STDOUT: - case MSG_IDENTIFY_ENVIRON: - case MSG_IDENTIFY_CLIENTPID: + case MSG_IDENTIFY_TERM: + case MSG_IDENTIFY_TERMINFO: + case MSG_IDENTIFY_TTYNAME: case MSG_IDENTIFY_DONE: server_client_dispatch_identify(c, imsg); break; @@ -2200,6 +2202,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->term_name = xstrdup(data); log_debug("client %p IDENTIFY_TERM %s", c, data); break; + case MSG_IDENTIFY_TERMINFO: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_TERMINFO string"); + c->term_caps = xreallocarray(c->term_caps, c->term_ncaps + 1, + sizeof *c->term_caps); + c->term_caps[c->term_ncaps++] = xstrdup(data); + log_debug("client %p IDENTIFY_TERMINFO %s", c, data); + break; case MSG_IDENTIFY_TTYNAME: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TTYNAME string"); diff --git a/tmux.h b/tmux.h index 18921f6e..88c6d30e 100644 --- a/tmux.h +++ b/tmux.h @@ -500,6 +500,7 @@ enum msgtype { MSG_IDENTIFY_FEATURES, MSG_IDENTIFY_STDOUT, MSG_IDENTIFY_LONGFLAGS, + MSG_IDENTIFY_TERMINFO, MSG_COMMAND = 200, MSG_DETACH, @@ -1603,6 +1604,8 @@ struct client { char *term_name; int term_features; char *term_type; + char **term_caps; + u_int term_ncaps; char *ttyname; struct tty tty; @@ -2167,8 +2170,12 @@ extern struct tty_terms tty_terms; u_int tty_term_ncodes(void); void tty_term_apply(struct tty_term *, const char *, int); void tty_term_apply_overrides(struct tty_term *); -struct tty_term *tty_term_create(struct tty *, char *, int *, int, char **); +struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, int *, + char **); void tty_term_free(struct tty_term *); +int tty_term_read_list(const char *, int, char ***, u_int *, + char **); +void tty_term_free_list(char **, u_int); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); diff --git a/tty-term.c b/tty-term.c index b989328c..c8048f77 100644 --- a/tty-term.c +++ b/tty-term.c @@ -450,7 +450,8 @@ tty_term_apply_overrides(struct tty_term *term) } struct tty_term * -tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) +tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps, + int *feat, char **cause) { struct tty_term *term; const struct tty_term_code_entry *ent; @@ -458,10 +459,9 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) struct options_entry *o; struct options_array_item *a; union options_value *ov; - u_int i; - int n, error; - const char *s, *acs; - size_t offset; + u_int i, j; + const char *s, *acs, *value; + size_t offset, namelen; char *first; log_debug("adding term %s", name); @@ -472,57 +472,38 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) term->codes = xcalloc(tty_term_ncodes(), sizeof *term->codes); LIST_INSERT_HEAD(&tty_terms, term, entry); - /* Set up curses terminal. */ - if (setupterm(name, fd, &error) != OK) { - switch (error) { - case 1: - xasprintf(cause, "can't use hardcopy terminal: %s", - name); - break; - case 0: - xasprintf(cause, "missing or unsuitable terminal: %s", - name); - break; - case -1: - xasprintf(cause, "can't find terminfo database"); - break; - default: - xasprintf(cause, "unknown error"); - break; - } - goto error; - } - /* Fill in codes. */ - for (i = 0; i < tty_term_ncodes(); i++) { - ent = &tty_term_codes[i]; + for (i = 0; i < ncaps; i++) { + namelen = strcspn(caps[i], "="); + if (namelen == 0) + continue; + value = caps[i] + namelen + 1; - code = &term->codes[i]; - code->type = TTYCODE_NONE; - switch (ent->type) { - case TTYCODE_NONE: - break; - case TTYCODE_STRING: - s = tigetstr((char *) ent->name); - if (s == NULL || s == (char *) -1) + for (j = 0; j < tty_term_ncodes(); j++) { + ent = &tty_term_codes[j]; + if (strncmp(ent->name, caps[i], namelen) != 0) + continue; + if (ent->name[namelen] != '\0') + continue; + + code = &term->codes[j]; + code->type = TTYCODE_NONE; + switch (ent->type) { + case TTYCODE_NONE: break; - code->type = TTYCODE_STRING; - code->value.string = tty_term_strip(s); - break; - case TTYCODE_NUMBER: - n = tigetnum((char *) ent->name); - if (n == -1 || n == -2) + case TTYCODE_STRING: + code->type = TTYCODE_STRING; + code->value.string = tty_term_strip(value); break; - code->type = TTYCODE_NUMBER; - code->value.number = n; - break; - case TTYCODE_FLAG: - n = tigetflag((char *) ent->name); - if (n == -1) + case TTYCODE_NUMBER: + code->type = TTYCODE_NUMBER; + code->value.number = atoi(value); break; - code->type = TTYCODE_FLAG; - code->value.flag = n; - break; + case TTYCODE_FLAG: + code->type = TTYCODE_FLAG; + code->value.flag = (*value == '1'); + break; + } } } @@ -643,6 +624,85 @@ tty_term_free(struct tty_term *term) free(term); } +int +tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps, + char **cause) +{ + const struct tty_term_code_entry *ent; + int error, n; + u_int i; + const char *s; + char tmp[11]; + + if (setupterm(name, fd, &error) != OK) { + switch (error) { + case 1: + xasprintf(cause, "can't use hardcopy terminal: %s", + name); + break; + case 0: + xasprintf(cause, "missing or unsuitable terminal: %s", + name); + break; + case -1: + xasprintf(cause, "can't find terminfo database"); + break; + default: + xasprintf(cause, "unknown error"); + break; + } + return (-1); + } + + *ncaps = 0; + *caps = NULL; + + for (i = 0; i < tty_term_ncodes(); i++) { + ent = &tty_term_codes[i]; + switch (ent->type) { + case TTYCODE_NONE: + break; + case TTYCODE_STRING: + s = tigetstr((char *)ent->name); + if (s == NULL || s == (char *)-1) + continue; + break; + case TTYCODE_NUMBER: + n = tigetnum((char *)ent->name); + if (n == -1 || n == -2) + continue; + xsnprintf(tmp, sizeof tmp, "%d", n); + s = tmp; + break; + case TTYCODE_FLAG: + n = tigetflag((char *) ent->name); + if (n == -1) + continue; + if (n) + s = "1"; + else + s = "0"; + break; + } + *caps = xreallocarray(*caps, (*ncaps) + 1, sizeof **caps); + xasprintf(&(*caps)[*ncaps], "%s=%s", ent->name, s); + (*ncaps)++; + } + + del_curterm(cur_term); + return (0); +} + +void +tty_term_free_list(char **caps, u_int ncaps) +{ + u_int i; + + for (i = 0; i < ncaps; i++) + free(caps[i]); + free(caps); +} + int tty_term_has(struct tty_term *term, enum tty_code_code code) { diff --git a/tty.c b/tty.c index 279bafaf..399bbae8 100644 --- a/tty.c +++ b/tty.c @@ -249,8 +249,8 @@ tty_open(struct tty *tty, char **cause) { struct client *c = tty->client; - tty->term = tty_term_create(tty, c->term_name, &c->term_features, - c->fd, cause); + tty->term = tty_term_create(tty, c->term_name, c->term_caps, + c->term_ncaps, &c->term_features, cause); if (tty->term == NULL) { tty_close(tty); return (-1); From fb42ae3071763fdc6a2fe2e57feec774623b1c33 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Feb 2021 13:30:24 +0000 Subject: [PATCH 0706/1006] Reduce len when moving past spaces in OSC 11 parameter. --- input.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index 590a157d..f6aeb027 100644 --- a/input.c +++ b/input.c @@ -2460,8 +2460,10 @@ input_osc_parse_colour(const char *p) (1 - m) * (1 - k) * 255, (1 - y) * (1 - k) * 255); } else { - while (*p == ' ') + while (len != 0 && *p == ' ') { p++; + len--; + } while (len != 0 && p[len - 1] == ' ') len--; copy = xstrndup(p, len); From b04f8acb7057bda74e30976acedbbd73767e5bdc Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 19 Feb 2021 09:09:16 +0000 Subject: [PATCH 0707/1006] Check return value of chdir() to stop a silly warning with some compilers, GitHub issue 2573. --- job.c | 8 ++++---- spawn.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/job.c b/job.c index efc0199e..6267336b 100644 --- a/job.c +++ b/job.c @@ -114,10 +114,10 @@ job_run(const char *cmd, struct session *s, const char *cwd, proc_clear_signals(server_proc, 1); sigprocmask(SIG_SETMASK, &oldset, NULL); - if (cwd == NULL || chdir(cwd) != 0) { - if ((home = find_home()) == NULL || chdir(home) != 0) - chdir("/"); - } + if ((cwd == NULL || chdir(cwd) != 0) && + ((home = find_home()) == NULL || chdir(home) != 0) && + chdir("/") != 0) + fatal("chdir failed"); environ_push(env); environ_free(env); diff --git a/spawn.c b/spawn.c index d5b52ffa..41ffe612 100644 --- a/spawn.c +++ b/spawn.c @@ -379,10 +379,10 @@ spawn_pane(struct spawn_context *sc, char **cause) * Child process. Change to the working directory or home if that * fails. */ - if (chdir(new_wp->cwd) != 0) { - if ((tmp = find_home()) == NULL || chdir(tmp) != 0) - chdir("/"); - } + if (chdir(new_wp->cwd) != 0 && + ((tmp = find_home()) == NULL || chdir(tmp) != 0) && + chdir("/") != 0) + fatal("chdir failed"); /* * Update terminal escape characters from the session if available and From 8986c8dfcd0083e5c767b8a247c119a25e1f8093 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 22 Feb 2021 06:53:04 +0000 Subject: [PATCH 0708/1006] Move jump commands to grid reader, make them UTF-8 aware, and tidy up, from Anindya Mukherjee. --- grid-reader.c | 62 +++++++++ status.c | 12 +- tmux.h | 4 + window-copy.c | 344 +++++++++++++++++++------------------------------- 4 files changed, 206 insertions(+), 216 deletions(-) diff --git a/grid-reader.c b/grid-reader.c index a1af3aaa..c011ea1d 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -17,6 +17,7 @@ */ #include "tmux.h" +#include /* Initialise virtual cursor. */ void @@ -301,3 +302,64 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, gr->cx = oldx; gr->cy = oldy; } + +/* Jump forward to character. */ +int +grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc) +{ + struct grid_cell gc; + u_int px, py, xx, yy; + + px = gr->cx; + yy = gr->gd->hsize + gr->gd->sy - 1; + + for (py = gr->cy; py <= yy; py++) { + xx = grid_line_length(gr->gd, py); + while (px < xx) { + grid_get_cell(gr->gd, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == jc->size && + memcmp(gc.data.data, jc->data, gc.data.size) == 0) { + gr->cx = px; + gr->cy = py; + return 1; + } + px++; + } + + if (py == yy || + !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)) + return 0; + px = 0; + } + return 0; +} + +/* Jump back to character. */ +int +grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc) +{ + struct grid_cell gc; + u_int px, py, xx; + + xx = gr->cx + 1; + + for (py = gr->cy + 1; py > 0; py--) { + for (px = xx; px > 0; px--) { + grid_get_cell(gr->gd, px - 1, py - 1, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == jc->size && + memcmp(gc.data.data, jc->data, gc.data.size) == 0) { + gr->cx = px - 1; + gr->cy = py - 1; + return 1; + } + } + + if (py == 1 || + !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED)) + return 0; + xx = grid_line_length(gr->gd, py - 2); + } + return 0; +} diff --git a/status.c b/status.c index 82107c58..154d9452 100644 --- a/status.c +++ b/status.c @@ -1319,12 +1319,14 @@ append_key: } if (c->prompt_flags & PROMPT_SINGLE) { - s = utf8_tocstr(c->prompt_buffer); - if (strlen(s) != 1) + if (utf8_strlen(c->prompt_buffer) != 1) status_prompt_clear(c); - else if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) - status_prompt_clear(c); - free(s); + else { + s = utf8_tocstr(c->prompt_buffer); + if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) + status_prompt_clear(c); + free(s); + } } changed: diff --git a/tmux.h b/tmux.h index 88c6d30e..4339f6a7 100644 --- a/tmux.h +++ b/tmux.h @@ -2589,6 +2589,10 @@ void grid_reader_cursor_next_word(struct grid_reader *, const char *); void grid_reader_cursor_next_word_end(struct grid_reader *, const char *); void grid_reader_cursor_previous_word(struct grid_reader *, const char *, int); +int grid_reader_cursor_jump(struct grid_reader *, + const struct utf8_data *); +int grid_reader_cursor_jump_back(struct grid_reader *, + const struct utf8_data *); /* grid-view.c */ void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); diff --git a/window-copy.c b/window-copy.c index c6b25413..af8d2937 100644 --- a/window-copy.c +++ b/window-copy.c @@ -135,6 +135,10 @@ static void window_copy_move_mouse(struct mouse_event *); static void window_copy_drag_update(struct client *, struct mouse_event *); static void window_copy_drag_release(struct client *, struct mouse_event *); static void window_copy_jump_to_mark(struct window_mode_entry *); +static void window_copy_acquire_cursor_up(struct window_mode_entry *, + u_int, u_int, u_int, u_int, u_int); +static void window_copy_acquire_cursor_down(struct window_mode_entry *, + u_int, u_int, u_int, u_int, u_int, u_int, int); const struct window_mode window_copy_mode = { .name = "copy-mode", @@ -284,8 +288,8 @@ struct window_copy_mode_data { #define WINDOW_COPY_SEARCH_TIMEOUT 10000 #define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200 - int jumptype; - char jumpchar; + int jumptype; + struct utf8_data *jumpchar; struct event dragtimer; #define WINDOW_COPY_DRAG_REPEAT_TIME 50000 @@ -401,7 +405,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->searchall = 1; data->jumptype = WINDOW_COPY_OFF; - data->jumpchar = '\0'; + data->jumpchar = NULL; screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0); data->modekeys = options_get_number(wp->window->options, "mode-keys"); @@ -482,6 +486,7 @@ window_copy_free(struct window_mode_entry *wme) free(data->searchmark); free(data->searchstr); + free(data->jumpchar); screen_free(data->backing); free(data->backing); @@ -1935,7 +1940,8 @@ window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs) if (*argument != '\0') { data->jumptype = WINDOW_COPY_JUMPBACKWARD; - data->jumpchar = *argument; + free(data->jumpchar); + data->jumpchar = utf8_fromcstr(argument); for (; np != 0; np--) window_copy_cursor_jump_back(wme); } @@ -1952,7 +1958,8 @@ window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs) if (*argument != '\0') { data->jumptype = WINDOW_COPY_JUMPFORWARD; - data->jumpchar = *argument; + free(data->jumpchar); + data->jumpchar = utf8_fromcstr(argument); for (; np != 0; np--) window_copy_cursor_jump(wme); } @@ -1969,7 +1976,8 @@ window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs) if (*argument != '\0') { data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; - data->jumpchar = *argument; + free(data->jumpchar); + data->jumpchar = utf8_fromcstr(argument); for (; np != 0; np--) window_copy_cursor_jump_to_back(wme); } @@ -1986,7 +1994,8 @@ window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) if (*argument != '\0') { data->jumptype = WINDOW_COPY_JUMPTOFORWARD; - data->jumpchar = *argument; + free(data->jumpchar); + data->jumpchar = utf8_fromcstr(argument); for (; np != 0; np--) window_copy_cursor_jump_to(wme); } @@ -4074,7 +4083,7 @@ window_copy_cursor_start_of_line(struct window_mode_entry *wme) struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, oldy, yy, ny, nd, hsize; + u_int px, py, oldy, hsize; px = data->cx; hsize = screen_hsize(back_s); @@ -4084,25 +4093,7 @@ window_copy_cursor_start_of_line(struct window_mode_entry *wme) grid_reader_start(&gr, back_s->grid, px, py); grid_reader_cursor_start_of_line(&gr, 1); grid_reader_get_cursor(&gr, &px, &py); - - /* Scroll up if we went off the visible screen. */ - yy = hsize - data->oy; - if (py < yy) { - ny = yy - py; - cy = 0; - nd = 1; - } else { - ny = 0; - cy = py - yy; - nd = oldy - cy + 1; - } - while (ny > 0) { - window_copy_cursor_up(wme, 1); - ny--; - } - window_copy_update_cursor(wme, px, cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, nd); + window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } static void @@ -4134,7 +4125,7 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme) struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, oldy, yy, ny, nd, hsize; + u_int px, py, oldy, hsize; px = data->cx; hsize = screen_hsize(back_s); @@ -4147,28 +4138,8 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme) else grid_reader_cursor_end_of_line(&gr, 1, 0); grid_reader_get_cursor(&gr, &px, &py); - - /* Scroll down if we went off the visible screen. */ - cy = py - hsize + data->oy; - yy = screen_size_y(back_s) - 1; - if (cy > yy) { - ny = cy - yy; - oldy = yy; - nd = 1; - } else { - ny = 0; - nd = cy - oldy + 1; - } - while (ny > 0) { - window_copy_cursor_down(wme, 1); - ny--; - } - if (cy > yy) - window_copy_update_cursor(wme, px, yy); - else - window_copy_update_cursor(wme, px, cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, oldy, nd); + window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), + data->oy, oldy, px, py, 0); } static void @@ -4228,32 +4199,17 @@ window_copy_cursor_left(struct window_mode_entry *wme) struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, yy, ny, hsize; + u_int px, py, oldy, hsize; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; + oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); grid_reader_cursor_left(&gr); grid_reader_get_cursor(&gr, &px, &py); - - /* Scroll up if we went off the visible screen. */ - yy = hsize - data->oy; - if (py < yy) { - ny = yy - py; - cy = 0; - } else { - ny = 0; - cy = py - yy; - } - while (ny > 0) { - window_copy_cursor_up(wme, 1); - ny--; - } - window_copy_update_cursor(wme, px, cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); + window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } static void @@ -4262,33 +4218,18 @@ window_copy_cursor_right(struct window_mode_entry *wme, int all) struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, yy, ny, hsize; + u_int px, py, oldy, hsize; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; + oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); grid_reader_cursor_right(&gr, 1, all); grid_reader_get_cursor(&gr, &px, &py); - - /* Scroll down if we went off the visible screen. */ - cy = py - hsize + data->oy; - yy = screen_size_y(back_s) - 1; - if (cy > yy) - ny = cy - yy; - else - ny = 0; - while (ny > 0) { - window_copy_cursor_down(wme, 1); - ny--; - } - if (cy > yy) - window_copy_update_cursor(wme, px, yy); - else - window_copy_update_cursor(wme, px, cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); + window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), + data->oy, oldy, px, py, 0); } static void @@ -4422,23 +4363,19 @@ window_copy_cursor_jump(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; - struct grid_cell gc; - u_int px, py, xx; + struct grid_reader gr; + u_int px, py, oldy, hsize; px = data->cx + 1; - py = screen_hsize(back_s) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + oldy = data->cy; - while (px < xx) { - grid_get_cell(back_s->grid, px, py, &gc); - if (!(gc.flags & GRID_FLAG_PADDING) && - gc.data.size == 1 && *gc.data.data == data->jumpchar) { - window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); - return; - } - px++; + grid_reader_start(&gr, back_s->grid, px, py); + if (grid_reader_cursor_jump(&gr, data->jumpchar)) { + grid_reader_get_cursor(&gr, &px, &py); + window_copy_acquire_cursor_down(wme, hsize, + screen_size_y(back_s), data->oy, oldy, px, py, 0); } } @@ -4447,27 +4384,22 @@ window_copy_cursor_jump_back(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; - struct grid_cell gc; - u_int px, py; + struct grid_reader gr; + u_int px, py, oldy, hsize; px = data->cx; - py = screen_hsize(back_s) + data->cy - data->oy; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + oldy = data->cy; if (px > 0) px--; - for (;;) { - grid_get_cell(back_s->grid, px, py, &gc); - if (!(gc.flags & GRID_FLAG_PADDING) && - gc.data.size == 1 && *gc.data.data == data->jumpchar) { - window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); - return; - } - if (px == 0) - break; - px--; + grid_reader_start(&gr, back_s->grid, px, py); + if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { + grid_reader_get_cursor(&gr, &px, &py); + window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, + py); } } @@ -4476,23 +4408,20 @@ window_copy_cursor_jump_to(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; - struct grid_cell gc; - u_int px, py, xx; + struct grid_reader gr; + u_int px, py, oldy, hsize; px = data->cx + 2; - py = screen_hsize(back_s) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + oldy = data->cy; - while (px < xx) { - grid_get_cell(back_s->grid, px, py, &gc); - if (!(gc.flags & GRID_FLAG_PADDING) && - gc.data.size == 1 && *gc.data.data == data->jumpchar) { - window_copy_update_cursor(wme, px - 1, data->cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); - return; - } - px++; + grid_reader_start(&gr, back_s->grid, px, py); + if (grid_reader_cursor_jump(&gr, data->jumpchar)) { + grid_reader_cursor_left(&gr); + grid_reader_get_cursor(&gr, &px, &py); + window_copy_acquire_cursor_down(wme, hsize, + screen_size_y(back_s), data->oy, oldy, px, py, 0); } } @@ -4501,11 +4430,13 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; - struct grid_cell gc; - u_int px, py; + struct grid_reader gr; + u_int px, py, oldy, hsize; px = data->cx; - py = screen_hsize(back_s) + data->cy - data->oy; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + oldy = data->cy; if (px > 0) px--; @@ -4513,18 +4444,12 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme) if (px > 0) px--; - for (;;) { - grid_get_cell(back_s->grid, px, py, &gc); - if (!(gc.flags & GRID_FLAG_PADDING) && - gc.data.size == 1 && *gc.data.data == data->jumpchar) { - window_copy_update_cursor(wme, px + 1, data->cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); - return; - } - if (px == 0) - break; - px--; + grid_reader_start(&gr, back_s->grid, px, py); + if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { + grid_reader_cursor_right(&gr, 1, 0); + grid_reader_get_cursor(&gr, &px, &py); + window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, + py); } } @@ -4535,7 +4460,7 @@ window_copy_cursor_next_word(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, oldy, yy, ny, nd, hsize; + u_int px, py, oldy, hsize; px = data->cx; hsize = screen_hsize(back_s); @@ -4545,28 +4470,8 @@ window_copy_cursor_next_word(struct window_mode_entry *wme, grid_reader_start(&gr, back_s->grid, px, py); grid_reader_cursor_next_word(&gr, separators); grid_reader_get_cursor(&gr, &px, &py); - - /* Scroll down if we went off the visible screen. */ - cy = py - hsize + data->oy; - yy = screen_size_y(back_s) - 1; - if (cy > yy) { - ny = cy - yy; - oldy = yy; - nd = 1; - } else { - ny = 0; - nd = cy - oldy + 1; - } - while (ny > 0) { - window_copy_cursor_down(wme, 1); - ny--; - } - if (cy > yy) - window_copy_update_cursor(wme, px, yy); - else - window_copy_update_cursor(wme, px, cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, oldy, nd); + window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), + data->oy, oldy, px, py, 0); } static void @@ -4627,7 +4532,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, struct options *oo = wp->window->options; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, oldy, yy, ny, nd, hsize; + u_int px, py, oldy, hsize; int keys; px = data->cx; @@ -4643,28 +4548,8 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, if (keys == MODEKEY_VI) grid_reader_cursor_left(&gr); grid_reader_get_cursor(&gr, &px, &py); - - /* Scroll down if we went off the visible screen. */ - cy = py - hsize + data->oy; - yy = screen_size_y(back_s) - 1; - if (cy > yy) { - ny = cy - yy; - oldy = yy; - nd = 1; - } else { - ny = 0; - nd = cy - oldy + 1; - } - while (ny > 0) { - window_copy_cursor_down(wme, 1); - ny--; - } - if (cy > yy) - window_copy_update_cursor(wme, px, yy); - else - window_copy_update_cursor(wme, px, cy); - if (window_copy_update_selection(wme, 1, no_reset)) - window_copy_redraw_lines(wme, oldy, nd); + window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), + data->oy, oldy, px, py, no_reset); } /* Compute the previous place where a word begins. */ @@ -4721,7 +4606,7 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_reader gr; - u_int px, py, cy, oldy, yy, ny, nd, hsize; + u_int px, py, oldy, hsize; px = data->cx; hsize = screen_hsize(back_s); @@ -4731,25 +4616,7 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, grid_reader_start(&gr, back_s->grid, px, py); grid_reader_cursor_previous_word(&gr, separators, already); grid_reader_get_cursor(&gr, &px, &py); - - /* Scroll up if we went off the visible screen. */ - yy = hsize - data->oy; - if (py < yy) { - ny = yy - py; - cy = 0; - nd = 1; - } else { - ny = 0; - cy = py - yy; - nd = oldy - cy + 1; - } - while (ny > 0) { - window_copy_cursor_up(wme, 1); - ny--; - } - window_copy_update_cursor(wme, px, cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, nd); + window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } static void @@ -5000,3 +4867,58 @@ window_copy_jump_to_mark(struct window_mode_entry *wme) window_copy_update_selection(wme, 0, 0); window_copy_redraw_screen(wme); } + +/* Scroll up if the cursor went off the visible screen. */ +static void +window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize, + u_int oy, u_int oldy, u_int px, u_int py) +{ + u_int cy, yy, ny, nd; + + yy = hsize - oy; + if (py < yy) { + ny = yy - py; + cy = 0; + nd = 1; + } else { + ny = 0; + cy = py - yy; + nd = oldy - cy + 1; + } + while (ny > 0) { + window_copy_cursor_up(wme, 1); + ny--; + } + window_copy_update_cursor(wme, px, cy); + if (window_copy_update_selection(wme, 1, 0)) + window_copy_redraw_lines(wme, cy, nd); +} + +/* Scroll down if the cursor went off the visible screen. */ +static void +window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize, + u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset) +{ + u_int cy, yy, ny, nd; + + cy = py - hsize + oy; + yy = sy - 1; + if (cy > yy) { + ny = cy - yy; + oldy = yy; + nd = 1; + } else { + ny = 0; + nd = cy - oldy + 1; + } + while (ny > 0) { + window_copy_cursor_down(wme, 1); + ny--; + } + if (cy > yy) + window_copy_update_cursor(wme, px, yy); + else + window_copy_update_cursor(wme, px, cy); + if (window_copy_update_selection(wme, 1, no_reset)) + window_copy_redraw_lines(wme, oldy, nd); +} From e858270006a9041b9016ed9e6cc12d622ac8fe31 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 22 Feb 2021 07:09:06 +0000 Subject: [PATCH 0709/1006] There are many format variables now so allocating all the default ones each time a tree is created is too expensive. Instead, convert them all into callbacks and put them in a static table so they only allocate on demand. The tree remains for the moment for extra (non-default) variables added by for example copy mode or popups. Also reduce expensive calls to localtime_r/strftime. GitHub issue 2253. --- format.c | 2509 ++++++++++++++++++++++++++++++++++++++++--------- tmux.h | 3 +- window-copy.c | 6 +- window.c | 11 - 4 files changed, 2050 insertions(+), 479 deletions(-) diff --git a/format.c b/format.c index 47e0bb29..014f1385 100644 --- a/format.c +++ b/format.c @@ -119,13 +119,23 @@ struct format_entry { RB_ENTRY(format_entry) entry; }; -/* Format entry tree. */ +/* Format type. */ +enum format_type { + FORMAT_TYPE_UNKNOWN, + FORMAT_TYPE_SESSION, + FORMAT_TYPE_WINDOW, + FORMAT_TYPE_PANE +}; + struct format_tree { + enum format_type type; + struct client *c; struct session *s; struct winlink *wl; struct window *w; struct window_pane *wp; + struct paste_buffer *pb; struct cmdq_item *item; struct client *client; @@ -144,6 +154,7 @@ struct format_expand_state { struct format_tree *ft; u_int loop; time_t time; + struct tm tm; int flags; }; @@ -263,6 +274,7 @@ format_copy_state(struct format_expand_state *to, to->ft = from->ft; to->loop = from->loop; to->time = from->time; + memcpy(&to->tm, &from->tm, sizeof to->tm); to->flags = from->flags|flags; } @@ -451,8 +463,21 @@ format_job_timer(__unused int fd, __unused short events, __unused void *arg) evtimer_add(&format_job_event, &tv); } +/* Wrapper for asprintf. */ +static char * printflike(1, 2) +format_printf(const char *fmt, ...) +{ + va_list ap; + char *s; + + va_start(ap, fmt); + xvasprintf(&s, fmt, ap); + va_end(ap); + return (s); +} + /* Callback for host. */ -static char * +static void * format_cb_host(__unused struct format_tree *ft) { char host[HOST_NAME_MAX + 1]; @@ -463,7 +488,7 @@ format_cb_host(__unused struct format_tree *ft) } /* Callback for host_short. */ -static char * +static void * format_cb_host_short(__unused struct format_tree *ft) { char host[HOST_NAME_MAX + 1], *cp; @@ -476,7 +501,7 @@ format_cb_host_short(__unused struct format_tree *ft) } /* Callback for pid. */ -static char * +static void * format_cb_pid(__unused struct format_tree *ft) { char *value; @@ -486,7 +511,7 @@ format_cb_pid(__unused struct format_tree *ft) } /* Callback for session_attached_list. */ -static char * +static void * format_cb_session_attached_list(struct format_tree *ft) { struct session *s = ft->s; @@ -517,7 +542,7 @@ format_cb_session_attached_list(struct format_tree *ft) } /* Callback for session_alerts. */ -static char * +static void * format_cb_session_alerts(struct format_tree *ft) { struct session *s = ft->s; @@ -547,7 +572,7 @@ format_cb_session_alerts(struct format_tree *ft) } /* Callback for session_stack. */ -static char * +static void * format_cb_session_stack(struct format_tree *ft) { struct session *s = ft->s; @@ -569,14 +594,18 @@ format_cb_session_stack(struct format_tree *ft) } /* Callback for window_stack_index. */ -static char * +static void * format_cb_window_stack_index(struct format_tree *ft) { - struct session *s = ft->wl->session; + struct session *s; struct winlink *wl; u_int idx; char *value = NULL; + if (ft->wl == NULL) + return (NULL); + s = ft->wl->session; + idx = 0; TAILQ_FOREACH(wl, &s->lastw, sentry) { idx++; @@ -590,15 +619,19 @@ format_cb_window_stack_index(struct format_tree *ft) } /* Callback for window_linked_sessions_list. */ -static char * +static void * format_cb_window_linked_sessions_list(struct format_tree *ft) { - struct window *w = ft->wl->window; + struct window *w; struct winlink *wl; struct evbuffer *buffer; int size; char *value = NULL; + if (ft->wl == NULL) + return (NULL); + w = ft->wl->window; + buffer = evbuffer_new(); if (buffer == NULL) fatalx("out of memory"); @@ -616,14 +649,18 @@ format_cb_window_linked_sessions_list(struct format_tree *ft) } /* Callback for window_active_sessions. */ -static char * +static void * format_cb_window_active_sessions(struct format_tree *ft) { - struct window *w = ft->wl->window; + struct window *w; struct winlink *wl; u_int n = 0; char *value; + if (ft->wl == NULL) + return (NULL); + w = ft->wl->window; + TAILQ_FOREACH(wl, &w->winlinks, wentry) { if (wl->session->curw == wl) n++; @@ -634,15 +671,19 @@ format_cb_window_active_sessions(struct format_tree *ft) } /* Callback for window_active_sessions_list. */ -static char * +static void * format_cb_window_active_sessions_list(struct format_tree *ft) { - struct window *w = ft->wl->window; + struct window *w; struct winlink *wl; struct evbuffer *buffer; int size; char *value = NULL; + if (ft->wl == NULL) + return (NULL); + w = ft->wl->window; + buffer = evbuffer_new(); if (buffer == NULL) fatalx("out of memory"); @@ -662,15 +703,19 @@ format_cb_window_active_sessions_list(struct format_tree *ft) } /* Callback for window_active_clients. */ -static char * +static void * format_cb_window_active_clients(struct format_tree *ft) { - struct window *w = ft->wl->window; + struct window *w; struct client *loop; struct session *client_session; u_int n = 0; char *value; + if (ft->wl == NULL) + return (NULL); + w = ft->wl->window; + TAILQ_FOREACH(loop, &clients, entry) { client_session = loop->session; if (client_session == NULL) @@ -685,16 +730,20 @@ format_cb_window_active_clients(struct format_tree *ft) } /* Callback for window_active_clients_list. */ -static char * +static void * format_cb_window_active_clients_list(struct format_tree *ft) { - struct window *w = ft->wl->window; + struct window *w; struct client *loop; struct session *client_session; struct evbuffer *buffer; int size; char *value = NULL; + if (ft->wl == NULL) + return (NULL); + w = ft->wl->window; + buffer = evbuffer_new(); if (buffer == NULL) fatalx("out of memory"); @@ -718,7 +767,7 @@ format_cb_window_active_clients_list(struct format_tree *ft) } /* Callback for window_layout. */ -static char * +static void * format_cb_window_layout(struct format_tree *ft) { struct window *w = ft->w; @@ -732,7 +781,7 @@ format_cb_window_layout(struct format_tree *ft) } /* Callback for window_visible_layout. */ -static char * +static void * format_cb_window_visible_layout(struct format_tree *ft) { struct window *w = ft->w; @@ -744,7 +793,7 @@ format_cb_window_visible_layout(struct format_tree *ft) } /* Callback for pane_start_command. */ -static char * +static void * format_cb_start_command(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -756,7 +805,7 @@ format_cb_start_command(struct format_tree *ft) } /* Callback for pane_current_command. */ -static char * +static void * format_cb_current_command(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -780,7 +829,7 @@ format_cb_current_command(struct format_tree *ft) } /* Callback for pane_current_path. */ -static char * +static void * format_cb_current_path(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -796,7 +845,7 @@ format_cb_current_path(struct format_tree *ft) } /* Callback for history_bytes. */ -static char * +static void * format_cb_history_bytes(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -822,7 +871,7 @@ format_cb_history_bytes(struct format_tree *ft) } /* Callback for history_all_bytes. */ -static char * +static void * format_cb_history_all_bytes(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -849,7 +898,7 @@ format_cb_history_all_bytes(struct format_tree *ft) } /* Callback for pane_tabs. */ -static char * +static void * format_cb_pane_tabs(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -879,7 +928,7 @@ format_cb_pane_tabs(struct format_tree *ft) } /* Callback for pane_fg. */ -static char * +static void * format_cb_pane_fg(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -890,7 +939,7 @@ format_cb_pane_fg(struct format_tree *ft) } /* Callback for pane_bg. */ -static char * +static void * format_cb_pane_bg(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -901,7 +950,7 @@ format_cb_pane_bg(struct format_tree *ft) } /* Callback for session_group_list. */ -static char * +static void * format_cb_session_group_list(struct format_tree *ft) { struct session *s = ft->s; @@ -934,7 +983,7 @@ format_cb_session_group_list(struct format_tree *ft) } /* Callback for session_group_attached_list. */ -static char * +static void * format_cb_session_group_attached_list(struct format_tree *ft) { struct session *s = ft->s, *client_session, *session_loop; @@ -974,7 +1023,7 @@ format_cb_session_group_attached_list(struct format_tree *ft) } /* Callback for pane_in_mode. */ -static char * +static void * format_cb_pane_in_mode(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -986,13 +1035,13 @@ format_cb_pane_in_mode(struct format_tree *ft) return (NULL); TAILQ_FOREACH(wme, &wp->modes, entry) - n++; + n++; xasprintf(&value, "%u", n); return (value); } /* Callback for pane_at_top. */ -static char * +static void * format_cb_pane_at_top(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -1014,7 +1063,7 @@ format_cb_pane_at_top(struct format_tree *ft) } /* Callback for pane_at_bottom. */ -static char * +static void * format_cb_pane_at_bottom(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -1036,7 +1085,7 @@ format_cb_pane_at_bottom(struct format_tree *ft) } /* Callback for cursor_character. */ -static char * +static void * format_cb_cursor_character(struct format_tree *ft) { struct window_pane *wp = ft->wp; @@ -1052,78 +1101,8 @@ format_cb_cursor_character(struct format_tree *ft) return (value); } -/* Return word at given coordinates. Caller frees. */ -char * -format_grid_word(struct grid *gd, u_int x, u_int y) -{ - const struct grid_line *gl; - struct grid_cell gc; - const char *ws; - struct utf8_data *ud = NULL; - u_int end; - size_t size = 0; - int found = 0; - char *s = NULL; - - ws = options_get_string(global_s_options, "word-separators"); - - for (;;) { - grid_get_cell(gd, x, y, &gc); - if (gc.flags & GRID_FLAG_PADDING) - break; - if (utf8_cstrhas(ws, &gc.data)) { - found = 1; - break; - } - - if (x == 0) { - if (y == 0) - break; - gl = grid_peek_line(gd, y - 1); - if (~gl->flags & GRID_LINE_WRAPPED) - break; - y--; - x = grid_line_length(gd, y); - if (x == 0) - break; - } - x--; - } - for (;;) { - if (found) { - end = grid_line_length(gd, y); - if (end == 0 || x == end - 1) { - if (y == gd->hsize + gd->sy - 1) - break; - gl = grid_peek_line(gd, y); - if (~gl->flags & GRID_LINE_WRAPPED) - break; - y++; - x = 0; - } else - x++; - } - found = 1; - - grid_get_cell(gd, x, y, &gc); - if (gc.flags & GRID_FLAG_PADDING) - break; - if (utf8_cstrhas(ws, &gc.data)) - break; - - ud = xreallocarray(ud, size + 2, sizeof *ud); - memcpy(&ud[size++], &gc.data, sizeof *ud); - } - if (size != 0) { - ud[size].size = 0; - s = utf8_tocstr(ud); - free(ud); - } - return (s); -} - /* Callback for mouse_word. */ -static char * +static void * format_cb_mouse_word(struct format_tree *ft) { struct window_pane *wp; @@ -1149,34 +1128,8 @@ format_cb_mouse_word(struct format_tree *ft) return (format_grid_word(gd, x, gd->hsize + y)); } -/* Return line at given coordinates. Caller frees. */ -char * -format_grid_line(struct grid *gd, u_int y) -{ - struct grid_cell gc; - struct utf8_data *ud = NULL; - u_int x; - size_t size = 0; - char *s = NULL; - - for (x = 0; x < grid_line_length(gd, y); x++) { - grid_get_cell(gd, x, y, &gc); - if (gc.flags & GRID_FLAG_PADDING) - break; - - ud = xreallocarray(ud, size + 2, sizeof *ud); - memcpy(&ud[size++], &gc.data, sizeof *ud); - } - if (size != 0) { - ud[size].size = 0; - s = utf8_tocstr(ud); - free(ud); - } - return (s); -} - /* Callback for mouse_line. */ -static char * +static void * format_cb_mouse_line(struct format_tree *ft) { struct window_pane *wp; @@ -1201,6 +1154,1810 @@ format_cb_mouse_line(struct format_tree *ft) return (format_grid_line(gd, gd->hsize + y)); } +/* Callback for alternate_on. */ +static void * +format_cb_alternate_on(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.saved_grid != NULL) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for alternate_saved_x. */ +static void * +format_cb_alternate_saved_x(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->base.saved_cx)); + return (NULL); +} + +/* Callback for alternate_saved_y. */ +static void * +format_cb_alternate_saved_y(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->base.saved_cy)); + return (NULL); +} + +/* Callback for buffer_name. */ +static void * +format_cb_buffer_name(struct format_tree *ft) +{ + if (ft->pb != NULL) + return (xstrdup(paste_buffer_name(ft->pb))); + return (NULL); +} + +/* Callback for buffer_sample. */ +static void * +format_cb_buffer_sample(struct format_tree *ft) +{ + if (ft->pb != NULL) + return (paste_make_sample(ft->pb)); + return (NULL); +} + +/* Callback for buffer_size. */ +static void * +format_cb_buffer_size(struct format_tree *ft) +{ + size_t size; + + if (ft->pb != NULL) { + paste_buffer_data(ft->pb, &size); + return (format_printf("%zu", size)); + } + return (NULL); +} + +/* Callback for client_cell_height. */ +static void * +format_cb_client_cell_height(struct format_tree *ft) +{ + if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) + return (format_printf("%u", ft->c->tty.ypixel)); + return (NULL); +} + +/* Callback for client_cell_width. */ +static void * +format_cb_client_cell_width(struct format_tree *ft) +{ + if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) + return (format_printf("%u", ft->c->tty.xpixel)); + return (NULL); +} + +/* Callback for client_control_mode. */ +static void * +format_cb_client_control_mode(struct format_tree *ft) +{ + if (ft->c != NULL) { + if (ft->c->flags & CLIENT_CONTROL) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for client_discarded. */ +static void * +format_cb_client_discarded(struct format_tree *ft) +{ + if (ft->c != NULL) + return (format_printf("%zu", ft->c->discarded)); + return (NULL); +} + +/* Callback for client_flags. */ +static void * +format_cb_client_flags(struct format_tree *ft) +{ + if (ft->c != NULL) + return (xstrdup(server_client_get_flags(ft->c))); + return (NULL); +} + +/* Callback for client_height. */ +static void * +format_cb_client_height(struct format_tree *ft) +{ + if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) + return (format_printf("%u", ft->c->tty.sy)); + return (NULL); +} + +/* Callback for client_key_table. */ +static void * +format_cb_client_key_table(struct format_tree *ft) +{ + if (ft->c != NULL) + return (xstrdup(ft->c->keytable->name)); + return (NULL); +} + +/* Callback for client_last_session. */ +static void * +format_cb_client_last_session(struct format_tree *ft) +{ + if (ft->c != NULL && + ft->c->last_session != NULL && + session_alive(ft->c->last_session)) + return (xstrdup(ft->c->last_session->name)); + return (NULL); +} + +/* Callback for client_name. */ +static void * +format_cb_client_name(struct format_tree *ft) +{ + if (ft->c != NULL) + return (xstrdup(ft->c->name)); + return (NULL); +} + +/* Callback for client_pid. */ +static void * +format_cb_client_pid(struct format_tree *ft) +{ + if (ft->c != NULL) + return (format_printf("%ld", (long)ft->c->pid)); + return (NULL); +} + +/* Callback for client_prefix. */ +static void * +format_cb_client_prefix(struct format_tree *ft) +{ + const char *name; + + if (ft->c != NULL) { + name = server_client_get_key_table(ft->c); + if (strcmp(ft->c->keytable->name, name) == 0) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for client_readonly. */ +static void * +format_cb_client_readonly(struct format_tree *ft) +{ + if (ft->c != NULL) { + if (ft->c->flags & CLIENT_READONLY) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for client_session. */ +static void * +format_cb_client_session(struct format_tree *ft) +{ + if (ft->c != NULL && ft->c->session != NULL) + return (xstrdup(ft->c->session->name)); + return (NULL); +} + +/* Callback for client_termfeatures. */ +static void * +format_cb_client_termfeatures(struct format_tree *ft) +{ + if (ft->c != NULL) + return (xstrdup(tty_get_features(ft->c->term_features))); + return (NULL); +} + +/* Callback for client_termname. */ +static void * +format_cb_client_termname(struct format_tree *ft) +{ + if (ft->c != NULL) + return (xstrdup(ft->c->term_name)); + return (NULL); +} + +/* Callback for client_termtype. */ +static void * +format_cb_client_termtype(struct format_tree *ft) +{ + if (ft->c != NULL) + return (xstrdup(ft->c->term_type)); + return (NULL); +} + +/* Callback for client_tty. */ +static void * +format_cb_client_tty(struct format_tree *ft) +{ + if (ft->c != NULL) + return (xstrdup(ft->c->ttyname)); + return (NULL); +} + +/* Callback for client_utf8. */ +static void * +format_cb_client_utf8(struct format_tree *ft) +{ + if (ft->c != NULL) { + if (ft->c->flags & CLIENT_UTF8) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for client_width. */ +static void * +format_cb_client_width(struct format_tree *ft) +{ + if (ft->c != NULL) + return (format_printf("%u", ft->c->tty.sx)); + return (NULL); +} + +/* Callback for client_written. */ +static void * +format_cb_client_written(struct format_tree *ft) +{ + if (ft->c != NULL) + return (format_printf("%zu", ft->c->written)); + return (NULL); +} + +/* Callback for cursor_flag. */ +static void * +format_cb_cursor_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_CURSOR) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for cursor_x. */ +static void * +format_cb_cursor_x(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->base.cx)); + return (NULL); +} + +/* Callback for cursor_y. */ +static void * +format_cb_cursor_y(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->base.cy)); + return (NULL); +} + +/* Callback for history_limit. */ +static void * +format_cb_history_limit(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->base.grid->hlimit)); + return (NULL); +} + +/* Callback for history_size. */ +static void * +format_cb_history_size(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->base.grid->hsize)); + return (NULL); +} + +/* Callback for insert_flag. */ +static void * +format_cb_insert_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_INSERT) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for keypad_cursor_flag. */ +static void * +format_cb_keypad_cursor_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_KCURSOR) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for keypad_flag. */ +static void * +format_cb_keypad_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_KKEYPAD) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for mouse_all_flag. */ +static void * +format_cb_mouse_all_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_MOUSE_ALL) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for mouse_any_flag. */ +static void * +format_cb_mouse_any_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & ALL_MOUSE_MODES) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for mouse_button_flag. */ +static void * +format_cb_mouse_button_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_MOUSE_BUTTON) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for mouse_pane. */ +static void * +format_cb_mouse_pane(struct format_tree *ft) +{ + struct window_pane *wp; + + if (ft->m.valid) { + wp = cmd_mouse_pane(&ft->m, NULL, NULL); + if (wp != NULL) + return (format_printf("%%%u", wp->id)); + return (NULL); + } + return (NULL); +} + +/* Callback for mouse_sgr_flag. */ +static void * +format_cb_mouse_sgr_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_MOUSE_SGR) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for mouse_standard_flag. */ +static void * +format_cb_mouse_standard_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_MOUSE_STANDARD) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for mouse_utf8_flag. */ +static void * +format_cb_mouse_utf8_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_MOUSE_UTF8) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for mouse_x. */ +static void * +format_cb_mouse_x(struct format_tree *ft) +{ + struct window_pane *wp; + u_int x, y; + + if (ft->m.valid) { + wp = cmd_mouse_pane(&ft->m, NULL, NULL); + if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0) + return (format_printf("%u", x)); + return (NULL); + } + return (NULL); +} + +/* Callback for mouse_y. */ +static void * +format_cb_mouse_y(struct format_tree *ft) +{ + struct window_pane *wp; + u_int x, y; + + if (ft->m.valid) { + wp = cmd_mouse_pane(&ft->m, NULL, NULL); + if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0) + return (format_printf("%u", y)); + return (NULL); + } + return (NULL); +} + +/* Callback for origin_flag. */ +static void * +format_cb_origin_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_ORIGIN) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_active. */ +static void * +format_cb_pane_active(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp == ft->wp->window->active) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_at_left. */ +static void * +format_cb_pane_at_left(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->xoff == 0) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_at_right. */ +static void * +format_cb_pane_at_right(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->xoff + ft->wp->sx == ft->wp->window->sx) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_bottom. */ +static void * +format_cb_pane_bottom(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->yoff + ft->wp->sy - 1)); + return (NULL); +} + +/* Callback for pane_dead. */ +static void * +format_cb_pane_dead(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->fd == -1) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_dead_status. */ +static void * +format_cb_pane_dead_status(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + + if (wp != NULL) { + if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(wp->status)) + return (format_printf("%d", WEXITSTATUS(wp->status))); + return (NULL); + } + return (NULL); +} + +/* Callback for pane_format. */ +static void * +format_cb_pane_format(struct format_tree *ft) +{ + if (ft->type == FORMAT_TYPE_PANE) + return (xstrdup("1")); + return (xstrdup("0")); +} + +/* Callback for pane_height. */ +static void * +format_cb_pane_height(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->sy)); + return (NULL); +} + +/* Callback for pane_id. */ +static void * +format_cb_pane_id(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%%%u", ft->wp->id)); + return (NULL); +} + +/* Callback for pane_index. */ +static void * +format_cb_pane_index(struct format_tree *ft) +{ + u_int idx; + + if (ft->wp != NULL && window_pane_index(ft->wp, &idx) == 0) + return (format_printf("%u", idx)); + return (NULL); +} + +/* Callback for pane_input_off. */ +static void * +format_cb_pane_input_off(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->flags & PANE_INPUTOFF) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_last. */ +static void * +format_cb_pane_last(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp == ft->wp->window->last) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_left. */ +static void * +format_cb_pane_left(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->xoff)); + return (NULL); +} + +/* Callback for pane_marked. */ +static void * +format_cb_pane_marked(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (server_check_marked() && marked_pane.wp == ft->wp) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_marked_set. */ +static void * +format_cb_pane_marked_set(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (server_check_marked()) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_mode. */ +static void * +format_cb_pane_mode(struct format_tree *ft) +{ + struct window_mode_entry *wme; + + if (ft->wp != NULL) { + wme = TAILQ_FIRST(&ft->wp->modes); + if (wme != NULL) + return (xstrdup(wme->mode->name)); + return (NULL); + } + return (NULL); +} + +/* Callback for pane_path. */ +static void * +format_cb_pane_path(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.path == NULL) + return (xstrdup("")); + return (xstrdup(ft->wp->base.path)); + } + return (NULL); +} + +/* Callback for pane_pid. */ +static void * +format_cb_pane_pid(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%ld", (long)ft->wp->pid)); + return (NULL); +} + +/* Callback for pane_pipe. */ +static void * +format_cb_pane_pipe(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->pipe_fd != -1) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_right. */ +static void * +format_cb_pane_right(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->xoff + ft->wp->sx - 1)); + return (NULL); +} + +/* Callback for pane_search_string. */ +static void * +format_cb_pane_search_string(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->searchstr == NULL) + return (xstrdup("")); + return (xstrdup(ft->wp->searchstr)); + } + return (NULL); +} + +/* Callback for pane_synchronized. */ +static void * +format_cb_pane_synchronized(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (options_get_number(ft->wp->options, "synchronize-panes")) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_title. */ +static void * +format_cb_pane_title(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (xstrdup(ft->wp->base.title)); + return (NULL); +} + +/* Callback for pane_top. */ +static void * +format_cb_pane_top(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->yoff)); + return (NULL); +} + +/* Callback for pane_tty. */ +static void * +format_cb_pane_tty(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (xstrdup(ft->wp->tty)); + return (NULL); +} + +/* Callback for pane_width. */ +static void * +format_cb_pane_width(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->sx)); + return (NULL); +} + +/* Callback for scroll_region_lower. */ +static void * +format_cb_scroll_region_lower(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->base.rlower)); + return (NULL); +} + +/* Callback for scroll_region_upper. */ +static void * +format_cb_scroll_region_upper(struct format_tree *ft) +{ + if (ft->wp != NULL) + return (format_printf("%u", ft->wp->base.rupper)); + return (NULL); +} + +/* Callback for session_attached. */ +static void * +format_cb_session_attached(struct format_tree *ft) +{ + if (ft->s != NULL) + return (format_printf("%u", ft->s->attached)); + return (NULL); +} + +/* Callback for session_format. */ +static void * +format_cb_session_format(struct format_tree *ft) +{ + if (ft->type == FORMAT_TYPE_SESSION) + return (xstrdup("1")); + return (xstrdup("0")); +} + +/* Callback for session_group. */ +static void * +format_cb_session_group(struct format_tree *ft) +{ + struct session_group *sg; + + if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL) + return (xstrdup(sg->name)); + return (NULL); +} + +/* Callback for session_group_attached. */ +static void * +format_cb_session_group_attached(struct format_tree *ft) +{ + struct session_group *sg; + + if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL) + return (format_printf("%u", session_group_attached_count (sg))); + return (NULL); +} + +/* Callback for session_group_many_attached. */ +static void * +format_cb_session_group_many_attached(struct format_tree *ft) +{ + struct session_group *sg; + + if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL) { + if (session_group_attached_count (sg) > 1) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for session_group_size. */ +static void * +format_cb_session_group_size(struct format_tree *ft) +{ + struct session_group *sg; + + if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL) + return (format_printf("%u", session_group_count (sg))); + return (NULL); +} + +/* Callback for session_grouped. */ +static void * +format_cb_session_grouped(struct format_tree *ft) +{ + if (ft->s != NULL) { + if (session_group_contains(ft->s) != NULL) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for session_id. */ +static void * +format_cb_session_id(struct format_tree *ft) +{ + if (ft->s != NULL) + return (format_printf("$%u", ft->s->id)); + return (NULL); +} + +/* Callback for session_many_attached. */ +static void * +format_cb_session_many_attached(struct format_tree *ft) +{ + if (ft->s != NULL) { + if (ft->s->attached > 1) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for session_marked. */ +static void * +format_cb_session_marked(struct format_tree *ft) +{ + if (ft->s != NULL) { + if (server_check_marked() && marked_pane.s == ft->s) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for session_name. */ +static void * +format_cb_session_name(struct format_tree *ft) +{ + if (ft->s != NULL) + return (xstrdup(ft->s->name)); + return (NULL); +} + +/* Callback for session_path. */ +static void * +format_cb_session_path(struct format_tree *ft) +{ + if (ft->s != NULL) + return (xstrdup(ft->s->cwd)); + return (NULL); +} + +/* Callback for session_windows. */ +static void * +format_cb_session_windows(struct format_tree *ft) +{ + if (ft->s != NULL) + return (format_printf ("%u", winlink_count(&ft->s->windows))); + return (NULL); +} + +/* Callback for socket_path. */ +static void * +format_cb_socket_path(__unused struct format_tree *ft) +{ + return (xstrdup(socket_path)); +} + +/* Callback for version. */ +static void * +format_cb_version(__unused struct format_tree *ft) +{ + return (xstrdup(getversion())); +} + +/* Callback for window_active. */ +static void * +format_cb_window_active(struct format_tree *ft) +{ + if (ft->wl != NULL) { + if (ft->wl == ft->wl->session->curw) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for window_activity_flag. */ +static void * +format_cb_window_activity_flag(struct format_tree *ft) +{ + if (ft->wl != NULL) { + if (ft->wl->flags & WINLINK_ACTIVITY) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for window_bell_flag. */ +static void * +format_cb_window_bell_flag(struct format_tree *ft) +{ + if (ft->wl != NULL) { + if (ft->wl->flags & WINLINK_BELL) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for window_bigger. */ +static void * +format_cb_window_bigger(struct format_tree *ft) +{ + u_int ox, oy, sx, sy; + + if (ft->c != NULL) { + if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy)) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for window_cell_height. */ +static void * +format_cb_window_cell_height(struct format_tree *ft) +{ + if (ft->w != NULL) + return (format_printf("%u", ft->w->ypixel)); + return (NULL); +} + +/* Callback for window_cell_width. */ +static void * +format_cb_window_cell_width(struct format_tree *ft) +{ + if (ft->w != NULL) + return (format_printf("%u", ft->w->xpixel)); + return (NULL); +} + +/* Callback for window_end_flag. */ +static void * +format_cb_window_end_flag(struct format_tree *ft) +{ + if (ft->wl != NULL) { + if (ft->wl == RB_MAX(winlinks, &ft->wl->session->windows)) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for window_flags. */ +static void * +format_cb_window_flags(struct format_tree *ft) +{ + if (ft->wl != NULL) + return (xstrdup(window_printable_flags(ft->wl, 1))); + return (NULL); +} + +/* Callback for window_format. */ +static void * +format_cb_window_format(struct format_tree *ft) +{ + if (ft->type == FORMAT_TYPE_WINDOW) + return (xstrdup("1")); + return (xstrdup("0")); +} + +/* Callback for window_height. */ +static void * +format_cb_window_height(struct format_tree *ft) +{ + if (ft->w != NULL) + return (format_printf("%u", ft->w->sy)); + return (NULL); +} + +/* Callback for window_id. */ +static void * +format_cb_window_id(struct format_tree *ft) +{ + if (ft->w != NULL) + return (format_printf("@%u", ft->w->id)); + return (NULL); +} + +/* Callback for window_index. */ +static void * +format_cb_window_index(struct format_tree *ft) +{ + if (ft->wl != NULL) + return (format_printf("%d", ft->wl->idx)); + return (NULL); +} + +/* Callback for window_last_flag. */ +static void * +format_cb_window_last_flag(struct format_tree *ft) +{ + if (ft->wl != NULL) { + if (ft->wl == TAILQ_FIRST(&ft->wl->session->lastw)) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for window_linked. */ +static void * +format_cb_window_linked(struct format_tree *ft) +{ + if (ft->wl != NULL) { + if (session_is_linked(ft->wl->session, ft->wl->window)) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for window_linked_sessions. */ +static void * +format_cb_window_linked_sessions(struct format_tree *ft) +{ + if (ft->wl != NULL) + return (format_printf("%u", ft->wl->window->references)); + return (NULL); +} + +/* Callback for window_marked_flag. */ +static void * +format_cb_window_marked_flag(struct format_tree *ft) +{ + if (ft->wl != NULL) { + if (server_check_marked() && marked_pane.wl == ft->wl) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for window_name. */ +static void * +format_cb_window_name(struct format_tree *ft) +{ + if (ft->w != NULL) + return (format_printf("%s", ft->w->name)); + return (NULL); +} + +/* Callback for window_offset_x. */ +static void * +format_cb_window_offset_x(struct format_tree *ft) +{ + u_int ox, oy, sx, sy; + + if (ft->c != NULL) { + if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy)) + return (format_printf("%u", ox)); + return (NULL); + } + return (NULL); +} + +/* Callback for window_offset_y. */ +static void * +format_cb_window_offset_y(struct format_tree *ft) +{ + u_int ox, oy, sx, sy; + + if (ft->c != NULL) { + if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy)) + return (format_printf("%u", oy)); + return (NULL); + } + return (NULL); +} + +/* Callback for window_panes. */ +static void * +format_cb_window_panes(struct format_tree *ft) +{ + if (ft->w != NULL) + return (format_printf("%u", window_count_panes(ft->w))); + return (NULL); +} + +/* Callback for window_raw_flags. */ +static void * +format_cb_window_raw_flags(struct format_tree *ft) +{ + if (ft->wl != NULL) + return (xstrdup(window_printable_flags(ft->wl, 0))); + return (NULL); +} + +/* Callback for window_silence_flag. */ +static void * +format_cb_window_silence_flag(struct format_tree *ft) +{ + if (ft->wl != NULL) { + if (ft->wl->flags & WINLINK_SILENCE) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for window_start_flag. */ +static void * +format_cb_window_start_flag(struct format_tree *ft) +{ + if (ft->wl != NULL) { + if (ft->wl == RB_MIN(winlinks, &ft->wl->session->windows)) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for window_width. */ +static void * +format_cb_window_width(struct format_tree *ft) +{ + if (ft->w != NULL) + return (format_printf("%u", ft->w->sx)); + return (NULL); +} + +/* Callback for window_zoomed_flag. */ +static void * +format_cb_window_zoomed_flag(struct format_tree *ft) +{ + if (ft->w != NULL) { + if (ft->w->flags & WINDOW_ZOOMED) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for wrap_flag. */ +static void * +format_cb_wrap_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_WRAP) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for buffer_created. */ +static void * +format_cb_buffer_created(struct format_tree *ft) +{ + static struct timeval tv; + + if (ft->pb != NULL) { + timerclear(&tv); + tv.tv_sec = paste_buffer_created(ft->pb); + return (&tv); + } + return (NULL); +} + +/* Callback for client_activity. */ +static void * +format_cb_client_activity(struct format_tree *ft) +{ + if (ft->c != NULL) + return (&ft->c->activity_time); + return (NULL); +} + +/* Callback for client_created. */ +static void * +format_cb_client_created(struct format_tree *ft) +{ + if (ft->c != NULL) + return (&ft->c->creation_time); + return (NULL); +} + +/* Callback for session_activity. */ +static void * +format_cb_session_activity(struct format_tree *ft) +{ + if (ft->s != NULL) + return (&ft->s->activity_time); + return (NULL); +} + +/* Callback for session_created. */ +static void * +format_cb_session_created(struct format_tree *ft) +{ + if (ft->s != NULL) + return (&ft->s->creation_time); + return (NULL); +} + +/* Callback for session_last_attached. */ +static void * +format_cb_session_last_attached(struct format_tree *ft) +{ + if (ft->s != NULL) + return (&ft->s->last_attached_time); + return (NULL); +} + +/* Callback for start_time. */ +static void * +format_cb_start_time(__unused struct format_tree *ft) +{ + return (&start_time); +} + +/* Callback for window_activity. */ +static void * +format_cb_window_activity(struct format_tree *ft) +{ + if (ft->w != NULL) + return (&ft->w->activity_time); + return (NULL); +} + +/* Callback for buffer_mode_format, */ +static void * +format_cb_buffer_mode_format(__unused struct format_tree *ft) +{ + return (xstrdup(window_buffer_mode.default_format)); +} + +/* Callback for client_mode_format, */ +static void * +format_cb_client_mode_format(__unused struct format_tree *ft) +{ + return (xstrdup(window_client_mode.default_format)); +} + +/* Callback for tree_mode_format, */ +static void * +format_cb_tree_mode_format(__unused struct format_tree *ft) +{ + return (xstrdup(window_tree_mode.default_format)); +} + +/* Format table type. */ +enum format_table_type { + FORMAT_TABLE_STRING, + FORMAT_TABLE_TIME +}; + +/* Format table entry. */ +struct format_table_entry { + const char *key; + enum format_table_type type; + format_cb cb; +}; + +/* + * Format table. Default format variables (that are almost always in the tree + * and where the value is expanded by a callback in this file) are listed here. + * Only variables which are added by the caller go into the tree. + */ +static const struct format_table_entry format_table[] = { + { "alternate_on", FORMAT_TABLE_STRING, + format_cb_alternate_on + }, + { "alternate_saved_x", FORMAT_TABLE_STRING, + format_cb_alternate_saved_x + }, + { "alternate_saved_y", FORMAT_TABLE_STRING, + format_cb_alternate_saved_y + }, + { "buffer_created", FORMAT_TABLE_TIME, + format_cb_buffer_created + }, + { "buffer_mode_format", FORMAT_TABLE_STRING, + format_cb_buffer_mode_format + }, + { "buffer_name", FORMAT_TABLE_STRING, + format_cb_buffer_name + }, + { "buffer_sample", FORMAT_TABLE_STRING, + format_cb_buffer_sample + }, + { "buffer_size", FORMAT_TABLE_STRING, + format_cb_buffer_size + }, + { "client_activity", FORMAT_TABLE_TIME, + format_cb_client_activity + }, + { "client_cell_height", FORMAT_TABLE_STRING, + format_cb_client_cell_height + }, + { "client_cell_width", FORMAT_TABLE_STRING, + format_cb_client_cell_width + }, + { "client_control_mode", FORMAT_TABLE_STRING, + format_cb_client_control_mode + }, + { "client_created", FORMAT_TABLE_TIME, + format_cb_client_created + }, + { "client_discarded", FORMAT_TABLE_STRING, + format_cb_client_discarded + }, + { "client_flags", FORMAT_TABLE_STRING, + format_cb_client_flags + }, + { "client_height", FORMAT_TABLE_STRING, + format_cb_client_height + }, + { "client_key_table", FORMAT_TABLE_STRING, + format_cb_client_key_table + }, + { "client_last_session", FORMAT_TABLE_STRING, + format_cb_client_last_session + }, + { "client_mode_format", FORMAT_TABLE_STRING, + format_cb_client_mode_format + }, + { "client_name", FORMAT_TABLE_STRING, + format_cb_client_name + }, + { "client_pid", FORMAT_TABLE_STRING, + format_cb_client_pid + }, + { "client_prefix", FORMAT_TABLE_STRING, + format_cb_client_prefix + }, + { "client_readonly", FORMAT_TABLE_STRING, + format_cb_client_readonly + }, + { "client_session", FORMAT_TABLE_STRING, + format_cb_client_session + }, + { "client_termfeatures", FORMAT_TABLE_STRING, + format_cb_client_termfeatures + }, + { "client_termname", FORMAT_TABLE_STRING, + format_cb_client_termname + }, + { "client_termtype", FORMAT_TABLE_STRING, + format_cb_client_termtype + }, + { "client_tty", FORMAT_TABLE_STRING, + format_cb_client_tty + }, + { "client_utf8", FORMAT_TABLE_STRING, + format_cb_client_utf8 + }, + { "client_width", FORMAT_TABLE_STRING, + format_cb_client_width + }, + { "client_written", FORMAT_TABLE_STRING, + format_cb_client_written + }, + { "cursor_character", FORMAT_TABLE_STRING, + format_cb_cursor_character + }, + { "cursor_flag", FORMAT_TABLE_STRING, + format_cb_cursor_flag + }, + { "cursor_x", FORMAT_TABLE_STRING, + format_cb_cursor_x + }, + { "cursor_y", FORMAT_TABLE_STRING, + format_cb_cursor_y + }, + { "history_all_bytes", FORMAT_TABLE_STRING, + format_cb_history_all_bytes + }, + { "history_bytes", FORMAT_TABLE_STRING, + format_cb_history_bytes + }, + { "history_limit", FORMAT_TABLE_STRING, + format_cb_history_limit + }, + { "history_size", FORMAT_TABLE_STRING, + format_cb_history_size + }, + { "host", FORMAT_TABLE_STRING, + format_cb_host + }, + { "host_short", FORMAT_TABLE_STRING, + format_cb_host_short + }, + { "insert_flag", FORMAT_TABLE_STRING, + format_cb_insert_flag + }, + { "keypad_cursor_flag", FORMAT_TABLE_STRING, + format_cb_keypad_cursor_flag + }, + { "keypad_flag", FORMAT_TABLE_STRING, + format_cb_keypad_flag + }, + { "mouse_all_flag", FORMAT_TABLE_STRING, + format_cb_mouse_all_flag + }, + { "mouse_any_flag", FORMAT_TABLE_STRING, + format_cb_mouse_any_flag + }, + { "mouse_button_flag", FORMAT_TABLE_STRING, + format_cb_mouse_button_flag + }, + { "mouse_line", FORMAT_TABLE_STRING, + format_cb_mouse_line + }, + { "mouse_pane", FORMAT_TABLE_STRING, + format_cb_mouse_pane + }, + { "mouse_sgr_flag", FORMAT_TABLE_STRING, + format_cb_mouse_sgr_flag + }, + { "mouse_standard_flag", FORMAT_TABLE_STRING, + format_cb_mouse_standard_flag + }, + { "mouse_utf8_flag", FORMAT_TABLE_STRING, + format_cb_mouse_utf8_flag + }, + { "mouse_word", FORMAT_TABLE_STRING, + format_cb_mouse_word + }, + { "mouse_x", FORMAT_TABLE_STRING, + format_cb_mouse_x + }, + { "mouse_y", FORMAT_TABLE_STRING, + format_cb_mouse_y + }, + { "origin_flag", FORMAT_TABLE_STRING, + format_cb_origin_flag + }, + { "pane_active", FORMAT_TABLE_STRING, + format_cb_pane_active + }, + { "pane_at_bottom", FORMAT_TABLE_STRING, + format_cb_pane_at_bottom + }, + { "pane_at_left", FORMAT_TABLE_STRING, + format_cb_pane_at_left + }, + { "pane_at_right", FORMAT_TABLE_STRING, + format_cb_pane_at_right + }, + { "pane_at_top", FORMAT_TABLE_STRING, + format_cb_pane_at_top + }, + { "pane_bg", FORMAT_TABLE_STRING, + format_cb_pane_bg + }, + { "pane_bottom", FORMAT_TABLE_STRING, + format_cb_pane_bottom + }, + { "pane_current_command", FORMAT_TABLE_STRING, + format_cb_current_command + }, + { "pane_current_path", FORMAT_TABLE_STRING, + format_cb_current_path + }, + { "pane_dead", FORMAT_TABLE_STRING, + format_cb_pane_dead + }, + { "pane_dead_status", FORMAT_TABLE_STRING, + format_cb_pane_dead_status + }, + { "pane_fg", FORMAT_TABLE_STRING, + format_cb_pane_fg + }, + { "pane_format", FORMAT_TABLE_STRING, + format_cb_pane_format + }, + { "pane_height", FORMAT_TABLE_STRING, + format_cb_pane_height + }, + { "pane_id", FORMAT_TABLE_STRING, + format_cb_pane_id + }, + { "pane_in_mode", FORMAT_TABLE_STRING, + format_cb_pane_in_mode + }, + { "pane_index", FORMAT_TABLE_STRING, + format_cb_pane_index + }, + { "pane_input_off", FORMAT_TABLE_STRING, + format_cb_pane_input_off + }, + { "pane_last", FORMAT_TABLE_STRING, + format_cb_pane_last + }, + { "pane_left", FORMAT_TABLE_STRING, + format_cb_pane_left + }, + { "pane_marked", FORMAT_TABLE_STRING, + format_cb_pane_marked + }, + { "pane_marked_set", FORMAT_TABLE_STRING, + format_cb_pane_marked_set + }, + { "pane_mode", FORMAT_TABLE_STRING, + format_cb_pane_mode + }, + { "pane_path", FORMAT_TABLE_STRING, + format_cb_pane_path + }, + { "pane_pid", FORMAT_TABLE_STRING, + format_cb_pane_pid + }, + { "pane_pipe", FORMAT_TABLE_STRING, + format_cb_pane_pipe + }, + { "pane_right", FORMAT_TABLE_STRING, + format_cb_pane_right + }, + { "pane_search_string", FORMAT_TABLE_STRING, + format_cb_pane_search_string + }, + { "pane_start_command", FORMAT_TABLE_STRING, + format_cb_start_command + }, + { "pane_synchronized", FORMAT_TABLE_STRING, + format_cb_pane_synchronized + }, + { "pane_tabs", FORMAT_TABLE_STRING, + format_cb_pane_tabs + }, + { "pane_title", FORMAT_TABLE_STRING, + format_cb_pane_title + }, + { "pane_top", FORMAT_TABLE_STRING, + format_cb_pane_top + }, + { "pane_tty", FORMAT_TABLE_STRING, + format_cb_pane_tty + }, + { "pane_width", FORMAT_TABLE_STRING, + format_cb_pane_width + }, + { "pid", FORMAT_TABLE_STRING, + format_cb_pid + }, + { "scroll_region_lower", FORMAT_TABLE_STRING, + format_cb_scroll_region_lower + }, + { "scroll_region_upper", FORMAT_TABLE_STRING, + format_cb_scroll_region_upper + }, + { "session_activity", FORMAT_TABLE_TIME, + format_cb_session_activity + }, + { "session_alerts", FORMAT_TABLE_STRING, + format_cb_session_alerts + }, + { "session_attached", FORMAT_TABLE_STRING, + format_cb_session_attached + }, + { "session_attached_list", FORMAT_TABLE_STRING, + format_cb_session_attached_list + }, + { "session_created", FORMAT_TABLE_TIME, + format_cb_session_created + }, + { "session_format", FORMAT_TABLE_STRING, + format_cb_session_format + }, + { "session_group", FORMAT_TABLE_STRING, + format_cb_session_group + }, + { "session_group_attached", FORMAT_TABLE_STRING, + format_cb_session_group_attached + }, + { "session_group_attached_list", FORMAT_TABLE_STRING, + format_cb_session_group_attached_list + }, + { "session_group_list", FORMAT_TABLE_STRING, + format_cb_session_group_list + }, + { "session_group_many_attached", FORMAT_TABLE_STRING, + format_cb_session_group_many_attached + }, + { "session_group_size", FORMAT_TABLE_STRING, + format_cb_session_group_size + }, + { "session_grouped", FORMAT_TABLE_STRING, + format_cb_session_grouped + }, + { "session_id", FORMAT_TABLE_STRING, + format_cb_session_id + }, + { "session_last_attached", FORMAT_TABLE_TIME, + format_cb_session_last_attached + }, + { "session_many_attached", FORMAT_TABLE_STRING, + format_cb_session_many_attached + }, + { "session_marked", FORMAT_TABLE_STRING, + format_cb_session_marked, + }, + { "session_name", FORMAT_TABLE_STRING, + format_cb_session_name + }, + { "session_path", FORMAT_TABLE_STRING, + format_cb_session_path + }, + { "session_stack", FORMAT_TABLE_STRING, + format_cb_session_stack + }, + { "session_windows", FORMAT_TABLE_STRING, + format_cb_session_windows + }, + { "socket_path", FORMAT_TABLE_STRING, + format_cb_socket_path + }, + { "start_time", FORMAT_TABLE_TIME, + format_cb_start_time + }, + { "tree_mode_format", FORMAT_TABLE_STRING, + format_cb_tree_mode_format + }, + { "version", FORMAT_TABLE_STRING, + format_cb_version + }, + { "window_active", FORMAT_TABLE_STRING, + format_cb_window_active + }, + { "window_active_clients", FORMAT_TABLE_STRING, + format_cb_window_active_clients + }, + { "window_active_clients_list", FORMAT_TABLE_STRING, + format_cb_window_active_clients_list + }, + { "window_active_sessions", FORMAT_TABLE_STRING, + format_cb_window_active_sessions + }, + { "window_active_sessions_list", FORMAT_TABLE_STRING, + format_cb_window_active_sessions_list + }, + { "window_activity", FORMAT_TABLE_TIME, + format_cb_window_activity + }, + { "window_activity_flag", FORMAT_TABLE_STRING, + format_cb_window_activity_flag + }, + { "window_bell_flag", FORMAT_TABLE_STRING, + format_cb_window_bell_flag + }, + { "window_bigger", FORMAT_TABLE_STRING, + format_cb_window_bigger + }, + { "window_cell_height", FORMAT_TABLE_STRING, + format_cb_window_cell_height + }, + { "window_cell_width", FORMAT_TABLE_STRING, + format_cb_window_cell_width + }, + { "window_end_flag", FORMAT_TABLE_STRING, + format_cb_window_end_flag + }, + { "window_flags", FORMAT_TABLE_STRING, + format_cb_window_flags + }, + { "window_format", FORMAT_TABLE_STRING, + format_cb_window_format + }, + { "window_height", FORMAT_TABLE_STRING, + format_cb_window_height + }, + { "window_id", FORMAT_TABLE_STRING, + format_cb_window_id + }, + { "window_index", FORMAT_TABLE_STRING, + format_cb_window_index + }, + { "window_last_flag", FORMAT_TABLE_STRING, + format_cb_window_last_flag + }, + { "window_layout", FORMAT_TABLE_STRING, + format_cb_window_layout + }, + { "window_linked", FORMAT_TABLE_STRING, + format_cb_window_linked + }, + { "window_linked_sessions", FORMAT_TABLE_STRING, + format_cb_window_linked_sessions + }, + { "window_linked_sessions_list", FORMAT_TABLE_STRING, + format_cb_window_linked_sessions_list + }, + { "window_marked_flag", FORMAT_TABLE_STRING, + format_cb_window_marked_flag + }, + { "window_name", FORMAT_TABLE_STRING, + format_cb_window_name + }, + { "window_offset_x", FORMAT_TABLE_STRING, + format_cb_window_offset_x + }, + { "window_offset_y", FORMAT_TABLE_STRING, + format_cb_window_offset_y + }, + { "window_panes", FORMAT_TABLE_STRING, + format_cb_window_panes + }, + { "window_raw_flags", FORMAT_TABLE_STRING, + format_cb_window_raw_flags + }, + { "window_silence_flag", FORMAT_TABLE_STRING, + format_cb_window_silence_flag + }, + { "window_stack_index", FORMAT_TABLE_STRING, + format_cb_window_stack_index + }, + { "window_start_flag", FORMAT_TABLE_STRING, + format_cb_window_start_flag + }, + { "window_visible_layout", FORMAT_TABLE_STRING, + format_cb_window_visible_layout + }, + { "window_width", FORMAT_TABLE_STRING, + format_cb_window_width + }, + { "window_zoomed_flag", FORMAT_TABLE_STRING, + format_cb_window_zoomed_flag + }, + { "wrap_flag", FORMAT_TABLE_STRING, + format_cb_wrap_flag + } +}; + +/* Compare format table entries. */ +static int +format_table_compare(const void *key0, const void *entry0) +{ + const char *key = key0; + const struct format_table_entry *entry = entry0; + + return (strcmp(key, entry->key)); +} + +/* Get a format callback. */ +static struct format_table_entry * +format_table_get(const char *key) +{ + return (bsearch(key, format_table, nitems(format_table), + sizeof *format_table, format_table_compare)); +} + /* Merge one format tree into another. */ void format_merge(struct format_tree *ft, struct format_tree *from) @@ -1226,20 +2983,8 @@ format_create_add_item(struct format_tree *ft, struct cmdq_item *item) { struct key_event *event = cmdq_get_event(item); struct mouse_event *m = &event->m; - struct window_pane *wp; - u_int x, y; cmdq_merge_formats(item, ft); - - if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) { - format_add(ft, "mouse_pane", "%%%u", wp->id); - if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) { - format_add(ft, "mouse_x", "%u", x); - format_add(ft, "mouse_y", "%u", y); - format_add_cb(ft, "mouse_word", format_cb_mouse_word); - format_add_cb(ft, "mouse_line", format_cb_mouse_line); - } - } memcpy(&ft->m, m, sizeof ft->m); } @@ -1247,9 +2992,7 @@ format_create_add_item(struct format_tree *ft, struct cmdq_item *item) struct format_tree * format_create(struct client *c, struct cmdq_item *item, int tag, int flags) { - struct format_tree *ft; - const struct window_mode **wm; - char tmp[64]; + struct format_tree *ft; if (!event_initialized(&format_job_event)) { evtimer_set(&format_job_event, format_job_timer, NULL); @@ -1268,21 +3011,6 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags) ft->tag = tag; ft->flags = flags; - format_add(ft, "version", "%s", getversion()); - format_add_cb(ft, "host", format_cb_host); - format_add_cb(ft, "host_short", format_cb_host_short); - format_add_cb(ft, "pid", format_cb_pid); - format_add(ft, "socket_path", "%s", socket_path); - format_add_tv(ft, "start_time", &start_time); - - for (wm = all_window_modes; *wm != NULL; wm++) { - if ((*wm)->default_format != NULL) { - xsnprintf(tmp, sizeof tmp, "%s_format", (*wm)->name); - tmp[strcspn(tmp, "-")] = '_'; - format_add(ft, tmp, "%s", (*wm)->default_format); - } - } - if (item != NULL) format_create_add_item(ft, item); @@ -1312,9 +3040,28 @@ void format_each(struct format_tree *ft, void (*cb)(const char *, const char *, void *), void *arg) { - struct format_entry *fe; - char s[64]; + const struct format_table_entry *fte; + struct format_entry *fe; + u_int i; + char s[64]; + void *value; + struct timeval *tv; + for (i = 0; i < nitems(format_table); i++) { + fte = &format_table[i]; + + value = fte->cb(ft); + if (value == NULL) + continue; + if (fte->type == FORMAT_TABLE_TIME) { + tv = value; + xsnprintf(s, sizeof s, "%lld", (long long)tv->tv_sec); + cb(fte->key, s, arg); + } else { + cb(fte->key, value, arg); + free(value); + } + } RB_FOREACH(fe, format_entry_tree, &ft->tree) { if (fe->time != 0) { xsnprintf(s, sizeof s, "%lld", (long long)fe->time); @@ -1484,14 +3231,16 @@ static char * format_find(struct format_tree *ft, const char *key, int modifiers, const char *time_format) { - struct format_entry *fe, fe_find; - struct environ_entry *envent; - struct options_entry *o; - int idx; - char *found = NULL, *saved, s[512]; - const char *errstr; - time_t t = 0; - struct tm tm; + struct format_table_entry *fte; + void *value; + struct format_entry *fe, fe_find; + struct environ_entry *envent; + struct options_entry *o; + int idx; + char *found = NULL, *saved, s[512]; + const char *errstr; + time_t t = 0; + struct tm tm; o = options_parse_get(global_options, key, &idx, 0); if (o == NULL && ft->wp != NULL) @@ -1509,6 +3258,15 @@ format_find(struct format_tree *ft, const char *key, int modifiers, goto found; } + fte = format_table_get(key); + if (fte != NULL) { + value = fte->cb(ft); + if (fte->type == FORMAT_TABLE_TIME) + t = ((struct timeval *)value)->tv_sec; + else + found = value; + goto found; + } fe_find.key = (char *)key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe != NULL) { @@ -2619,7 +4377,6 @@ format_expand1(struct format_expand_state *es, const char *fmt) const char *ptr, *s; size_t off, len, n, outlen; int ch, brackets; - struct tm *tm; char expanded[8192]; if (fmt == NULL || *fmt == '\0') @@ -2631,11 +4388,12 @@ format_expand1(struct format_expand_state *es, const char *fmt) format_log(es, "expanding format: %s", fmt); - if (es->flags & FORMAT_EXPAND_TIME) { - if (es->time == 0) + if ((es->flags & FORMAT_EXPAND_TIME) && strchr(fmt, '%') != NULL) { + if (es->time == 0) { es->time = time(NULL); - tm = localtime(&es->time); - if (strftime(expanded, sizeof expanded, fmt, tm) == 0) { + localtime_r(&es->time, &es->tm); + } + if (strftime(expanded, sizeof expanded, fmt, &es->tm) == 0) { format_log(es, "format is too long"); return (xstrdup("")); } @@ -2887,9 +4645,14 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s, if (c != NULL && s != NULL && c->session != s) log_debug("%s: session does not match", __func__); - format_add(ft, "session_format", "%d", s != NULL); - format_add(ft, "window_format", "%d", wl != NULL); - format_add(ft, "pane_format", "%d", wp != NULL); + if (s != NULL) + ft->type = FORMAT_TYPE_SESSION; + else if (wl != NULL) + ft->type = FORMAT_TYPE_WINDOW; + else if (wp != NULL) + ft->type = FORMAT_TYPE_PANE; + else + ft->type = FORMAT_TYPE_UNKNOWN; if (s == NULL && c != NULL) s = c->session; @@ -2916,106 +4679,16 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s, static void format_defaults_session(struct format_tree *ft, struct session *s) { - struct session_group *sg; - ft->s = s; - - format_add(ft, "session_name", "%s", s->name); - format_add(ft, "session_path", "%s", s->cwd); - format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); - format_add(ft, "session_id", "$%u", s->id); - - sg = session_group_contains(s); - format_add(ft, "session_grouped", "%d", sg != NULL); - if (sg != NULL) { - format_add(ft, "session_group", "%s", sg->name); - format_add(ft, "session_group_size", "%u", - session_group_count (sg)); - format_add(ft, "session_group_attached", "%u", - session_group_attached_count (sg)); - format_add(ft, "session_group_many_attached", "%u", - session_group_attached_count (sg) > 1); - format_add_cb(ft, "session_group_list", - format_cb_session_group_list); - format_add_cb(ft, "session_group_attached_list", - format_cb_session_group_attached_list); - } - - format_add_tv(ft, "session_created", &s->creation_time); - format_add_tv(ft, "session_last_attached", &s->last_attached_time); - format_add_tv(ft, "session_activity", &s->activity_time); - - format_add(ft, "session_attached", "%u", s->attached); - format_add(ft, "session_many_attached", "%d", s->attached > 1); - format_add_cb(ft, "session_attached_list", - format_cb_session_attached_list); - - format_add_cb(ft, "session_alerts", format_cb_session_alerts); - format_add_cb(ft, "session_stack", format_cb_session_stack); - - if (server_check_marked() && marked_pane.s == s) - format_add(ft, "session_marked", "1"); - else - format_add(ft, "session_marked", "0"); } /* Set default format keys for a client. */ static void format_defaults_client(struct format_tree *ft, struct client *c) { - struct session *s; - const char *name; - struct tty *tty = &c->tty; - if (ft->s == NULL) ft->s = c->session; ft->c = c; - - format_add(ft, "client_name", "%s", c->name); - format_add(ft, "client_pid", "%ld", (long) c->pid); - format_add(ft, "client_height", "%u", tty->sy); - format_add(ft, "client_width", "%u", tty->sx); - format_add(ft, "client_cell_width", "%u", tty->xpixel); - format_add(ft, "client_cell_height", "%u", tty->ypixel); - format_add(ft, "client_tty", "%s", c->ttyname); - format_add(ft, "client_control_mode", "%d", - !!(c->flags & CLIENT_CONTROL)); - - format_add(ft, "client_termname", "%s", c->term_name); - format_add(ft, "client_termfeatures", "%s", - tty_get_features(c->term_features)); - if (c->term_type != NULL) - format_add(ft, "client_termtype", "%s", c->term_type); - - format_add_tv(ft, "client_created", &c->creation_time); - format_add_tv(ft, "client_activity", &c->activity_time); - - format_add(ft, "client_written", "%zu", c->written); - format_add(ft, "client_discarded", "%zu", c->discarded); - - name = server_client_get_key_table(c); - if (strcmp(c->keytable->name, name) == 0) - format_add(ft, "client_prefix", "%d", 0); - else - format_add(ft, "client_prefix", "%d", 1); - format_add(ft, "client_key_table", "%s", c->keytable->name); - - if (c->flags & CLIENT_UTF8) - format_add(ft, "client_utf8", "%d", 1); - else - format_add(ft, "client_utf8", "%d", 0); - if (c->flags & CLIENT_READONLY) - format_add(ft, "client_readonly", "%d", 1); - else - format_add(ft, "client_readonly", "%d", 0); - format_add(ft, "client_flags", "%s", server_client_get_flags(c)); - - s = c->session; - if (s != NULL) - format_add(ft, "client_session", "%s", s->name); - s = c->last_session; - if (s != NULL && session_alive(s)) - format_add(ft, "client_last_session", "%s", s->name); } /* Set default format keys for a window. */ @@ -3023,221 +4696,131 @@ void format_defaults_window(struct format_tree *ft, struct window *w) { ft->w = w; - - format_add_tv(ft, "window_activity", &w->activity_time); - format_add(ft, "window_id", "@%u", w->id); - format_add(ft, "window_name", "%s", w->name); - format_add(ft, "window_width", "%u", w->sx); - format_add(ft, "window_height", "%u", w->sy); - format_add(ft, "window_cell_width", "%u", w->xpixel); - format_add(ft, "window_cell_height", "%u", w->ypixel); - format_add_cb(ft, "window_layout", format_cb_window_layout); - format_add_cb(ft, "window_visible_layout", - format_cb_window_visible_layout); - format_add(ft, "window_panes", "%u", window_count_panes(w)); - format_add(ft, "window_zoomed_flag", "%d", - !!(w->flags & WINDOW_ZOOMED)); } /* Set default format keys for a winlink. */ static void format_defaults_winlink(struct format_tree *ft, struct winlink *wl) { - struct client *c = ft->c; - struct session *s = wl->session; - struct window *w = wl->window; - int flag; - u_int ox, oy, sx, sy; - if (ft->w == NULL) - format_defaults_window(ft, w); + format_defaults_window(ft, wl->window); ft->wl = wl; - - if (c != NULL) { - flag = tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); - format_add(ft, "window_bigger", "%d", flag); - if (flag) { - format_add(ft, "window_offset_x", "%u", ox); - format_add(ft, "window_offset_y", "%u", oy); - } - } - - format_add(ft, "window_index", "%d", wl->idx); - format_add_cb(ft, "window_stack_index", format_cb_window_stack_index); - format_add(ft, "window_flags", "%s", window_printable_flags(wl, 1)); - format_add(ft, "window_raw_flags", "%s", window_printable_flags(wl, 0)); - format_add(ft, "window_active", "%d", wl == s->curw); - format_add_cb(ft, "window_active_sessions", - format_cb_window_active_sessions); - format_add_cb(ft, "window_active_sessions_list", - format_cb_window_active_sessions_list); - format_add_cb(ft, "window_active_clients", - format_cb_window_active_clients); - format_add_cb(ft, "window_active_clients_list", - format_cb_window_active_clients_list); - - format_add(ft, "window_start_flag", "%d", - !!(wl == RB_MIN(winlinks, &s->windows))); - format_add(ft, "window_end_flag", "%d", - !!(wl == RB_MAX(winlinks, &s->windows))); - - if (server_check_marked() && marked_pane.wl == wl) - format_add(ft, "window_marked_flag", "1"); - else - format_add(ft, "window_marked_flag", "0"); - - format_add(ft, "window_bell_flag", "%d", - !!(wl->flags & WINLINK_BELL)); - format_add(ft, "window_activity_flag", "%d", - !!(wl->flags & WINLINK_ACTIVITY)); - format_add(ft, "window_silence_flag", "%d", - !!(wl->flags & WINLINK_SILENCE)); - format_add(ft, "window_last_flag", "%d", - !!(wl == TAILQ_FIRST(&s->lastw))); - format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); - - format_add_cb(ft, "window_linked_sessions_list", - format_cb_window_linked_sessions_list); - format_add(ft, "window_linked_sessions", "%u", - wl->window->references); } /* Set default format keys for a window pane. */ void format_defaults_pane(struct format_tree *ft, struct window_pane *wp) { - struct window *w = wp->window; - struct grid *gd = wp->base.grid; - int status = wp->status; - u_int idx; struct window_mode_entry *wme; if (ft->w == NULL) - format_defaults_window(ft, w); + format_defaults_window(ft, wp->window); ft->wp = wp; - format_add(ft, "history_size", "%u", gd->hsize); - format_add(ft, "history_limit", "%u", gd->hlimit); - format_add_cb(ft, "history_bytes", format_cb_history_bytes); - format_add_cb(ft, "history_all_bytes", format_cb_history_all_bytes); - - if (window_pane_index(wp, &idx) != 0) - fatalx("index not found"); - format_add(ft, "pane_index", "%u", idx); - - format_add(ft, "pane_width", "%u", wp->sx); - format_add(ft, "pane_height", "%u", wp->sy); - format_add(ft, "pane_title", "%s", wp->base.title); - if (wp->base.path != NULL) - format_add(ft, "pane_path", "%s", wp->base.path); - format_add(ft, "pane_id", "%%%u", wp->id); - format_add(ft, "pane_active", "%d", wp == w->active); - format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); - format_add(ft, "pane_pipe", "%d", wp->pipe_fd != -1); - - if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(status)) - format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status)); - if (~wp->flags & PANE_EMPTY) - 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"); - else - format_add(ft, "pane_marked", "0"); - format_add(ft, "pane_marked_set", "%d", server_check_marked()); - - format_add(ft, "pane_left", "%u", wp->xoff); - format_add(ft, "pane_top", "%u", wp->yoff); - format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); - format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); - format_add(ft, "pane_at_left", "%d", wp->xoff == 0); - format_add_cb(ft, "pane_at_top", format_cb_pane_at_top); - format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == w->sx); - format_add_cb(ft, "pane_at_bottom", format_cb_pane_at_bottom); - wme = TAILQ_FIRST(&wp->modes); - if (wme != NULL) { - format_add(ft, "pane_mode", "%s", wme->mode->name); - if (wme->mode->formats != NULL) - wme->mode->formats(wme, ft); - } - format_add_cb(ft, "pane_in_mode", format_cb_pane_in_mode); - - format_add(ft, "pane_synchronized", "%d", - !!options_get_number(wp->options, "synchronize-panes")); - if (wp->searchstr != NULL) - format_add(ft, "pane_search_string", "%s", wp->searchstr); - - format_add(ft, "pane_tty", "%s", wp->tty); - format_add(ft, "pane_pid", "%ld", (long) wp->pid); - format_add_cb(ft, "pane_start_command", format_cb_start_command); - format_add_cb(ft, "pane_current_command", format_cb_current_command); - format_add_cb(ft, "pane_current_path", format_cb_current_path); - - format_add(ft, "cursor_x", "%u", wp->base.cx); - format_add(ft, "cursor_y", "%u", wp->base.cy); - format_add_cb(ft, "cursor_character", format_cb_cursor_character); - - format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); - format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); - - format_add(ft, "alternate_on", "%d", wp->base.saved_grid != NULL); - if (wp->base.saved_cx != UINT_MAX) - format_add(ft, "alternate_saved_x", "%u", wp->base.saved_cx); - if (wp->base.saved_cy != UINT_MAX) - format_add(ft, "alternate_saved_y", "%u", wp->base.saved_cy); - - format_add(ft, "cursor_flag", "%d", - !!(wp->base.mode & MODE_CURSOR)); - format_add(ft, "insert_flag", "%d", - !!(wp->base.mode & MODE_INSERT)); - format_add(ft, "keypad_cursor_flag", "%d", - !!(wp->base.mode & MODE_KCURSOR)); - format_add(ft, "keypad_flag", "%d", - !!(wp->base.mode & MODE_KKEYPAD)); - format_add(ft, "wrap_flag", "%d", - !!(wp->base.mode & MODE_WRAP)); - format_add(ft, "origin_flag", "%d", - !!(wp->base.mode & MODE_ORIGIN)); - - format_add(ft, "mouse_any_flag", "%d", - !!(wp->base.mode & ALL_MOUSE_MODES)); - format_add(ft, "mouse_standard_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_STANDARD)); - format_add(ft, "mouse_button_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_BUTTON)); - format_add(ft, "mouse_all_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_ALL)); - format_add(ft, "mouse_utf8_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_UTF8)); - format_add(ft, "mouse_sgr_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_SGR)); - - format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); - format_add_cb(ft, "pane_fg", format_cb_pane_fg); - format_add_cb(ft, "pane_bg", format_cb_pane_bg); + if (wme != NULL && wme->mode->formats != NULL) + wme->mode->formats(wme, ft); } /* Set default format keys for paste buffer. */ void format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) { - struct timeval tv; - size_t size; - char *s; - - timerclear(&tv); - tv.tv_sec = paste_buffer_created(pb); - paste_buffer_data(pb, &size); - - format_add(ft, "buffer_size", "%zu", size); - format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); - format_add_tv(ft, "buffer_created", &tv); - - s = paste_make_sample(pb); - format_add(ft, "buffer_sample", "%s", s); - free(s); + ft->pb = pb; +} + +/* Return word at given coordinates. Caller frees. */ +char * +format_grid_word(struct grid *gd, u_int x, u_int y) +{ + const struct grid_line *gl; + struct grid_cell gc; + const char *ws; + struct utf8_data *ud = NULL; + u_int end; + size_t size = 0; + int found = 0; + char *s = NULL; + + ws = options_get_string(global_s_options, "word-separators"); + + for (;;) { + grid_get_cell(gd, x, y, &gc); + if (gc.flags & GRID_FLAG_PADDING) + break; + if (utf8_cstrhas(ws, &gc.data)) { + found = 1; + break; + } + + if (x == 0) { + if (y == 0) + break; + gl = grid_peek_line(gd, y - 1); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + y--; + x = grid_line_length(gd, y); + if (x == 0) + break; + } + x--; + } + for (;;) { + if (found) { + end = grid_line_length(gd, y); + if (end == 0 || x == end - 1) { + if (y == gd->hsize + gd->sy - 1) + break; + gl = grid_peek_line(gd, y); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + y++; + x = 0; + } else + x++; + } + found = 1; + + grid_get_cell(gd, x, y, &gc); + if (gc.flags & GRID_FLAG_PADDING) + break; + if (utf8_cstrhas(ws, &gc.data)) + break; + + ud = xreallocarray(ud, size + 2, sizeof *ud); + memcpy(&ud[size++], &gc.data, sizeof *ud); + } + if (size != 0) { + ud[size].size = 0; + s = utf8_tocstr(ud); + free(ud); + } + return (s); +} + +/* Return line at given coordinates. Caller frees. */ +char * +format_grid_line(struct grid *gd, u_int y) +{ + struct grid_cell gc; + struct utf8_data *ud = NULL; + u_int x; + size_t size = 0; + char *s = NULL; + + for (x = 0; x < grid_line_length(gd, y); x++) { + grid_get_cell(gd, x, y, &gc); + if (gc.flags & GRID_FLAG_PADDING) + break; + + ud = xreallocarray(ud, size + 2, sizeof *ud); + memcpy(&ud[size++], &gc.data, sizeof *ud); + } + if (size != 0) { + ud[size].size = 0; + s = utf8_tocstr(ud); + free(ud); + } + return (s); } diff --git a/tmux.h b/tmux.h index 4339f6a7..f3f6a867 100644 --- a/tmux.h +++ b/tmux.h @@ -1944,7 +1944,7 @@ char *paste_make_sample(struct paste_buffer *); #define FORMAT_WINDOW 0x40000000U struct format_tree; struct format_modifier; -typedef char *(*format_cb)(struct format_tree *); +typedef void *(*format_cb)(struct format_tree *); const char *format_skip(const char *, const char *); int format_true(const char *); struct format_tree *format_create(struct client *, struct cmdq_item *, int, @@ -2713,7 +2713,6 @@ void screen_alternate_off(struct screen *, struct grid_cell *, int); /* window.c */ extern struct windows windows; extern struct window_pane_tree all_window_panes; -extern const struct window_mode *all_window_modes[]; int window_cmp(struct window *, struct window *); RB_PROTOTYPE(windows, window, entry, window_cmp); int winlink_cmp(struct winlink *, struct winlink *); diff --git a/window-copy.c b/window-copy.c index af8d2937..2f4b06e8 100644 --- a/window-copy.c +++ b/window-copy.c @@ -710,7 +710,7 @@ window_copy_get_line(struct window_pane *wp, u_int y) return (format_grid_line(gd, gd->hsize + y)); } -static char * +static void * window_copy_cursor_word_cb(struct format_tree *ft) { struct window_pane *wp = format_get_pane(ft); @@ -720,7 +720,7 @@ window_copy_cursor_word_cb(struct format_tree *ft) return (window_copy_get_word(wp, data->cx, data->cy)); } -static char * +static void * window_copy_cursor_line_cb(struct format_tree *ft) { struct window_pane *wp = format_get_pane(ft); @@ -730,7 +730,7 @@ window_copy_cursor_line_cb(struct format_tree *ft) return (window_copy_get_line(wp, data->cy)); } -static char * +static void * window_copy_search_match_cb(struct format_tree *ft) { struct window_pane *wp = format_get_pane(ft); diff --git a/window.c b/window.c index 6bfdb1cd..07ef513f 100644 --- a/window.c +++ b/window.c @@ -63,17 +63,6 @@ static u_int next_window_pane_id; static u_int next_window_id; static u_int next_active_point; -/* List of window modes. */ -const struct window_mode *all_window_modes[] = { - &window_buffer_mode, - &window_client_mode, - &window_clock_mode, - &window_copy_mode, - &window_tree_mode, - &window_view_mode, - NULL -}; - struct window_pane_input_data { struct cmdq_item *item; u_int wp; From 6876381276ff2c2a40d304ada27651fdaf1cd8a7 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 22 Feb 2021 08:18:13 +0000 Subject: [PATCH 0710/1006] Move config file path expansion much earlier, keep the list of paths around rather than freeing later, and add a config_files format variable containing it. Suggested by kn@ a while back. --- cfg.c | 30 +++++++++++------------------- format.c | 23 +++++++++++++++++++++++ tmux.1 | 1 + tmux.c | 37 +++++++++++++++++++++++++------------ tmux.h | 6 +++--- 5 files changed, 63 insertions(+), 34 deletions(-) diff --git a/cfg.c b/cfg.c index 7c01f614..55c91bc4 100644 --- a/cfg.c +++ b/cfg.c @@ -28,12 +28,15 @@ #include "tmux.h" struct client *cfg_client; -static char *cfg_file; int cfg_finished; static char **cfg_causes; static u_int cfg_ncauses; static struct cmdq_item *cfg_item; +int cfg_quiet = 1; +char **cfg_files; +u_int cfg_nfiles; + static enum cmd_retval cfg_client_done(__unused struct cmdq_item *item, __unused void *data) { @@ -60,19 +63,11 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data) return (CMD_RETURN_NORMAL); } -void -set_cfg_file(const char *path) -{ - free(cfg_file); - cfg_file = xstrdup(path); -} - void start_cfg(void) { struct client *c; - char **paths; - u_int i, n; + u_int i; /* * Configuration files are loaded without a client, so commands are run @@ -90,15 +85,12 @@ start_cfg(void) cmdq_append(c, cfg_item); } - if (cfg_file == NULL) { - expand_paths(TMUX_CONF, &paths, &n); - for (i = 0; i < n; i++) { - load_cfg(paths[i], c, NULL, CMD_PARSE_QUIET, NULL); - free(paths[i]); - } - free(paths); - } else - load_cfg(cfg_file, c, NULL, 0, NULL); + for (i = 0; i < cfg_nfiles; i++) { + if (cfg_quiet) + load_cfg(cfg_files[i], c, NULL, CMD_PARSE_QUIET, NULL); + else + load_cfg(cfg_files[i], c, NULL, 0, NULL); + } cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); } diff --git a/format.c b/format.c index 014f1385..9fa6fc47 100644 --- a/format.c +++ b/format.c @@ -1412,6 +1412,26 @@ format_cb_client_written(struct format_tree *ft) return (NULL); } +/* Callback for config_files. */ +static void * +format_cb_config_files(__unused struct format_tree *ft) +{ + char *s = NULL; + size_t slen = 0; + u_int i; + size_t n; + + for (i = 0; i < cfg_nfiles; i++) { + n = strlen(cfg_files[i]) + 1; + s = xrealloc(s, slen + n + 1); + slen += xsnprintf(s + slen, n + 1, "%s,", cfg_files[i]); + } + if (s == NULL) + return (xstrdup("")); + s[slen - 1] = '\0'; + return (s); +} + /* Callback for cursor_flag. */ static void * format_cb_cursor_flag(struct format_tree *ft) @@ -2569,6 +2589,9 @@ static const struct format_table_entry format_table[] = { { "client_written", FORMAT_TABLE_STRING, format_cb_client_written }, + { "config_files", FORMAT_TABLE_STRING, + format_cb_config_files + }, { "cursor_character", FORMAT_TABLE_STRING, format_cb_cursor_character }, diff --git a/tmux.1 b/tmux.1 index 9150c4a6..c3164e8f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4763,6 +4763,7 @@ The following variables are available, where appropriate: .It Li "buffer_name" Ta "" Ta "Name of buffer" .It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" .It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" +.It Li "config_files" Ta "" Ta "List of configuration files loaded" .It Li "client_activity" Ta "" Ta "Time client last had activity" .It Li "client_cell_height" Ta "" Ta "Height of each client cell in pixels" .It Li "client_cell_width" Ta "" Ta "Width of each client cell in pixels" diff --git a/tmux.c b/tmux.c index 5861e66b..fb908031 100644 --- a/tmux.c +++ b/tmux.c @@ -142,11 +142,12 @@ expand_path(const char *path, const char *home) return (xstrdup(path)); } -void -expand_paths(const char *s, char ***paths, u_int *n) +static void +expand_paths(const char *s, char ***paths, u_int *n, int ignore_errors) { const char *home = find_home(); char *copy, *next, *tmp, resolved[PATH_MAX], *expanded; + char *path; u_int i; *paths = NULL; @@ -162,20 +163,26 @@ expand_paths(const char *s, char ***paths, u_int *n) if (realpath(expanded, resolved) == NULL) { log_debug("%s: realpath(\"%s\") failed: %s", __func__, expanded, strerror(errno)); + if (ignore_errors) { + free(expanded); + continue; + } + path = expanded; + } else { + path = xstrdup(resolved); free(expanded); - continue; } - free(expanded); for (i = 0; i < *n; i++) { - if (strcmp(resolved, (*paths)[i]) == 0) + if (strcmp(path, (*paths)[i]) == 0) break; } if (i != *n) { - log_debug("%s: duplicate path: %s", __func__, resolved); + log_debug("%s: duplicate path: %s", __func__, path); + free(path); continue; } *paths = xreallocarray(*paths, (*n) + 1, sizeof *paths); - (*paths)[(*n)++] = xstrdup(resolved); + (*paths)[(*n)++] = path; } free(copy); } @@ -193,7 +200,7 @@ make_label(const char *label, char **cause) label = "default"; uid = getuid(); - expand_paths(TMUX_SOCK, &paths, &n); + expand_paths(TMUX_SOCK, &paths, &n, 1); if (n == 0) { xasprintf(cause, "no suitable socket path"); return (NULL); @@ -330,10 +337,11 @@ main(int argc, char **argv) { char *path = NULL, *label = NULL; char *cause, **var; - const char *s, *shell, *cwd; + const char *s, *cwd; int opt, keys, feat = 0; uint64_t flags = 0; const struct options_table_entry *oe; + u_int i; if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && setlocale(LC_CTYPE, "C.UTF-8") == NULL) { @@ -349,6 +357,7 @@ main(int argc, char **argv) if (**argv == '-') flags = CLIENT_LOGIN; + expand_paths(TMUX_CONF, &cfg_files, &cfg_nfiles, 1); while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) { switch (opt) { @@ -368,7 +377,11 @@ main(int argc, char **argv) flags |= CLIENT_CONTROL; break; case 'f': - set_cfg_file(optarg); + for (i = 0; i < cfg_nfiles; i++) + free(cfg_files[i]); + free(cfg_files); + expand_paths(optarg, &cfg_files, &cfg_nfiles, 0); + cfg_quiet = 0; break; case 'V': printf("%s %s\n", getprogname(), getversion()); @@ -460,8 +473,8 @@ main(int argc, char **argv) * The default shell comes from SHELL or from the user's passwd entry * if available. */ - shell = getshell(); - options_set_string(global_s_options, "default-shell", 0, "%s", shell); + options_set_string(global_s_options, "default-shell", 0, "%s", + getshell()); /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { diff --git a/tmux.h b/tmux.h index f3f6a867..fb15356a 100644 --- a/tmux.h +++ b/tmux.h @@ -1887,7 +1887,6 @@ const char *sig2name(int); const char *find_cwd(void); const char *find_home(void); const char *getversion(void); -void expand_paths(const char *, char ***, u_int *); /* proc.c */ struct imsg; @@ -1907,13 +1906,14 @@ pid_t proc_fork_and_daemon(int *); /* cfg.c */ extern int cfg_finished; extern struct client *cfg_client; +extern char **cfg_files; +extern u_int cfg_nfiles; +extern int cfg_quiet; void start_cfg(void); int load_cfg(const char *, struct client *, struct cmdq_item *, int, struct cmdq_item **); int load_cfg_from_buffer(const void *, size_t, const char *, struct client *, struct cmdq_item *, int, struct cmdq_item **); -void set_cfg_file(const char *); -const char *get_cfg_file(void); void printflike(1, 2) cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmdq_item *); void cfg_show_causes(struct session *); From 5f425ee31810c964ae5cf1256d0d7fe5dde7536c Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 22 Feb 2021 08:31:19 +0000 Subject: [PATCH 0711/1006] Fix regex searching with wrapped lines, from Anindya Mukherjee; GitHub issue 2570. --- window-copy.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index 2f4b06e8..531431c8 100644 --- a/window-copy.c +++ b/window-copy.c @@ -73,6 +73,8 @@ static int window_copy_search_marks(struct window_mode_entry *, static void window_copy_clear_marks(struct window_mode_entry *); static void window_copy_move_left(struct screen *, u_int *, u_int *, int); static int window_copy_is_lowercase(const char *); +static void window_copy_search_back_overlap(struct grid *, regex_t *, + u_int *, u_int *, u_int *, u_int); static int window_copy_search_jump(struct window_mode_entry *, struct grid *, struct grid *, u_int, u_int, u_int, int, int, int, int, u_int *); @@ -2912,6 +2914,48 @@ window_copy_is_lowercase(const char *ptr) return (1); } +/* + * Handle backward wrapped regex searches with overlapping matches. In this case + * find the longest overlapping match from previous wrapped lines. + */ +static void +window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx, + u_int *psx, u_int *ppy, u_int endline) +{ + u_int endx, endy, oldendx, oldendy, px, py, sx; + int found = 1; + + oldendx = *ppx + *psx; + oldendy = *ppy - 1; + while (oldendx > gd->sx - 1) { + oldendx -= gd->sx; + oldendy++; + } + endx = oldendx; + endy = oldendy; + px = *ppx; + py = *ppy; + while (found && px == 0 && py - 1 > endline && + grid_get_line(gd, py - 2)->flags & GRID_LINE_WRAPPED && + endx == oldendx && endy == oldendy) { + py--; + found = window_copy_search_rl_regex(gd, &px, &sx, py - 1, 0, + gd->sx, preg); + if (found) { + endx = px + sx; + endy = py - 1; + while (endx > gd->sx - 1) { + endx -= gd->sx; + endy++; + } + if (endx == oldendx && endy == oldendy) { + *ppx = px; + *ppy = py; + } + } + } +} + /* * Search for text stored in sgd starting from position fx,fy up to endline. If * found, jump to it. If cis then ignore case. The direction is 0 for searching @@ -2964,6 +3008,10 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, if (regex) { found = window_copy_search_rl_regex(gd, &px, &sx, i - 1, 0, fx + 1, ®); + if (found) { + window_copy_search_back_overlap(gd, + ®, &px, &sx, &i, endline); + } } else { found = window_copy_search_rl(gd, sgd, &px, i - 1, 0, fx + 1, cis); @@ -3048,6 +3096,12 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex, if (found) { window_copy_search_marks(wme, &ss, regex, visible_only); if (foundlen != 0) { + /* Adjust for wrapped lines eating one right. */ + i = data->cx + foundlen; + while (i > gd->sx - 1) { + i -= gd->sx; + window_copy_cursor_right(wme, 1); + } for (i = 0; i < foundlen; i++) window_copy_cursor_right(wme, 1); } @@ -3164,8 +3218,11 @@ again: if (window_copy_search_mark_at(data, px, py, &b) == 0) { if (b + width > gd->sx * gd->sy) width = (gd->sx * gd->sy) - b; - for (i = b; i < b + width; i++) + for (i = b; i < b + width; i++) { + if (data->searchmark[i] != 0) + continue; data->searchmark[i] = data->searchgen; + } if (data->searchgen == UCHAR_MAX) data->searchgen = 1; else From 6d8efe9319857c0b1bc49118248efe7f55112bb5 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 22 Feb 2021 11:42:50 +0000 Subject: [PATCH 0712/1006] expand_paths needs the global environment to be set up, do that first. --- tmux.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tmux.c b/tmux.c index fb908031..71cbccba 100644 --- a/tmux.c +++ b/tmux.c @@ -357,6 +357,12 @@ main(int argc, char **argv) if (**argv == '-') flags = CLIENT_LOGIN; + + global_environ = environ_create(); + for (var = environ; *var != NULL; var++) + environ_put(global_environ, *var, 0); + if ((cwd = find_cwd()) != NULL) + environ_set(global_environ, "PWD", 0, "%s", cwd); expand_paths(TMUX_CONF, &cfg_files, &cfg_nfiles, 1); while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) { @@ -451,12 +457,6 @@ main(int argc, char **argv) flags |= CLIENT_UTF8; } - global_environ = environ_create(); - for (var = environ; *var != NULL; var++) - environ_put(global_environ, *var, 0); - if ((cwd = find_cwd()) != NULL) - environ_set(global_environ, "PWD", 0, "%s", cwd); - global_options = options_create(NULL); global_s_options = options_create(NULL); global_w_options = options_create(NULL); From 6aaef3e705507566631905560b1c903f8ae22ab3 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 24 Feb 2021 09:22:15 +0000 Subject: [PATCH 0713/1006] Correct client_prefix so it returns 1 if in prefix, not 0. --- format.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 9fa6fc47..a9694bab 100644 --- a/format.c +++ b/format.c @@ -1319,8 +1319,8 @@ format_cb_client_prefix(struct format_tree *ft) if (ft->c != NULL) { name = server_client_get_key_table(ft->c); if (strcmp(ft->c->keytable->name, name) == 0) - return (xstrdup("1")); - return (xstrdup("0")); + return (xstrdup("0")); + return (xstrdup("1")); } return (NULL); } From dd7006c850e1f973c214f70eee87a054b00f19e7 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 26 Feb 2021 07:53:26 +0000 Subject: [PATCH 0714/1006] Add a couple of format variables for active and last window index. --- format.c | 28 ++++++++++++++++++++++++++++ tmux.1 | 6 ++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index a9694bab..00413bae 100644 --- a/format.c +++ b/format.c @@ -2099,6 +2099,28 @@ format_cb_version(__unused struct format_tree *ft) return (xstrdup(getversion())); } +/* Callback for active_window_index. */ +static void * +format_cb_active_window_index(struct format_tree *ft) +{ + if (ft->s != NULL) + return (format_printf("%u", ft->s->curw->idx)); + return (NULL); +} + +/* Callback for last_window_index. */ +static void * +format_cb_last_window_index(struct format_tree *ft) +{ + struct winlink *wl; + + if (ft->s != NULL) { + wl = RB_MAX(winlinks, &ft->s->windows); + return (format_printf("%u", wl->idx)); + } + return (NULL); +} + /* Callback for window_active. */ static void * format_cb_window_active(struct format_tree *ft) @@ -2496,6 +2518,9 @@ struct format_table_entry { * Only variables which are added by the caller go into the tree. */ static const struct format_table_entry format_table[] = { + { "active_window_index", FORMAT_TABLE_STRING, + format_cb_active_window_index + }, { "alternate_on", FORMAT_TABLE_STRING, format_cb_alternate_on }, @@ -2631,6 +2656,9 @@ static const struct format_table_entry format_table[] = { { "keypad_flag", FORMAT_TABLE_STRING, format_cb_keypad_flag }, + { "last_window_index", FORMAT_TABLE_STRING, + format_cb_last_window_index + }, { "mouse_all_flag", FORMAT_TABLE_STRING, format_cb_mouse_all_flag }, diff --git a/tmux.1 b/tmux.1 index c3164e8f..a88a1d34 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4756,6 +4756,7 @@ will be replaced by The following variables are available, where appropriate: .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" .It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" +.It Li "active_window_index" Ta "" Ta "Index of active window in session" .It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen" .It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" .It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" @@ -4763,7 +4764,6 @@ The following variables are available, where appropriate: .It Li "buffer_name" Ta "" Ta "Name of buffer" .It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" .It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" -.It Li "config_files" Ta "" Ta "List of configuration files loaded" .It Li "client_activity" Ta "" Ta "Time client last had activity" .It Li "client_cell_height" Ta "" Ta "Height of each client cell in pixels" .It Li "client_cell_width" Ta "" Ta "Width of each client cell in pixels" @@ -4790,6 +4790,7 @@ The following variables are available, where appropriate: .It Li "command_list_alias" Ta "" Ta "Command alias if listing commands" .It Li "command_list_name" Ta "" Ta "Command name if listing commands" .It Li "command_list_usage" Ta "" Ta "Command usage if listing commands" +.It Li "config_files" Ta "" Ta "List of configuration files loaded" .It Li "copy_cursor_line" Ta "" Ta "Line the cursor is on in copy mode" .It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode" .It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode" @@ -4812,6 +4813,7 @@ The following variables are available, where appropriate: .It Li "insert_flag" Ta "" Ta "Pane insert flag" .It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" .It Li "keypad_flag" Ta "" Ta "Pane keypad flag" +.It Li "last_window_index" Ta "" Ta "Index of last window in session" .It Li "line" Ta "" Ta "Line number in the list" .It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag" .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" @@ -4867,8 +4869,8 @@ The following variables are available, where appropriate: .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" -.It Li "search_present" Ta "" Ta "1 if search started in copy mode" .It Li "search_match" Ta "" Ta "Search match if any" +.It Li "search_present" Ta "" Ta "1 if search started in copy mode" .It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode" .It Li "selection_end_x" Ta "" Ta "X position of the end of the selection" .It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection" From 583aaebc0ac437777ce796fc6cc29de7d488cf1a Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 26 Feb 2021 21:53:41 +0000 Subject: [PATCH 0715/1006] Check session, window, pane in the right order when working out format type. --- format.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index 00413bae..46af036c 100644 --- a/format.c +++ b/format.c @@ -4696,12 +4696,12 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s, if (c != NULL && s != NULL && c->session != s) log_debug("%s: session does not match", __func__); - if (s != NULL) - ft->type = FORMAT_TYPE_SESSION; + if (wp != NULL) + ft->type = FORMAT_TYPE_PANE; else if (wl != NULL) ft->type = FORMAT_TYPE_WINDOW; - else if (wp != NULL) - ft->type = FORMAT_TYPE_PANE; + else if (s != NULL) + ft->type = FORMAT_TYPE_SESSION; else ft->type = FORMAT_TYPE_UNKNOWN; From 40ad11b2b5155dbccf15881500f2b4f8b16d509d Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 27 Feb 2021 06:28:16 +0000 Subject: [PATCH 0716/1006] Handle NULL term_type. --- format.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 46af036c..c8550907 100644 --- a/format.c +++ b/format.c @@ -1368,8 +1368,11 @@ format_cb_client_termname(struct format_tree *ft) static void * format_cb_client_termtype(struct format_tree *ft) { - if (ft->c != NULL) + if (ft->c != NULL) { + if (ft->c->term_type == NULL) + return (xstrdup("")); return (xstrdup(ft->c->term_type)); + } return (NULL); } From b6dfb9996a9f73c418b2291a8c29d345966eae7d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Mar 2021 10:44:38 +0000 Subject: [PATCH 0717/1006] Add some text with examples of ; as a separator, GitHub issues 2522 and 2580. --- tmux.1 | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tmux.1 b/tmux.1 index a88a1d34..ee07546b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -522,6 +522,69 @@ Commands separated by semicolons together form a - if a command in the sequence encounters an error, no subsequent commands are executed. .Pp +It is recommended that a semicolon used as a command separator should be +written as an individual token, for example from +.Xr sh 1 : +.Bd -literal -offset indent +$ tmux neww \\; splitw +.Ed +.Pp +Or: +.Bd -literal -offset indent +$ tmux neww ';' splitw +.Ed +.Pp +Or from the tmux command prompt: +.Bd -literal -offset indent +neww ; splitw +.Ed +.Pp +However, a trailing semicolon is also interpreted as a command separator, +for example in these +.Xr sh 1 +commands: +.Bd -literal -offset indent +$ tmux neww\\; splitw +.Ed +.Pp +Or: +.Bd -literal -offset indent +$ tmux 'neww;' splitw +.Ed +.Pp +As in these examples, when running tmux from the shell extra care must be taken +to properly quote semicolons: +.Bl -enum -offset Ds +.It +Semicolons that should be interpreted as a command separator +should be escaped according to the shell conventions. +For +.Xr sh 1 +this typically means quoted (such as +.Ql neww ';' splitw ) +or escaped (such as +.Ql neww \\\\; splitw ) . +.It +Individual semicolons or trailing semicolons that should be interpreted as +arguments should be escaped twice: once according to the shell conventions and +a second time for +.Nm ; +for example: +.Bd -literal -offset indent +$ tmux neww 'foo\\;' bar +$ tmux neww foo\\\\; bar +.Ed +.Pp +.It +Semicolons that are not individual tokens or trailing another token should only +be escaped once according to shell conventions; for example: +.Bd -literal -offset indent +$ tmux neww 'foo-;-bar' +$ tmux neww foo-\\;-bar +.Ed +.Pp +.El +.Pp Comments are marked by the unquoted # character - any remaining text after a comment is ignored until the end of the line. .Pp From 8a4a2153fdec71397fe555073d4d77ee68947fff Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Mar 2021 10:50:14 +0000 Subject: [PATCH 0718/1006] There is no need to call del_curterm in the server anymore. --- tty-term.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/tty-term.c b/tty-term.c index c8048f77..9df50948 100644 --- a/tty-term.c +++ b/tty-term.c @@ -521,9 +521,6 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps, a = options_array_next(a); } - /* Delete curses data. */ - del_curterm(cur_term); - /* Apply overrides so any capabilities used for features are changed. */ tty_term_apply_overrides(term); From 9cd45ddad3141370e0dac117388984a3f026c097 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 1 Mar 2021 10:51:24 +0000 Subject: [PATCH 0719/1006] Reinstate del_curterm ifdef bits. --- tty-term.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tty-term.c b/tty-term.c index cc0b8ceb..1d9b36da 100644 --- a/tty-term.c +++ b/tty-term.c @@ -695,7 +695,10 @@ tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps, (*ncaps)++; } +#if !defined(NCURSES_VERSION_MAJOR) || NCURSES_VERSION_MAJOR > 5 || \ + (NCURSES_VERSION_MAJOR == 5 && NCURSES_VERSION_MINOR > 6) del_curterm(cur_term); +#endif return (0); } From de3a898e8af335325039a4165525fff1d5e1a1b0 Mon Sep 17 00:00:00 2001 From: jmc Date: Mon, 1 Mar 2021 17:49:08 +0000 Subject: [PATCH 0720/1006] escape quotes and remove some unneccessary Pp; ok nicm --- tmux.1 | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tmux.1 b/tmux.1 index ee07546b..4046dbf7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -544,7 +544,7 @@ for example in these .Xr sh 1 commands: .Bd -literal -offset indent -$ tmux neww\\; splitw +$ tmux neww\e\e; splitw .Ed .Pp Or: @@ -563,7 +563,7 @@ For this typically means quoted (such as .Ql neww ';' splitw ) or escaped (such as -.Ql neww \\\\; splitw ) . +.Ql neww \e\e\e\e; splitw ) . .It Individual semicolons or trailing semicolons that should be interpreted as arguments should be escaped twice: once according to the shell conventions and @@ -571,18 +571,16 @@ a second time for .Nm ; for example: .Bd -literal -offset indent -$ tmux neww 'foo\\;' bar -$ tmux neww foo\\\\; bar +$ tmux neww 'foo\e\e;' bar +$ tmux neww foo\e\e\e\e; bar .Ed -.Pp .It Semicolons that are not individual tokens or trailing another token should only be escaped once according to shell conventions; for example: .Bd -literal -offset indent $ tmux neww 'foo-;-bar' -$ tmux neww foo-\\;-bar +$ tmux neww foo-\e\e;-bar .Ed -.Pp .El .Pp Comments are marked by the unquoted # character - any remaining text after a From c44750792a9683c5cd6f9df5a69e7417b88772d2 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Mar 2021 10:56:45 +0000 Subject: [PATCH 0721/1006] Drop support for popups where the content is provided directly to tmux (which does not have many practical uses) and only support running a program in the popup. display-popup is now simpler and can accept multiple arguments to avoid escaping problems (like the other commands). --- cmd-display-menu.c | 59 ++++++------- cmd-if-shell.c | 2 +- cmd-run-shell.c | 2 +- format.c | 2 +- job.c | 46 ++++++---- popup.c | 207 ++++----------------------------------------- spawn.c | 5 +- tmux.1 | 61 ++----------- tmux.h | 19 ++--- window-copy.c | 4 +- 10 files changed, 97 insertions(+), 310 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index ae000abe..27a4c1d7 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -18,6 +18,7 @@ #include +#include #include #include @@ -50,10 +51,10 @@ const struct cmd_entry cmd_display_popup_entry = { .name = "display-popup", .alias = "popup", - .args = { "CEKc:d:h:R:t:w:x:y:", 0, -1 }, - .usage = "[-CEK] [-c target-client] [-d start-directory] [-h height] " - "[-R shell-command] " CMD_TARGET_PANE_USAGE " [-w width] " - "[-x position] [-y position] [command line ...]", + .args = { "Cc:d:Eh:t:w:x:y:", 0, -1 }, + .usage = "[-CE] [-c target-client] [-d start-directory] [-h height] " + CMD_TARGET_PANE_USAGE " [-w width] " + "[-x position] [-y position] [command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -325,13 +326,14 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); + struct session *s = target->s; struct client *tc = cmdq_get_target_client(item); struct tty *tty = &tc->tty; - const char *value, *cmd = NULL, **lines = NULL; + const char *value, *shell[] = { NULL, NULL }; const char *shellcmd = NULL; - char *cwd, *cause; - int flags = 0; - u_int px, py, w, h, nlines = 0; + char *cwd, *cause, **argv = args->argv; + int flags = 0, argc = args->argc; + u_int px, py, w, h; if (args_has(args, 'C')) { server_client_clear_overlay(tc); @@ -340,17 +342,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) if (tc->overlay_draw != NULL) return (CMD_RETURN_NORMAL); - if (args->argc >= 1) - cmd = args->argv[0]; - if (args->argc >= 2) { - lines = (const char **)args->argv + 1; - nlines = args->argc - 1; - } - - if (nlines != 0) - h = popup_height(nlines, lines) + 2; - else - h = tty->sy / 2; + h = tty->sy / 2; if (args_has(args, 'h')) { h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause); if (cause != NULL) { @@ -360,10 +352,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) } } - if (nlines != 0) - w = popup_width(item, nlines, lines, tc, target) + 2; - else - w = tty->sx / 2; + w = tty->sx / 2; if (args_has(args, 'w')) { w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause); if (cause != NULL) { @@ -384,20 +373,26 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) if (value != NULL) cwd = format_single_from_target(item, value); else - cwd = xstrdup(server_client_get_cwd(tc, target->s)); + cwd = xstrdup(server_client_get_cwd(tc, s)); + if (argc == 0) + shellcmd = options_get_string(s->options, "default-command"); + else if (argc == 1) + shellcmd = argv[0]; + if (argc <= 1 && (shellcmd == NULL || *shellcmd == '\0')) { + shellcmd = NULL; + shell[0] = options_get_string(s->options, "default-shell"); + if (!checkshell(shell[0])) + shell[0] = _PATH_BSHELL; + argc = 1; + argv = (char**)shell; + } - value = args_get(args, 'R'); - if (value != NULL) - shellcmd = format_single_from_target(item, value); - - if (args_has(args, 'K')) - flags |= POPUP_WRITEKEYS; if (args_has(args, 'E') > 1) flags |= POPUP_CLOSEEXITZERO; else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; - if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, - cmd, cwd, tc, target, NULL, NULL) != 0) + if (popup_display(flags, item, px, py, w, h, shellcmd, argc, argv, cwd, + tc, s, NULL, NULL) != 0) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } diff --git a/cmd-if-shell.c b/cmd-if-shell.c index d980472a..65fbf19b 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -128,7 +128,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->input.c->references++; cmd_find_copy_state(&cdata->input.fs, target); - if (job_run(shellcmd, s, + if (job_run(shellcmd, 0, NULL, s, server_client_get_cwd(cmdq_get_client(item), s), NULL, cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1, -1) == NULL) { diff --git a/cmd-run-shell.c b/cmd-run-shell.c index b259276d..4f30d05d 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -174,7 +174,7 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) enum cmd_parse_status status; if (cmd != NULL && cdata->shell) { - if (job_run(cmd, cdata->s, cdata->cwd, NULL, + if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL, cmd_run_shell_callback, cmd_run_shell_free, cdata, cdata->flags, -1, -1) == NULL) cmd_run_shell_free(cdata); diff --git a/format.c b/format.c index c8550907..b913f56a 100644 --- a/format.c +++ b/format.c @@ -394,7 +394,7 @@ format_job_get(struct format_expand_state *es, const char *cmd) if (force && fj->job != NULL) job_free(fj->job); if (force || (fj->job == NULL && fj->last != t)) { - fj->job = job_run(expanded, NULL, + fj->job = job_run(expanded, 0, NULL, NULL, server_client_get_cwd(ft->client, NULL), format_job_update, format_job_complete, NULL, fj, JOB_NOWAIT, -1, -1); if (fj->job == NULL) { diff --git a/job.c b/job.c index 6267336b..a972bc0e 100644 --- a/job.c +++ b/job.c @@ -68,19 +68,20 @@ struct job { /* All jobs list. */ static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs); -/* Start a job running, if it isn't already. */ +/* Start a job running. */ struct job * -job_run(const char *cmd, struct session *s, const char *cwd, - job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, - void *data, int flags, int sx, int sy) +job_run(const char *cmd, int argc, char **argv, struct session *s, + const char *cwd, job_update_cb updatecb, job_complete_cb completecb, + job_free_cb freecb, void *data, int flags, int sx, int sy) { - struct job *job; - struct environ *env; - pid_t pid; - int nullfd, out[2], master; - const char *home; - sigset_t set, oldset; - struct winsize ws; + struct job *job; + struct environ *env; + pid_t pid; + int nullfd, out[2], master; + const char *home; + sigset_t set, oldset; + struct winsize ws; + char **argvp; /* * Do not set TERM during .tmux.conf, it is nice to be able to use @@ -101,7 +102,13 @@ job_run(const char *cmd, struct session *s, const char *cwd, goto fail; pid = fork(); } - log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd); + if (cmd == NULL) { + cmd_log_argv(argc, argv, "%s:", __func__); + log_debug("%s: cwd=%s", __func__, cwd == NULL ? "" : cwd); + } else { + log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, + cwd == NULL ? "" : cwd); + } switch (pid) { case -1: @@ -141,8 +148,14 @@ job_run(const char *cmd, struct session *s, const char *cwd, } closefrom(STDERR_FILENO + 1); - execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); - fatal("execl failed"); + if (cmd != NULL) { + execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); + fatal("execl failed"); + } else { + argvp = cmd_copy_argv(argc, argv); + execvp(argvp[0], argvp); + fatal("execvp failed"); + } } sigprocmask(SIG_SETMASK, &oldset, NULL); @@ -152,7 +165,10 @@ job_run(const char *cmd, struct session *s, const char *cwd, job->state = JOB_RUNNING; job->flags = flags; - job->cmd = xstrdup(cmd); + if (cmd != NULL) + job->cmd = xstrdup(cmd); + else + job->cmd = cmd_stringify_argv(argc, argv); job->pid = pid; job->status = 0; diff --git a/popup.c b/popup.c index 675bb2f9..d63ee996 100644 --- a/popup.c +++ b/popup.c @@ -32,13 +32,7 @@ struct popup_data { struct cmdq_item *item; int flags; - char **lines; - u_int nlines; - - char *cmd; - struct cmd_find_state fs; struct screen s; - struct job *job; struct input_ctx *ictx; int status; @@ -105,54 +99,11 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) ttyctx->arg = pd; } -static void -popup_write_screen(struct client *c, struct popup_data *pd) -{ - struct cmdq_item *item = pd->item; - struct screen_write_ctx ctx; - char *copy, *next, *loop, *tmp; - struct format_tree *ft; - u_int i, y; - - ft = format_create(c, item, FORMAT_NONE, 0); - if (cmd_find_valid_state(&pd->fs)) - format_defaults(ft, c, pd->fs.s, pd->fs.wl, pd->fs.wp); - else - format_defaults(ft, c, NULL, NULL, NULL); - - screen_write_start(&ctx, &pd->s); - screen_write_clearscreen(&ctx, 8); - - y = 0; - for (i = 0; i < pd->nlines; i++) { - if (y == pd->sy - 2) - break; - copy = next = xstrdup(pd->lines[i]); - while ((loop = strsep(&next, "\n")) != NULL) { - if (y == pd->sy - 2) - break; - tmp = format_expand(ft, loop); - screen_write_cursormove(&ctx, 0, y, 0); - format_draw(&ctx, &grid_default_cell, pd->sx - 2, tmp, - NULL); - free(tmp); - y++; - } - free(copy); - } - - format_free(ft); - screen_write_cursormove(&ctx, 0, y, 0); - screen_write_stop(&ctx); -} - static struct screen * popup_mode_cb(struct client *c, u_int *cx, u_int *cy) { struct popup_data *pd = c->overlay_data; - if (pd->ictx == NULL) - return (0); *cx = pd->px + 1 + pd->s.cx; *cy = pd->py + 1 + pd->s.cy; return (&pd->s); @@ -200,14 +151,12 @@ popup_free_cb(struct client *c) { struct popup_data *pd = c->overlay_data; struct cmdq_item *item = pd->item; - u_int i; if (pd->cb != NULL) pd->cb(pd->status, pd->arg); if (item != NULL) { - if (pd->ictx != NULL && - cmdq_get_client(item) != NULL && + if (cmdq_get_client(item) != NULL && cmdq_get_client(item)->session == NULL) cmdq_get_client(item)->retval = pd->status; cmdq_continue(item); @@ -216,15 +165,9 @@ popup_free_cb(struct client *c) if (pd->job != NULL) job_free(pd->job); - if (pd->ictx != NULL) - input_free(pd->ictx); - - for (i = 0; i < pd->nlines; i++) - free(pd->lines[i]); - free(pd->lines); + input_free(pd->ictx); screen_free(&pd->s); - free(pd->cmd); free(pd); } @@ -263,9 +206,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd, pd->sy = m->y - pd->py; 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) + if (pd->job != NULL) job_resize(pd->job, pd->sx - 2, pd->sy - 2); server_redraw_client(c); } @@ -276,13 +217,8 @@ popup_key_cb(struct client *c, struct key_event *event) { struct popup_data *pd = c->overlay_data; struct mouse_event *m = &event->m; - struct cmd_find_state *fs = &pd->fs; - struct format_tree *ft; - const char *cmd, *buf; + const char *buf; size_t len; - struct cmdq_state *state; - enum cmd_parse_status status; - char *error; if (KEYC_IS_MOUSE(event->key)) { if (pd->dragging != OFF) { @@ -314,13 +250,11 @@ popup_key_cb(struct client *c, struct key_event *event) } } - if (pd->ictx != NULL && (pd->flags & POPUP_WRITEKEYS)) { - if (((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0 || - pd->job == NULL) && - (event->key == '\033' || event->key == '\003')) - return (1); - if (pd->job == NULL) - return (0); + if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) || + pd->job == NULL) && + (event->key == '\033' || event->key == '\003')) + return (1); + if (pd->job != NULL) { if (KEYC_IS_MOUSE(event->key)) { /* Must be inside, checked already. */ if (!input_key_get_mouse(&pd->s, m, m->x - pd->px - 1, @@ -330,40 +264,8 @@ popup_key_cb(struct client *c, struct key_event *event) return (0); } input_key(&pd->s, job_get_event(pd->job), event->key); - return (0); } - - if (pd->cmd == NULL) - return (1); - - ft = format_create(NULL, pd->item, FORMAT_NONE, 0); - if (cmd_find_valid_state(fs)) - format_defaults(ft, c, fs->s, fs->wl, fs->wp); - else - format_defaults(ft, c, NULL, NULL, NULL); - format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key, 0)); - if (KEYC_IS_MOUSE(event->key)) { - format_add(ft, "popup_mouse", "1"); - format_add(ft, "popup_mouse_x", "%u", m->x - pd->px); - format_add(ft, "popup_mouse_y", "%u", m->y - pd->py); - } - cmd = format_expand(ft, pd->cmd); - format_free(ft); - - if (pd->item != NULL) - event = cmdq_get_event(pd->item); - else - event = NULL; - state = cmdq_new_state(&pd->fs, event, 0); - - status = cmd_parse_and_append(cmd, NULL, c, state, &error); - if (status == CMD_PARSE_ERROR) { - cmdq_append(c, cmdq_get_error(error)); - free(error); - } - cmdq_free_state(state); - - return (1); + return (0); out: pd->lx = m->x; @@ -416,62 +318,12 @@ popup_job_complete_cb(struct job *job) server_client_clear_overlay(pd->c); } -u_int -popup_height(u_int nlines, const char **lines) -{ - char *copy, *next, *loop; - u_int i, height = 0; - - for (i = 0; i < nlines; i++) { - copy = next = xstrdup(lines[i]); - while ((loop = strsep(&next, "\n")) != NULL) - height++; - free(copy); - } - - return (height); -} - -u_int -popup_width(struct cmdq_item *item, u_int nlines, const char **lines, - struct client *c, struct cmd_find_state *fs) -{ - char *copy, *next, *loop, *tmp; - struct format_tree *ft; - u_int i, width = 0, tmpwidth; - - ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); - if (fs != NULL && cmd_find_valid_state(fs)) - format_defaults(ft, c, fs->s, fs->wl, fs->wp); - else - format_defaults(ft, c, NULL, NULL, NULL); - - for (i = 0; i < nlines; i++) { - copy = next = xstrdup(lines[i]); - while ((loop = strsep(&next, "\n")) != NULL) { - tmp = format_expand(ft, loop); - tmpwidth = format_width(tmp); - if (tmpwidth > width) - width = tmpwidth; - free(tmp); - } - free(copy); - } - - format_free(ft); - return (width); -} - int popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, - u_int sy, u_int nlines, const char **lines, const char *shellcmd, - const char *cmd, const char *cwd, struct client *c, - struct cmd_find_state *fs, popup_close_cb cb, void *arg) + u_int sy, const char *shellcmd, int argc, char **argv, const char *cwd, + struct client *c, struct session *s, popup_close_cb cb, void *arg) { struct popup_data *pd; - u_int i; - struct session *s; - int jobflags; if (sx < 3 || sy < 3) return (-1); @@ -489,39 +341,17 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->arg = arg; pd->status = 128 + SIGHUP; - if (fs != NULL) - cmd_find_copy_state(&pd->fs, fs); screen_init(&pd->s, sx - 2, sy - 2, 0); - if (cmd != NULL) - pd->cmd = xstrdup(cmd); - pd->px = px; pd->py = py; pd->sx = sx; pd->sy = sy; - pd->nlines = nlines; - if (pd->nlines != 0) - pd->lines = xreallocarray(NULL, pd->nlines, sizeof *pd->lines); - - for (i = 0; i < pd->nlines; i++) - pd->lines[i] = xstrdup(lines[i]); - popup_write_screen(c, pd); - - if (shellcmd != NULL) { - if (fs != NULL) - s = fs->s; - else - s = NULL; - jobflags = JOB_NOWAIT|JOB_PTY; - if (flags & POPUP_WRITEKEYS) - jobflags |= JOB_KEEPWRITE; - pd->job = job_run(shellcmd, s, cwd, popup_job_update_cb, - popup_job_complete_cb, NULL, pd, jobflags, pd->sx - 2, - pd->sy - 2); - pd->ictx = input_init(NULL, job_get_event(pd->job)); - } + pd->job = job_run(shellcmd, argc, argv, s, cwd, + popup_job_update_cb, popup_job_complete_cb, NULL, pd, + JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, pd->sx - 2, pd->sy - 2); + pd->ictx = input_init(NULL, job_get_event(pd->job)); server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, popup_draw_cb, popup_key_cb, popup_free_cb, pd); @@ -607,9 +437,8 @@ popup_editor(struct client *c, const char *buf, size_t len, py = (c->tty.sy / 2) - (sy / 2); xasprintf(&cmd, "%s %s", editor, path); - if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, - 0, NULL, cmd, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb, - pe) != 0) { + if (popup_display(POPUP_CLOSEEXIT, NULL, px, py, sx, sy, cmd, 0, NULL, + _PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) { popup_editor_free(pe); free(cmd); return (-1); diff --git a/spawn.c b/spawn.c index 41ffe612..9a801a38 100644 --- a/spawn.c +++ b/spawn.c @@ -265,8 +265,9 @@ spawn_pane(struct spawn_context *sc, char **cause) } /* - * Now we have a pane with nothing running in it ready for the new process. - * Work out the command and arguments and store the working directory. + * Now we have a pane with nothing running in it ready for the new + * process. Work out the command and arguments and store the working + * directory. */ if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) { cmd = options_get_string(s->options, "default-command"); diff --git a/tmux.1 b/tmux.1 index 4046dbf7..8b83732e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4923,9 +4923,6 @@ The following variables are available, where appropriate: .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" .It Li "pid" Ta "" Ta "Server PID" -.It Li "popup_key" Ta "" Ta "Key pressed in popup" -.It Li "popup_mouse_x" Ta "" Ta "Mouse X position in popup" -.It Li "popup_mouse_y" Ta "" Ta "Mouse Y position in popup" .It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated" .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" @@ -5584,58 +5581,24 @@ lists the format variables and their values. forwards any input read from stdin to the empty pane given by .Ar target-pane . .It Xo Ic display-popup -.Op Fl CEK +.Op Fl CE .Op Fl c Ar target-client .Op Fl d Ar start-directory .Op Fl h Ar height -.Op Fl R Ar shell-command .Op Fl t Ar target-pane .Op Fl w Ar width .Op Fl x Ar position .Op Fl y Ar position -.Op Ar command Ar line Ar ... +.Op Ar shell-command .Xc .D1 (alias: Ic popup ) -Display a popup on +Display a popup running +.Ar shell-command +on .Ar target-client . A popup is a rectangular box drawn over the top of any panes. Panes are not updated while a popup is present. -The popup content may be given in two ways: -.Bl -enum -offset Ds -.It -A set of lines as arguments. -Each line is a format which is expanded using -.Ar target-pane -as the target. -If a line contains newlines it is split into multiple lines. -Lines may use styles, see the -.Sx STYLES -section. -.It -A shell command given by -.Fl R -which is run and any output shown in the pane. -.El .Pp -The first argument, -.Ar command , -is a -.Nm -command which is run when a key is pressed. -The key is available in the -.Ql popup_key -format. -After -.Ar command -is run, the popup is closed. -It may be empty to discard any key presses. -If -.Fl K -is given together with -.Fl R , -key presses are instead passed to the -.Fl R -shell command. .Fl E closes the popup automatically when .Ar shell-command @@ -5645,14 +5608,6 @@ Two closes the popup only if .Ar shell-command exited with success. -With -.Fl K , -.Ql Escape -and -.Ql C-c -close the popup unless -.Fl E -is also given. .Pp .Fl x and @@ -5665,11 +5620,7 @@ and .Fl h give the width and height - both may be a percentage (followed by .Ql % ) . -If omitted, without -.Fl R -they are calculated from the given lines and with -.Fl R -they use half the terminal size. +If omitted, half of the terminal size is used. .Pp The .Fl C diff --git a/tmux.h b/tmux.h index fb15356a..d5e86e13 100644 --- a/tmux.h +++ b/tmux.h @@ -2067,9 +2067,9 @@ typedef void (*job_free_cb) (void *); #define JOB_NOWAIT 0x1 #define JOB_KEEPWRITE 0x2 #define JOB_PTY 0x4 -struct job *job_run(const char *, struct session *, const char *, - job_update_cb, job_complete_cb, job_free_cb, void *, int, - int, int); +struct job *job_run(const char *, int, char **, struct session *, + const char *, job_update_cb, job_complete_cb, job_free_cb, + void *, int, int, int); void job_free(struct job *); void job_resize(struct job *, u_int, u_int); void job_check_died(pid_t, int); @@ -3038,18 +3038,13 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int, menu_choice_cb, void *); /* popup.c */ -#define POPUP_WRITEKEYS 0x1 -#define POPUP_CLOSEEXIT 0x2 -#define POPUP_CLOSEEXITZERO 0x4 +#define POPUP_CLOSEEXIT 0x1 +#define POPUP_CLOSEEXITZERO 0x2 typedef void (*popup_close_cb)(int, void *); typedef void (*popup_finish_edit_cb)(char *, size_t, void *); -u_int popup_width(struct cmdq_item *, u_int, const char **, - struct client *, struct cmd_find_state *); -u_int popup_height(u_int, const char **); int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, - u_int, u_int, const char **, const char *, const char *, - const char *, struct client *, struct cmd_find_state *, - popup_close_cb, void *); + u_int, const char *, int, char **, const char *, + struct client *, struct session *, popup_close_cb, void *); int popup_editor(struct client *, const char *, size_t, popup_finish_edit_cb, void *); diff --git a/window-copy.c b/window-copy.c index 531431c8..4558ed48 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3963,8 +3963,8 @@ window_copy_pipe_run(struct window_mode_entry *wme, struct session *s, if (cmd == NULL || *cmd == '\0') cmd = options_get_string(global_options, "copy-command"); if (cmd != NULL && *cmd != '\0') { - job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT, - -1, -1); + job = job_run(cmd, 0, NULL, s, NULL, NULL, NULL, NULL, NULL, + JOB_NOWAIT, -1, -1); bufferevent_write(job_get_event(job), buf, *len); } return (buf); From 81f9a23d25237f2b0c52a2867ddd2db59cc8f368 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Mar 2021 11:00:38 +0000 Subject: [PATCH 0722/1006] Do not use NULL active window; also do not leak window name. GitHub issue 2590 from Chester Liu. --- names.c | 2 ++ spawn.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/names.c b/names.c index f437b53e..09b33082 100644 --- a/names.c +++ b/names.c @@ -109,6 +109,8 @@ default_window_name(struct window *w) { char *cmd, *s; + if (w->active == NULL) + return (xstrdup("")); cmd = cmd_stringify_argv(w->active->argc, w->active->argv); if (cmd != NULL && *cmd != '\0') s = parse_window_name(cmd); diff --git a/spawn.c b/spawn.c index 9a801a38..e3f8debe 100644 --- a/spawn.c +++ b/spawn.c @@ -184,7 +184,7 @@ spawn_window(struct spawn_context *sc, char **cause) NULL); options_set_number(w->options, "automatic-rename", 0); } else - w->name = xstrdup(default_window_name(w)); + w->name = default_window_name(w); } /* Switch to the new window if required. */ From 1466b570eedda0423d5a386d2b16b7ff0c0e477c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 2 Mar 2021 12:05:15 +0000 Subject: [PATCH 0723/1006] Update CHANGES. --- CHANGES | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 8a438f81..a81f160c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,9 +1,69 @@ -CHANGES FROM 3.2 TO 3.3 - -XXX - CHANGES FROM 3.1c TO 3.2 +* Improve performance of format evaluation. + +* Make jump command support UTF-8 in copy mode. + +* Support X11 colour names and other colour formats for OSC 10 and 11. + +* Add "pipe" variants of "copy-pipe" commands which do not copy. + +* Include "focused" in client flags. + +* Send Unicode directional isolate characters around horizontal pane borders if + the terminal supports UTF-8 and an extension terminfo(5) capability "Bidi" is + present. + +* Add a -S flag to new-window to make it select the existing window if one + with the given name already exists rather than failing with an error. + +* Addd a format modifier to check if a window or session name exists (N/w or + N/s). + +* Add compat clock_gettime for older macOS. + +* Add a no-detached choice to detach-on-destroy which detaches only if there + are no other detached sessions to switch to. + +* Add rectangle-on and rectangle-off copy mode commands. + +* Change so that window_flags escapes # automatically. A new format + window_raw_flags contains the old unescaped version. + +* Add -N flag to never start server even if command would normally do so. + +* With incremental search, start empty and only repeat the previous search if + the user tries to search again with an empty prompt. + +* Add a value for remain-on-exit that only keeps the pane if the program + failed. + +* Add a -C flag to run-shell to use a tmux command rather than a shell command. + +* Do not list user options with show-hooks. + +* Remove current match indicator in copy mode which can't work anymore since we + only search the visible region. + +* Make synchronize-panes a pane option and add -U flag to set-option to unset + an option on all panes. + +* Make replacement of ##s consistent when drawing formats, whether followed by + [ or not. Add a flag (e) to the q: format modifier to double up #s + +* Add -N flag to display-panes to ignore keys. + +* Change how escaping is processed for formats so that ## and # can be used in + styles. + +* Add a 'w' format modifier for string width. + +* Add support for Haiku. + +* Expand menu and popup -x and -y as formats. + +* Add numeric comparisons for formats. + * 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 @@ -271,9 +331,9 @@ CHANGES FROM 3.1c TO 3.2 * Change default position for display-menu -x and -y to centre rather than top left. -* Add support for per-client transient popups, similar to menus. These are - created with new command display-popup. Popups may either show fixed text and - trigger a tmux command when a key is pressed, or run a program (-R flag). +* Add support for per-client transient popups, similar to menus but which are + connected to an external command (like a pane). These are created with new + command display-popup. * Change double and triple click bindings so that only one is fired (previously double click was fired on the way to triple click). Also add default double From fb039d5b82c9f7b3682f2583c33c13ad6b561dfd Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 2 Mar 2021 12:09:59 +0000 Subject: [PATCH 0724/1006] paths.h is compat. --- cmd-display-menu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 27a4c1d7..5a5aabcd 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -18,7 +18,6 @@ #include -#include #include #include From b243f1b2ee19d5a019b0365f31523130ad26c2c5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 2 Mar 2021 12:37:23 +0000 Subject: [PATCH 0725/1006] No sys/queue.h. --- proc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/proc.c b/proc.c index a1a2b36a..958a9483 100644 --- a/proc.c +++ b/proc.c @@ -17,7 +17,6 @@ */ #include -#include #include #include #include From de4ac37baa2d32c3a3597be39eeac953c6159f77 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 2 Mar 2021 12:37:52 +0000 Subject: [PATCH 0726/1006] 3.3. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 08671c71..4175f5c8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.2-rc4) +AC_INIT([tmux], next-3.3) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 0792b65863f45a4fe8dcc2d787a4a75e9602d795 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 8 Mar 2021 08:59:13 +0000 Subject: [PATCH 0727/1006] Remove compat.h again. --- compat/closefrom.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/compat/closefrom.c b/compat/closefrom.c index 0175f364..be008239 100644 --- a/compat/closefrom.c +++ b/compat/closefrom.c @@ -14,8 +14,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "compat.h" - #ifndef HAVE_CLOSEFROM #include From 7bef887fd1051e2bbeb268888b51588ccaac306e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 8 Mar 2021 08:59:58 +0000 Subject: [PATCH 0728/1006] Update version. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4175f5c8..b8762e2f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], next-3.3) +AC_INIT([tmux], 3.2-rc5) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 81e57365102b8f7d067b1bd8b431529aef74fe14 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Mar 2021 08:24:09 +0000 Subject: [PATCH 0729/1006] Copy mode improvements from Anindya Mukherjee: - Fix word and word-end for wrapped lines. - Fix copying of selection end on wrapped lines. - Fix wrapped word selection edge case. - Update select-line to respect wrapped lines. - Update window_copy_..._pos() functions to use grid_reader. GitHub issue 2605. --- grid-reader.c | 10 ++--- window-copy.c | 120 ++++++++++++++++++++------------------------------ 2 files changed, 53 insertions(+), 77 deletions(-) diff --git a/grid-reader.c b/grid-reader.c index c011ea1d..ae2f4d2b 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -172,7 +172,7 @@ grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) /* Do not break up wrapped words. */ if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) - xx = grid_reader_line_length(gr) - 1; + xx = gr->gd->sx - 1; else xx = grid_reader_line_length(gr); yy = gr->gd->hsize + gr->gd->sy - 1; @@ -197,7 +197,7 @@ grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) - xx = grid_reader_line_length(gr) - 1; + xx = gr->gd->sx - 1; else xx = grid_reader_line_length(gr); } else @@ -216,7 +216,7 @@ grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) /* Do not break up wrapped words. */ if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) - xx = grid_reader_line_length(gr) - 1; + xx = gr->gd->sx - 1; else xx = grid_reader_line_length(gr); yy = gr->gd->hsize + gr->gd->sy - 1; @@ -241,7 +241,7 @@ grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) - xx = grid_reader_line_length(gr) - 1; + xx = gr->gd->sx - 1; else xx = grid_reader_line_length(gr); } else @@ -294,7 +294,7 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, GRID_LINE_WRAPPED) break; grid_reader_cursor_up(gr); - grid_reader_cursor_end_of_line(gr, 0, 0); + grid_reader_cursor_end_of_line(gr, 0, 1); } if (gr->cx > 0) gr->cx--; diff --git a/window-copy.c b/window-copy.c index 4558ed48..0723855d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1759,12 +1759,15 @@ window_copy_cmd_select_line(struct window_copy_cmd_state *cs) window_copy_cursor_start_of_line(wme); data->selrx = data->cx; data->selry = screen_hsize(data->backing) + data->cy - data->oy; - data->endselrx = window_copy_find_length(wme, data->selry); data->endselry = data->selry; window_copy_start_selection(wme); - for (; np > 1; np--) - window_copy_cursor_down(wme, 0); window_copy_cursor_end_of_line(wme); + data->endselry = screen_hsize(data->backing) + data->cy - data->oy; + data->endselrx = window_copy_find_length(wme, data->endselry); + for (; np > 1; np--) { + window_copy_cursor_down(wme, 0); + window_copy_cursor_end_of_line(wme); + } return (WINDOW_COPY_CMD_REDRAW); } @@ -1775,7 +1778,7 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct session *s = cs->s; struct window_copy_mode_data *data = wme->data; - u_int px, py; + u_int px, py, nextx, nexty; data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; @@ -1791,8 +1794,16 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) data->selry = py; window_copy_start_selection(wme); + /* Handle single character words. */ + nextx = px + 1; + nexty = py; + if (grid_get_line(data->backing->grid, nexty)->flags & + GRID_LINE_WRAPPED && nextx > screen_size_x(data->backing) - 1) { + nextx = 0; + nexty++; + } if (px >= window_copy_find_length(wme, py) || - !window_copy_in_set(wme, px + 1, py, data->ws)) + !window_copy_in_set(wme, nextx, nexty, data->ws)) window_copy_cursor_next_word_end(wme, data->ws, 1); else { window_copy_update_cursor(wme, px, data->cy); @@ -1801,7 +1812,10 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) } data->endselrx = data->cx; data->endselry = screen_hsize(data->backing) + data->cy - data->oy; - if (data->dx > data->endselrx) + if (data->dy > data->endselry) { + data->dy = data->endselry; + data->dx = data->endselrx; + } else if (data->dx > data->endselrx) data->dx = data->endselrx; return (WINDOW_COPY_CMD_REDRAW); @@ -3635,6 +3649,8 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, data->endsely = data->endselry; } else { /* Left to right selection. */ + if (yy < data->endselry) + yy = data->endselry; xx = window_copy_find_length(wme, yy); /* Reset the start. */ @@ -3929,8 +3945,12 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len) *len = 0; return (NULL); } - if (keys == MODEKEY_EMACS || lastex <= ey_last) - off -= 1; /* remove final \n (unless at end in vi mode) */ + /* Remove final \n (unless at end in vi mode). */ + if (keys == MODEKEY_EMACS || lastex <= ey_last) { + if (~grid_get_line(data->backing->grid, ey)->flags & + GRID_LINE_WRAPPED || lastex != ey_last) + off -= 1; + } *len = off; return (buf); } @@ -4531,6 +4551,7 @@ window_copy_cursor_next_word(struct window_mode_entry *wme, data->oy, oldy, px, py, 0); } +/* Compute the next place where a word ends. */ static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, const char *separators, u_int *ppx, u_int *ppy) @@ -4539,47 +4560,27 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct options *oo = wp->window->options; struct screen *back_s = data->backing; - u_int px, py, xx, yy; - int keys, expected = 1; + struct grid_reader gr; + u_int px, py, hsize; + int keys; px = data->cx; - py = screen_hsize(back_s) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); - yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + grid_reader_start(&gr, back_s->grid, px, py); keys = options_get_number(oo, "mode-keys"); - if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators)) - px++; - - /* - * First skip past any word characters, then any non-word characters. - * - * expected is initially set to 1 for the former and then 0 for the - * latter. - */ - do { - while (px > xx || - window_copy_in_set(wme, px, py, separators) == expected) { - /* Move down if we're past the end of the line. */ - if (px > xx) { - if (py == yy) - return; - py++; - px = 0; - xx = window_copy_find_length(wme, py); - } else - px++; - } - expected = !expected; - } while (expected == 0); - - if (keys == MODEKEY_VI && px != 0) - px--; - + if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators)) + grid_reader_cursor_right(&gr, 0, 0); + grid_reader_cursor_next_word_end(&gr, separators); + if (keys == MODEKEY_VI) + grid_reader_cursor_left(&gr); + grid_reader_get_cursor(&gr, &px, &py); *ppx = px; *ppy = py; } +/* Move to the next place where a word ends. */ static void window_copy_cursor_next_word_end(struct window_mode_entry *wme, const char *separators, int no_reset) @@ -4615,42 +4616,17 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, const char *separators, int already, u_int *ppx, u_int *ppy) { struct window_copy_mode_data *data = wme->data; + struct screen *back_s = data->backing; + struct grid_reader gr; u_int px, py, hsize; - hsize = screen_hsize(data->backing); px = data->cx; + hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; - /* Move back to the previous word character. */ - if (already || window_copy_in_set(wme, px, py, separators)) { - for (;;) { - if (px > 0) { - px--; - if (!window_copy_in_set(wme, px, py, - separators)) - break; - } else { - if (py == 0 || - (data->cy == 0 && - (hsize == 0 || data->oy > hsize - 1))) - goto out; - - py--; - px = window_copy_find_length(wme, py); - - /* Stop if separator at EOL. */ - if (px > 0 && window_copy_in_set(wme, px - 1, - py, separators)) - break; - } - } - } - - /* Move back to the beginning of this word. */ - while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators)) - px--; - -out: + grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_previous_word(&gr, separators, already); + grid_reader_get_cursor(&gr, &px, &py); *ppx = px; *ppy = py; } From 7f87280cd539c26a9b088cca2de895dd86b60449 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Mar 2021 13:07:50 +0000 Subject: [PATCH 0730/1006] Allow cursor to be just after match if copying, GitHub issue 2602. --- window-copy.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/window-copy.c b/window-copy.c index 0723855d..3fc7ad3e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3366,8 +3366,11 @@ window_copy_match_at_cursor(struct window_copy_mode_data *data) cy = screen_hsize(data->backing) - data->oy + data->cy; if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0) return (NULL); - if (data->searchmark[at] == 0) - return (NULL); + if (data->searchmark[at] == 0) { + /* Allow one position after the match. */ + if (at == 0 || data->searchmark[--at] == 0) + return (NULL); + } window_copy_match_start_end(data, at, &start, &end); /* From d98f9f7fe56beefeb294184ffaed24b7ea510a97 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Mar 2021 06:31:05 +0000 Subject: [PATCH 0731/1006] Add split-window -Z to start the pane zoomed, GitHub issue 2591. --- cmd-join-pane.c | 2 +- cmd-rotate-window.c | 2 +- cmd-select-pane.c | 12 ++++++------ cmd-split-window.c | 9 ++++++--- cmd-swap-pane.c | 4 ++-- cmd-switch-client.c | 2 +- layout-custom.c | 2 +- layout-set.c | 8 ++++---- layout.c | 22 +++++++++++++--------- options.c | 2 +- spawn.c | 5 ++++- tmux.1 | 4 +++- tmux.h | 8 +++++--- window.c | 6 +++--- 14 files changed, 51 insertions(+), 37 deletions(-) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index fc95a6b8..3d3f0ac8 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -147,7 +147,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry); else TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); - layout_assign_pane(lc, src_wp); + layout_assign_pane(lc, src_wp, 0); recalculate_sizes(); diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 55c1dde2..450ffc17 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -52,7 +52,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) struct layout_cell *lc; u_int sx, sy, xoff, yoff; - window_push_zoom(w, args_has(args, 'Z')); + window_push_zoom(w, 0, args_has(args, 'Z')); if (args_has(args, 'D')) { wp = TAILQ_LAST(&w->panes, window_panes); diff --git a/cmd-select-pane.c b/cmd-select-pane.c index fa388548..7871fe05 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -117,7 +117,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) server_redraw_window_borders(lastwp->window); server_status_window(lastwp->window); } else { - if (window_push_zoom(w, args_has(args, 'Z'))) + if (window_push_zoom(w, 0, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, lastwp); if (window_set_active_pane(w, lastwp, 1)) { @@ -171,19 +171,19 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'L')) { - window_push_zoom(w, 1); + window_push_zoom(w, 0, 1); wp = window_pane_find_left(wp); window_pop_zoom(w); } else if (args_has(args, 'R')) { - window_push_zoom(w, 1); + window_push_zoom(w, 0, 1); wp = window_pane_find_right(wp); window_pop_zoom(w); } else if (args_has(args, 'U')) { - window_push_zoom(w, 1); + window_push_zoom(w, 0, 1); wp = window_pane_find_up(wp); window_pop_zoom(w); } else if (args_has(args, 'D')) { - window_push_zoom(w, 1); + window_push_zoom(w, 0, 1); wp = window_pane_find_down(wp); window_pop_zoom(w); } @@ -220,7 +220,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) activewp = w->active; if (wp == activewp) return (CMD_RETURN_NORMAL); - if (window_push_zoom(w, args_has(args, 'Z'))) + if (window_push_zoom(w, 0, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) diff --git a/cmd-split-window.c b/cmd-split-window.c index c9d92fae..9e27bba1 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -40,8 +40,8 @@ const struct cmd_entry cmd_split_window_entry = { .name = "split-window", .alias = "splitw", - .args = { "bc:de:fF:hIl:p:Pt:v", 0, -1 }, - .usage = "[-bdefhIPv] [-c start-directory] [-e environment] " + .args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1 }, + .usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] " "[-F format] [-l size] " CMD_TARGET_PANE_USAGE " [command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -111,7 +111,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) } else size = -1; - server_unzoom_window(wp->window); + window_push_zoom(wp->window, 1, args_has(args, 'Z')); input = (args_has(args, 'I') && args->argc == 0); flags = 0; @@ -153,6 +153,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) sc.flags = flags; if (args_has(args, 'd')) sc.flags |= SPAWN_DETACHED; + if (args_has(args, 'Z')) + sc.flags |= SPAWN_ZOOM; if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { cmdq_error(item, "create pane failed: %s", cause); @@ -169,6 +171,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) } if (!args_has(args, 'd')) cmd_find_from_winlink_pane(current, wl, new_wp, 0); + window_pop_zoom(wp->window); server_redraw_window(wp->window); server_status_session(s); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index dd981b9a..12bc20b4 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -58,7 +58,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) src_w = source->wl->window; src_wp = source->wp; - if (window_push_zoom(dst_w, args_has(args, 'Z'))) + if (window_push_zoom(dst_w, 0, args_has(args, 'Z'))) server_redraw_window(dst_w); if (args_has(args, 'D')) { @@ -73,7 +73,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); } - if (src_w != dst_w && window_push_zoom(src_w, args_has(args, 'Z'))) + if (src_w != dst_w && window_push_zoom(src_w, 0, args_has(args, 'Z'))) server_redraw_window(src_w); if (src_wp == dst_wp) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index fc7f9d75..b10496e3 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -118,7 +118,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); if (wl != NULL && wp != NULL && wp != wl->window->active) { w = wl->window; - if (window_push_zoom(w, args_has(args, 'Z'))) + if (window_push_zoom(w, 0, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); window_set_active_pane(w, wp, 1); diff --git a/layout-custom.c b/layout-custom.c index 097dabe6..e7fb4253 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -233,7 +233,7 @@ layout_parse(struct window *w, const char *layout) /* Update pane offsets and sizes. */ layout_fix_offsets(w); - layout_fix_panes(w); + layout_fix_panes(w, NULL); recalculate_sizes(); layout_print_cell(lc, __func__, 0); diff --git a/layout-set.c b/layout-set.c index 9ef28416..c702817d 100644 --- a/layout-set.c +++ b/layout-set.c @@ -160,7 +160,7 @@ layout_set_even(struct window *w, enum layout_type type) /* Fix cell offsets. */ layout_fix_offsets(w); - layout_fix_panes(w); + layout_fix_panes(w, NULL); layout_print_cell(w->layout_root, __func__, 1); @@ -270,7 +270,7 @@ layout_set_main_h(struct window *w) /* Fix cell offsets. */ layout_fix_offsets(w); - layout_fix_panes(w); + layout_fix_panes(w, NULL); layout_print_cell(w->layout_root, __func__, 1); @@ -368,7 +368,7 @@ layout_set_main_v(struct window *w) /* Fix cell offsets. */ layout_fix_offsets(w); - layout_fix_panes(w); + layout_fix_panes(w, NULL); layout_print_cell(w->layout_root, __func__, 1); @@ -477,7 +477,7 @@ layout_set_tiled(struct window *w) /* Fix cell offsets. */ layout_fix_offsets(w); - layout_fix_panes(w); + layout_fix_panes(w, NULL); layout_print_cell(w->layout_root, __func__, 1); diff --git a/layout.c b/layout.c index 37214d02..04a13b0c 100644 --- a/layout.c +++ b/layout.c @@ -286,7 +286,7 @@ layout_add_border(struct window *w, struct layout_cell *lc, int status) /* Update pane offsets and sizes based on their cells. */ void -layout_fix_panes(struct window *w) +layout_fix_panes(struct window *w, struct window_pane *skip) { struct window_pane *wp; struct layout_cell *lc; @@ -294,7 +294,7 @@ layout_fix_panes(struct window *w) status = options_get_number(w->options, "pane-border-status"); TAILQ_FOREACH(wp, &w->panes, entry) { - if ((lc = wp->layout_cell) == NULL) + if ((lc = wp->layout_cell) == NULL || wp == skip) continue; wp->xoff = lc->xoff; @@ -482,7 +482,7 @@ layout_init(struct window *w, struct window_pane *wp) lc = w->layout_root = layout_create_cell(NULL); layout_set_size(lc, w->sx, w->sy, 0, 0); layout_make_leaf(lc, wp); - layout_fix_panes(w); + layout_fix_panes(w, NULL); } void @@ -540,7 +540,7 @@ layout_resize(struct window *w, u_int sx, u_int sy) /* Fix cell offsets. */ layout_fix_offsets(w); - layout_fix_panes(w); + layout_fix_panes(w, NULL); } /* Resize a pane to an absolute size. */ @@ -600,7 +600,7 @@ layout_resize_layout(struct window *w, struct layout_cell *lc, /* Fix cell offsets. */ layout_fix_offsets(w); - layout_fix_panes(w); + layout_fix_panes(w, NULL); notify_window("window-layout-changed", w); } @@ -704,10 +704,14 @@ layout_resize_pane_shrink(struct window *w, struct layout_cell *lc, /* Assign window pane to newly split cell. */ void -layout_assign_pane(struct layout_cell *lc, struct window_pane *wp) +layout_assign_pane(struct layout_cell *lc, struct window_pane *wp, + int do_not_resize) { layout_make_leaf(lc, wp); - layout_fix_panes(wp->window); + if (do_not_resize) + layout_fix_panes(wp->window, wp); + else + layout_fix_panes(wp->window, NULL); } /* Calculate the new pane size for resized parent. */ @@ -1040,7 +1044,7 @@ layout_close_pane(struct window_pane *wp) /* Fix pane offsets and sizes. */ if (w->layout_root != NULL) { layout_fix_offsets(w); - layout_fix_panes(w); + layout_fix_panes(w, NULL); } notify_window("window-layout-changed", w); } @@ -1109,7 +1113,7 @@ layout_spread_out(struct window_pane *wp) do { if (layout_spread_cell(w, parent)) { layout_fix_offsets(w); - layout_fix_panes(w); + layout_fix_panes(w, NULL); break; } } while ((parent = parent->parent) != NULL); diff --git a/options.c b/options.c index 9bc89db3..52f4f67e 100644 --- a/options.c +++ b/options.c @@ -1115,7 +1115,7 @@ options_push_changes(const char *name) } if (strcmp(name, "pane-border-status") == 0) { RB_FOREACH(w, windows, &windows) - layout_fix_panes(w); + layout_fix_panes(w, NULL); } RB_FOREACH(s, sessions, &sessions) status_update_cache(s); diff --git a/spawn.c b/spawn.c index e3f8debe..98b70312 100644 --- a/spawn.c +++ b/spawn.c @@ -261,7 +261,10 @@ spawn_pane(struct spawn_context *sc, char **cause) layout_init(w, new_wp); } else { new_wp = window_add_pane(w, sc->wp0, hlimit, sc->flags); - layout_assign_pane(sc->lc, new_wp); + if (sc->flags & SPAWN_ZOOM) + layout_assign_pane(sc->lc, new_wp, 1); + else + layout_assign_pane(sc->lc, new_wp, 0); } /* diff --git a/tmux.1 b/tmux.1 index 8b83732e..da3c0e42 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2804,7 +2804,7 @@ is given and the selected window is already the current window, the command behaves like .Ic last-window . .It Xo Ic split-window -.Op Fl bdfhIvP +.Op Fl bdfhIvPZ .Op Fl c Ar start-directory .Op Fl e Ar environment .Op Fl l Ar size @@ -2840,6 +2840,8 @@ option creates a new pane spanning the full window height (with or full window width (with .Fl v ) , instead of splitting the active pane. +.Fl Z +zooms if the window is not zoomed, or keeps it zoomed if already zoomed. .Pp An empty .Ar shell-command diff --git a/tmux.h b/tmux.h index d5e86e13..3a75e678 100644 --- a/tmux.h +++ b/tmux.h @@ -1862,6 +1862,7 @@ struct spawn_context { #define SPAWN_NONOTIFY 0x10 #define SPAWN_FULLSIZE 0x20 #define SPAWN_EMPTY 0x40 +#define SPAWN_ZOOM 0x80 }; /* Mode tree sort order. */ @@ -2752,7 +2753,7 @@ void window_resize(struct window *, u_int, u_int, int, int); void window_pane_send_resize(struct window_pane *, int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); -int window_push_zoom(struct window *, int); +int window_push_zoom(struct window *, int, int); int window_pop_zoom(struct window *); void window_lost_pane(struct window *, struct window_pane *); void window_remove_pane(struct window *, struct window_pane *); @@ -2815,7 +2816,7 @@ void layout_set_size(struct layout_cell *, u_int, u_int, u_int, void layout_make_leaf(struct layout_cell *, struct window_pane *); void layout_make_node(struct layout_cell *, enum layout_type); void layout_fix_offsets(struct window *); -void layout_fix_panes(struct window *); +void layout_fix_panes(struct window *, struct window_pane *); void layout_resize_adjust(struct window *, struct layout_cell *, enum layout_type, int); void layout_init(struct window *, struct window_pane *); @@ -2825,7 +2826,8 @@ void layout_resize_pane(struct window_pane *, enum layout_type, int, int); void layout_resize_pane_to(struct window_pane *, enum layout_type, u_int); -void layout_assign_pane(struct layout_cell *, struct window_pane *); +void layout_assign_pane(struct layout_cell *, struct window_pane *, + int); struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, int, int); void layout_close_pane(struct window_pane *); diff --git a/window.c b/window.c index 07ef513f..36cf12d9 100644 --- a/window.c +++ b/window.c @@ -622,18 +622,18 @@ window_unzoom(struct window *w) wp->layout_cell = wp->saved_layout_cell; wp->saved_layout_cell = NULL; } - layout_fix_panes(w); + layout_fix_panes(w, NULL); notify_window("window-layout-changed", w); return (0); } int -window_push_zoom(struct window *w, int flag) +window_push_zoom(struct window *w, int always, int flag) { log_debug("%s: @%u %d", __func__, w->id, flag && (w->flags & WINDOW_ZOOMED)); - if (flag && (w->flags & WINDOW_ZOOMED)) + if (flag && (always || (w->flags & WINDOW_ZOOMED))) w->flags |= WINDOW_WASZOOMED; else w->flags &= ~WINDOW_WASZOOMED; From 3eb91efba160eff0b077a5fee902edb632f7fdca Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Mar 2021 06:41:04 +0000 Subject: [PATCH 0732/1006] Add an "absolute-centre" alignment to use the centre of the total space instead of only the available space. From Magnus Gross in GitHub issue 2578. --- format-draw.c | 214 ++++++++++++++++++++++++++++++++++++++++++------ options-table.c | 2 +- style.c | 4 + tmux.1 | 7 +- tmux.h | 3 +- 5 files changed, 199 insertions(+), 31 deletions(-) diff --git a/format-draw.c b/format-draw.c index 67b961d9..cf0bdf5f 100644 --- a/format-draw.c +++ b/format-draw.c @@ -157,13 +157,14 @@ format_draw_put_list(struct screen_write_ctx *octx, static void format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, - struct format_ranges *frs) + struct screen *abs_centre, struct format_ranges *frs) { - u_int width_left, width_centre, width_right; + u_int width_left, width_centre, width_right, width_abs_centre; width_left = left->cx; width_centre = centre->cx; width_right = right->cx; + width_abs_centre = abs_centre->cx; /* * Try to keep as much of the left and right as possible at the expense @@ -199,23 +200,34 @@ format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx, - width_centre / 2, centre->cx / 2 - width_centre / 2, width_centre); + + /* + * Write abs_centre in the perfect centre of all horizontal space. + */ + if (width_abs_centre > available) + width_abs_centre = available; + format_draw_put(octx, ocx, ocy, abs_centre, frs, + (available - width_abs_centre) / 2, + 0, + width_abs_centre); } /* Draw format with list on the left. */ static void format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, - struct screen *list, struct screen *list_left, struct screen *list_right, - struct screen *after, int focus_start, int focus_end, - struct format_ranges *frs) + struct screen *abs_centre, struct screen *list, struct screen *list_left, + struct screen *list_right, struct screen *after, int focus_start, + int focus_end, struct format_ranges *frs) { u_int width_left, width_centre, width_right; - u_int width_list, width_after; + u_int width_list, width_after, width_abs_centre; struct screen_write_ctx ctx; width_left = left->cx; width_centre = centre->cx; width_right = right->cx; + width_abs_centre = abs_centre->cx; width_list = list->cx; width_after = after->cx; @@ -247,7 +259,7 @@ format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx, screen_write_stop(&ctx); format_draw_none(octx, available, ocx, ocy, left, centre, - right, frs); + right, abs_centre, frs); return; } @@ -291,23 +303,34 @@ format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx, focus_start = focus_end = 0; format_draw_put_list(octx, ocx, ocy, width_left, width_list, list, list_left, list_right, focus_start, focus_end, frs); + + /* + * Write abs_centre in the perfect centre of all horizontal space. + */ + if (width_abs_centre > available) + width_abs_centre = available; + format_draw_put(octx, ocx, ocy, abs_centre, frs, + (available - width_abs_centre) / 2, + 0, + width_abs_centre); } /* Draw format with list in the centre. */ static void format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, - struct screen *list, struct screen *list_left, struct screen *list_right, - struct screen *after, int focus_start, int focus_end, - struct format_ranges *frs) + struct screen *abs_centre, struct screen *list, struct screen *list_left, + struct screen *list_right, struct screen *after, int focus_start, + int focus_end, struct format_ranges *frs) { - u_int width_left, width_centre, width_right; - u_int width_list, width_after, middle; + u_int width_left, width_centre, width_right, middle; + u_int width_list, width_after, width_abs_centre; struct screen_write_ctx ctx; width_left = left->cx; width_centre = centre->cx; width_right = right->cx; + width_abs_centre = abs_centre->cx; width_list = list->cx; width_after = after->cx; @@ -339,7 +362,7 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx, screen_write_stop(&ctx); format_draw_none(octx, available, ocx, ocy, left, centre, - right, frs); + right, abs_centre, frs); return; } @@ -388,23 +411,34 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx, format_draw_put_list(octx, ocx, ocy, middle - width_list / 2, width_list, list, list_left, list_right, focus_start, focus_end, frs); + + /* + * Write abs_centre in the perfect centre of all horizontal space. + */ + if (width_abs_centre > available) + width_abs_centre = available; + format_draw_put(octx, ocx, ocy, abs_centre, frs, + (available - width_abs_centre) / 2, + 0, + width_abs_centre); } /* Draw format with list on the right. */ static void format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, - struct screen *list, struct screen *list_left, struct screen *list_right, - struct screen *after, int focus_start, int focus_end, - struct format_ranges *frs) + struct screen *abs_centre, struct screen *list, + struct screen *list_left, struct screen *list_right, struct screen *after, + int focus_start, int focus_end, struct format_ranges *frs) { u_int width_left, width_centre, width_right; - u_int width_list, width_after; + u_int width_list, width_after, width_abs_centre; struct screen_write_ctx ctx; width_left = left->cx; width_centre = centre->cx; width_right = right->cx; + width_abs_centre = abs_centre->cx; width_list = list->cx; width_after = after->cx; @@ -436,7 +470,7 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, screen_write_stop(&ctx); format_draw_none(octx, available, ocx, ocy, left, centre, - right, frs); + right, abs_centre, frs); return; } @@ -484,6 +518,118 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, format_draw_put_list(octx, ocx, ocy, available - width_list - width_after, width_list, list, list_left, list_right, focus_start, focus_end, frs); + + /* + * Write abs_centre in the perfect centre of all horizontal space. + */ + if (width_abs_centre > available) + width_abs_centre = available; + format_draw_put(octx, ocx, ocy, abs_centre, frs, + (available - width_abs_centre) / 2, + 0, + width_abs_centre); +} + +static void +format_draw_absolute_centre(struct screen_write_ctx *octx, u_int available, + u_int ocx, u_int ocy, struct screen *left, struct screen *centre, + struct screen *right, struct screen *abs_centre, struct screen *list, + struct screen *list_left, struct screen *list_right, struct screen *after, + int focus_start, int focus_end, struct format_ranges *frs) +{ + u_int width_left, width_centre, width_right, width_abs_centre; + u_int width_list, width_after, middle, abs_centre_offset; + + width_left = left->cx; + width_centre = centre->cx; + width_right = right->cx; + width_abs_centre = abs_centre->cx; + width_list = list->cx; + width_after = after->cx; + + /* + * Trim first centre, then the right, then the left. + */ + while (width_left + + width_centre + + width_right > available) { + if (width_centre > 0) + width_centre--; + else if (width_right > 0) + width_right--; + else + width_left--; + } + + /* + * We trim list after and abs_centre independently, as we are drawing + * them over the rest. Trim first the list, then after the list, then + * abs_centre. + */ + while (width_list + width_after + width_abs_centre > available) { + if (width_list > 0) + width_list--; + else if (width_after > 0) + width_after--; + else + width_abs_centre--; + } + + /* Write left at 0. */ + format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left); + + /* Write right at available - width_right. */ + format_draw_put(octx, ocx, ocy, right, frs, + available - width_right, + right->cx - width_right, + width_right); + + /* + * Keep writing centre at the relative centre. Only the list is written + * in the absolute centre of the horizontal space. + */ + middle = (width_left + ((available - width_right) - width_left) / 2); + + /* + * Write centre at + * middle - width_centre. + */ + format_draw_put(octx, ocx, ocy, centre, frs, + middle - width_centre, + 0, + width_centre); + + /* + * If there is no focus given, keep the centre in focus. + */ + if (focus_start == -1 || focus_end == -1) + focus_start = focus_end = list->cx / 2; + + /* + * We centre abs_centre and the list together, so their shared centre is + * in the perfect centre of horizontal space. + */ + abs_centre_offset = (available - width_list - width_abs_centre) / 2; + + /* + * Write abs_centre before the list. + */ + format_draw_put(octx, ocx, ocy, abs_centre, frs, abs_centre_offset, + 0, width_abs_centre); + abs_centre_offset += width_abs_centre; + + /* + * Draw the list in the absolute centre + */ + format_draw_put_list(octx, ocx, ocy, abs_centre_offset, width_list, + list, list_left, list_right, focus_start, focus_end, frs); + abs_centre_offset += width_list; + + /* + * Write after at the end of the centre + */ + format_draw_put(octx, ocx, ocy, after, frs, abs_centre_offset, 0, + width_after); } /* Draw multiple characters. */ @@ -506,6 +652,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, enum { LEFT, CENTRE, RIGHT, + ABSOLUTE_CENTRE, LIST, LIST_LEFT, LIST_RIGHT, @@ -514,6 +661,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, const char *names[] = { "LEFT", "CENTRE", "RIGHT", + "ABSOLUTE_CENTRE", "LIST", "LIST_LEFT", "LIST_RIGHT", @@ -522,7 +670,11 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, struct screen *os = octx->s, s[TOTAL]; struct screen_write_ctx ctx[TOTAL]; u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL]; - u_int map[] = { LEFT, LEFT, CENTRE, RIGHT }; + u_int map[] = { LEFT, + LEFT, + CENTRE, + RIGHT, + ABSOLUTE_CENTRE }; int focus_start = -1, focus_end = -1; int list_state = -1, fill = -1, even; enum style_align list_align = STYLE_ALIGN_DEFAULT; @@ -789,25 +941,35 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, case STYLE_ALIGN_DEFAULT: /* No list. */ format_draw_none(octx, available, ocx, ocy, &s[LEFT], - &s[CENTRE], &s[RIGHT], &frs); + &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs); break; case STYLE_ALIGN_LEFT: /* List is part of the left. */ format_draw_left(octx, available, ocx, ocy, &s[LEFT], - &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], - &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); + &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], + &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], + focus_start, focus_end, &frs); break; case STYLE_ALIGN_CENTRE: /* List is part of the centre. */ format_draw_centre(octx, available, ocx, ocy, &s[LEFT], - &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], - &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); + &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], + &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], + focus_start, focus_end, &frs); break; case STYLE_ALIGN_RIGHT: /* List is part of the right. */ format_draw_right(octx, available, ocx, ocy, &s[LEFT], - &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], - &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); + &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], + &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], + focus_start, focus_end, &frs); + break; + case STYLE_ALIGN_ABSOLUTE_CENTRE: + /* List is in the centre of the entire horizontal space. */ + format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT], + &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], + &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], + focus_start, focus_end, &frs); break; } diff --git a/options-table.c b/options-table.c index 9fc69db5..8ea56918 100644 --- a/options-table.c +++ b/options-table.c @@ -46,7 +46,7 @@ static const char *options_table_status_keys_list[] = { "emacs", "vi", NULL }; static const char *options_table_status_justify_list[] = { - "left", "centre", "right", NULL + "left", "centre", "right", "absolute-centre", NULL }; static const char *options_table_status_position_list[] = { "top", "bottom", NULL diff --git a/style.c b/style.c index 08614f9c..24b09882 100644 --- a/style.c +++ b/style.c @@ -139,6 +139,8 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->align = STYLE_ALIGN_CENTRE; else if (strcasecmp(tmp + 6, "right") == 0) sy->align = STYLE_ALIGN_RIGHT; + else if (strcasecmp(tmp + 6, "absolute-centre") == 0) + sy->align = STYLE_ALIGN_ABSOLUTE_CENTRE; else goto error; } else if (end > 5 && strncasecmp(tmp, "fill=", 5) == 0) { @@ -227,6 +229,8 @@ style_tostring(struct style *sy) tmp = "centre"; else if (sy->align == STYLE_ALIGN_RIGHT) tmp = "right"; + else if (sy->align == STYLE_ALIGN_ABSOLUTE_CENTRE) + tmp = "absolute-centre"; off += xsnprintf(s + off, sizeof s - off, "%salign=%s", comma, tmp); comma = ","; diff --git a/tmux.1 b/tmux.1 index da3c0e42..5c516fdb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3815,10 +3815,11 @@ seconds. By default, updates will occur every 15 seconds. A setting of zero disables redrawing at interval. .It Xo Ic status-justify -.Op Ic left | centre | right +.Op Ic left | centre | right | absolute-centre .Xc -Set the position of the window list component of the status line: left, centre -or right justified. +Set the position of the window list in the status line: left, centre or right. +centre puts the window list in the relative centre of the available free space; +absolute-centre uses the centre of the entire horizontal space. .It Xo Ic status-keys .Op Ic vi | emacs .Xc diff --git a/tmux.h b/tmux.h index 3a75e678..e8d2e8c3 100644 --- a/tmux.h +++ b/tmux.h @@ -740,7 +740,8 @@ enum style_align { STYLE_ALIGN_DEFAULT, STYLE_ALIGN_LEFT, STYLE_ALIGN_CENTRE, - STYLE_ALIGN_RIGHT + STYLE_ALIGN_RIGHT, + STYLE_ALIGN_ABSOLUTE_CENTRE }; /* Style list. */ From ee0df1b8f8a9099fbd67a73c9fb20b89c64d6106 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Mar 2021 07:08:18 +0000 Subject: [PATCH 0733/1006] Tidy old jobs every hour instead of every 30 seconds. --- format.c | 37 +++++++++++++------------------------ server.c | 26 ++++++++++++++++++++++---- tmux.h | 1 + 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/format.c b/format.c index b913f56a..80e72cf0 100644 --- a/format.c +++ b/format.c @@ -41,7 +41,6 @@ struct format_expand_state; 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 *); @@ -69,7 +68,6 @@ struct format_job { }; /* Format job tree. */ -static struct event format_job_event; static int format_job_cmp(struct format_job *, struct format_job *); static RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER(); RB_GENERATE_STATIC(format_job_tree, format_job, entry, format_job_cmp); @@ -437,6 +435,19 @@ format_job_tidy(struct format_job_tree *jobs, int force) } } +/* Tidy old jobs for all clients. */ +void +format_tidy_jobs(void) +{ + struct client *c; + + format_job_tidy(&format_jobs, 0); + TAILQ_FOREACH(c, &clients, entry) { + if (c->jobs != NULL) + format_job_tidy(c->jobs, 0); + } +} + /* Remove old jobs for client. */ void format_lost_client(struct client *c) @@ -446,23 +457,6 @@ format_lost_client(struct client *c) free(c->jobs); } -/* Remove old jobs periodically. */ -static void -format_job_timer(__unused int fd, __unused short events, __unused void *arg) -{ - struct client *c; - struct timeval tv = { .tv_sec = 60 }; - - format_job_tidy(&format_jobs, 0); - TAILQ_FOREACH(c, &clients, entry) { - if (c->jobs != NULL) - format_job_tidy(c->jobs, 0); - } - - evtimer_del(&format_job_event); - evtimer_add(&format_job_event, &tv); -} - /* Wrapper for asprintf. */ static char * printflike(1, 2) format_printf(const char *fmt, ...) @@ -3048,11 +3042,6 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags) { struct format_tree *ft; - if (!event_initialized(&format_job_event)) { - evtimer_set(&format_job_event, format_job_timer, NULL); - format_job_timer(-1, 0, NULL); - } - ft = xcalloc(1, sizeof *ft); RB_INIT(&ft->tree); diff --git a/server.c b/server.c index a286e779..6ac08a4d 100644 --- a/server.c +++ b/server.c @@ -48,6 +48,7 @@ static int server_fd = -1; static uint64_t server_client_flags; static int server_exit; static struct event server_ev_accept; +static struct event server_ev_tidy; struct cmd_find_state marked_pane; @@ -151,15 +152,29 @@ fail: return (-1); } +/* Tidy up every hour. */ +static void +server_tidy_event(__unused int fd, __unused short events, __unused void *data) +{ + struct timeval tv = { .tv_sec = 3600 }; + uint64_t t = get_timer(); + + format_tidy_jobs(); + + log_debug("%s: took %llu milliseconds", __func__, get_timer() - t); + evtimer_add(&server_ev_tidy, &tv); +} + /* Fork new server. */ int server_start(struct tmuxproc *client, int flags, struct event_base *base, int lockfd, char *lockfile) { - int fd; - sigset_t set, oldset; - struct client *c = NULL; - char *cause = NULL; + int fd; + sigset_t set, oldset; + struct client *c = NULL; + char *cause = NULL; + struct timeval tv = { .tv_sec = 3600 }; sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); @@ -218,6 +233,9 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, free(cause); } + evtimer_set(&server_ev_tidy, server_tidy_event, NULL); + evtimer_add(&server_ev_tidy, &tv); + server_add_accept(0); proc_loop(server_proc, server_loop); diff --git a/tmux.h b/tmux.h index e8d2e8c3..1fc27b53 100644 --- a/tmux.h +++ b/tmux.h @@ -1947,6 +1947,7 @@ char *paste_make_sample(struct paste_buffer *); struct format_tree; struct format_modifier; typedef void *(*format_cb)(struct format_tree *); +void format_tidy_jobs(void); const char *format_skip(const char *, const char *); int format_true(const char *); struct format_tree *format_create(struct client *, struct cmdq_item *, int, From ef9700816fd2bd521bab837ccd85e0a596aa3aa8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 11 Mar 2021 08:39:41 +0000 Subject: [PATCH 0734/1006] malloc_trim is itself very poor and gets slower and slower as the heap becomes more fragmented. Run it only once an hour. GitHub issue 2551. --- grid.c | 3 --- server.c | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/grid.c b/grid.c index 1822f2b5..7744587a 100644 --- a/grid.c +++ b/grid.c @@ -265,9 +265,6 @@ 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. */ diff --git a/server.c b/server.c index ab8c6574..0b878d9e 100644 --- a/server.c +++ b/server.c @@ -159,6 +159,10 @@ server_tidy_event(__unused int fd, __unused short events, __unused void *data) format_tidy_jobs(); +#ifdef HAVE_MALLOC_TRIM + malloc_trim(0); +#endif + log_debug("%s: took %llu milliseconds", __func__, get_timer() - t); evtimer_add(&server_ev_tidy, &tv); } From e8224fb0d123ca9042b0703a35e1a33f0787d327 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 12 Mar 2021 08:39:17 +0000 Subject: [PATCH 0735/1006] Fix so tmux correctly sends the cvvis (cursor very visible) capability rather than sending it and then immediately undoing it with cnorm. Also turn it off when the cursor shape is changed like xterm. --- screen.c | 4 +++- tty.c | 38 +++++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/screen.c b/screen.c index 2cdbec62..97ccdeec 100644 --- a/screen.c +++ b/screen.c @@ -154,8 +154,10 @@ screen_reset_tabs(struct screen *s) void screen_set_cursor_style(struct screen *s, u_int style) { - if (style <= 6) + if (style <= 6) { s->cstyle = style; + s->mode &= ~MODE_BLINKING; + } } /* Set screen cursor colour. */ diff --git a/tty.c b/tty.c index 399bbae8..e8a8cbaa 100644 --- a/tty.c +++ b/tty.c @@ -670,19 +670,10 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) if (changed != 0) log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); - if (changed & MODE_BLINKING) { - if (tty_term_has(tty->term, TTYC_CVVIS)) - tty_putcode(tty, TTYC_CVVIS); - else - tty_putcode(tty, TTYC_CNORM); - changed |= MODE_CURSOR; - } - if (changed & MODE_CURSOR) { - if (mode & MODE_CURSOR) - tty_putcode(tty, TTYC_CNORM); - else - tty_putcode(tty, TTYC_CIVIS); - } + /* + * The cursor blinking flag can be reset by setting the cursor style, so + * set the style first. + */ if (s != NULL && tty->cstyle != s->cstyle) { if (tty_term_has(tty->term, TTYC_SS)) { if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) @@ -691,7 +682,28 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) tty_putcode1(tty, TTYC_SS, s->cstyle); } tty->cstyle = s->cstyle; + changed |= (MODE_CURSOR|MODE_BLINKING); } + + /* + * Cursor invisible (RM ?25) overrides cursor blinking (SM ?12 or RM + * 34), and we need to be careful not send cnorm after cvvis since it + * can undo it. + */ + if (changed & (MODE_CURSOR|MODE_BLINKING)) { + log_debug("%s: cursor %s, %sblinking", __func__, + (mode & MODE_CURSOR) ? "on" : "off", + (mode & MODE_BLINKING) ? "" : "not "); + if (~mode & MODE_CURSOR) + tty_putcode(tty, TTYC_CIVIS); + else if (mode & MODE_BLINKING) { + tty_putcode(tty, TTYC_CNORM); + if (tty_term_has(tty->term, TTYC_CVVIS)) + tty_putcode(tty, TTYC_CVVIS); + } else + tty_putcode(tty, TTYC_CNORM); + } + if ((changed & ALL_MOUSE_MODES) && tty_term_has(tty->term, TTYC_KMOUS)) { /* From 9017af23556abdc31ba37c925adf6e0f1bb12671 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Mar 2021 13:06:33 +0000 Subject: [PATCH 0736/1006] Do not crash if there is no item to show command error, from Anindya Mukherjee. --- cmd-run-shell.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 4f30d05d..73ed79f4 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -190,8 +191,12 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) &error); } if (status == CMD_PARSE_ERROR) { - cmdq_error(cdata->item, "%s", error); - free(error); + if (cdata->item == NULL) { + *error = toupper((u_char)*error); + status_message_set(c, -1, 1, "%s", error); + } else + cmdq_error(cdata->item, "%s", error); + free(error); } } From 8b800b41c98c37a270cea61e57d1f2702fd75293 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Mar 2021 09:14:58 +0000 Subject: [PATCH 0737/1006] Add client-detached notification in control mode, from Mohsin Kaleem. --- control-notify.c | 11 +++++++++++ notify.c | 2 ++ tmux.1 | 2 ++ tmux.h | 1 + 4 files changed, 16 insertions(+) diff --git a/control-notify.c b/control-notify.c index cc706ac2..6ff0e436 100644 --- a/control-notify.c +++ b/control-notify.c @@ -171,6 +171,17 @@ control_notify_client_session_changed(struct client *cc) } } +void +control_notify_client_detached(struct client *cc) +{ + struct client *c; + + TAILQ_FOREACH(c, &clients, entry) { + if (CONTROL_SHOULD_NOTIFY_CLIENT(c)) + control_write(c, "%%client-detached %s", cc->name); + } +} + void control_notify_session_renamed(struct session *s) { diff --git a/notify.c b/notify.c index a50eb264..c645657f 100644 --- a/notify.c +++ b/notify.c @@ -126,6 +126,8 @@ notify_callback(struct cmdq_item *item, void *data) control_notify_window_renamed(ne->window); if (strcmp(ne->name, "client-session-changed") == 0) control_notify_client_session_changed(ne->client); + if (strcmp(ne->name, "client-detached") == 0) + control_notify_client_detached(ne->client); if (strcmp(ne->name, "session-renamed") == 0) control_notify_session_renamed(ne->session); if (strcmp(ne->name, "session-created") == 0) diff --git a/tmux.1 b/tmux.1 index 5c516fdb..ecbbc9fc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6107,6 +6107,8 @@ A notification will never occur inside an output block. .Pp The following notifications are defined: .Bl -tag -width Ds +.It Ic %client-detached Ar client +The client has detached. .It Ic %client-session-changed Ar client session-id name The client is now attached to the session with ID .Ar session-id , diff --git a/tmux.h b/tmux.h index 1fc27b53..e50f9fe7 100644 --- a/tmux.h +++ b/tmux.h @@ -2945,6 +2945,7 @@ void control_notify_window_unlinked(struct session *, struct window *); void control_notify_window_linked(struct session *, struct window *); void control_notify_window_renamed(struct window *); void control_notify_client_session_changed(struct client *); +void control_notify_client_detached(struct client *); void control_notify_session_renamed(struct session *); void control_notify_session_created(struct session *); void control_notify_session_closed(struct session *); From 4208641de7045299a10f51209bec4623cbea6744 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 28 Mar 2021 10:16:17 +0100 Subject: [PATCH 0738/1006] Remove queue.h, from Simon Holesch. --- proc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/proc.c b/proc.c index a1a2b36a..958a9483 100644 --- a/proc.c +++ b/proc.c @@ -17,7 +17,6 @@ */ #include -#include #include #include #include From a4b9b5a1e5326b4ac4dfb439627b735d831b8992 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 31 Mar 2021 08:37:48 +0000 Subject: [PATCH 0739/1006] Do not exit if cannot write to normal log file, GitHub issue 2630. --- log.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/log.c b/log.c index 44b24c67..3387ab0c 100644 --- a/log.c +++ b/log.c @@ -111,15 +111,16 @@ log_vwrite(const char *msg, va_list ap) return; if (vasprintf(&fmt, msg, ap) == -1) - exit(1); - if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) - exit(1); + return; + if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) { + free(fmt); + return; + } gettimeofday(&tv, NULL); if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec, - (int)tv.tv_usec, out) == -1) - exit(1); - fflush(log_file); + (int)tv.tv_usec, out) != -1) + fflush(log_file); free(out); free(fmt); From 6c98f222e90468106533d5c78a2acb0a5f5876d7 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 1 Apr 2021 06:37:46 +0000 Subject: [PATCH 0740/1006] Missing commas, from Vipul Kumar. --- tmux.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index ecbbc9fc..d76cadd3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3149,8 +3149,8 @@ The appearance and behaviour of may be modified by changing the value of various options. There are four types of option: .Em server options , -.Em session options -.Em window options +.Em session options , +.Em window options , and .Em pane options . .Pp From 28cd956729c13f8f66e26d438592df9c9930e7b8 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 1 Apr 2021 06:46:12 +0000 Subject: [PATCH 0741/1006] Change search-again with vi keys to work like actual vi(1), also some other fixes. From Aaron Jensen with help from Anindya Mukherjee. --- window-copy.c | 211 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 161 insertions(+), 50 deletions(-) diff --git a/window-copy.c b/window-copy.c index 3fc7ad3e..6276c8f9 100644 --- a/window-copy.c +++ b/window-copy.c @@ -64,6 +64,8 @@ static int window_copy_search_rl(struct grid *, struct grid *, u_int *, static int window_copy_last_regex(struct grid *, u_int, u_int, u_int, u_int, u_int *, u_int *, const char *, const regex_t *, int); +static int window_copy_search_mark_at(struct window_copy_mode_data *, + u_int, u_int, u_int *); static char *window_copy_stringify(struct grid *, u_int, u_int, u_int, char *, u_int *); static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, @@ -71,16 +73,15 @@ static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, 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 int window_copy_is_lowercase(const char *); static void window_copy_search_back_overlap(struct grid *, regex_t *, u_int *, u_int *, u_int *, u_int); static int window_copy_search_jump(struct window_mode_entry *, struct grid *, struct grid *, u_int, u_int, u_int, int, 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); + 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); 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); @@ -275,6 +276,7 @@ struct window_copy_mode_data { int showmark; int searchtype; + int searchdirection; int searchregex; char *searchstr; u_char *searchmark; @@ -1718,10 +1720,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, 1); + window_copy_search_up(wme, data->searchregex); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_down(wme, data->searchregex, 1); + window_copy_search_down(wme, data->searchregex); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1735,10 +1737,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, 1); + window_copy_search_down(wme, data->searchregex); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_up(wme, data->searchregex, 1); + window_copy_search_up(wme, data->searchregex); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2042,7 +2044,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, 0); + window_copy_search_up(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2062,7 +2064,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, 0); + window_copy_search_up(wme, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2082,7 +2084,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, 0); + window_copy_search_down(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2102,7 +2104,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, 0); + window_copy_search_down(wme, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2143,7 +2145,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, 1)) { + if (!window_copy_search_up(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2153,7 +2155,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, 0)) { + if (!window_copy_search_down(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2198,7 +2200,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, 1)) { + if (!window_copy_search_down(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2208,7 +2210,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, 1)) { + if (!window_copy_search_up(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2917,6 +2919,23 @@ 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) { @@ -2979,7 +2998,7 @@ window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx, 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, u_int *foundlen) + int direction, int regex) { u_int i, px, sx, ssize = 1; int found = 0, cflags = REG_EXTENDED; @@ -3004,20 +3023,15 @@ 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, @@ -3048,18 +3062,40 @@ 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, foundlen)); + direction, regex)); } return (0); } +static void +window_copy_move_after_search_mark(struct window_copy_mode_data *data, + u_int *fx, u_int *fy, int wrapflag) +{ + struct screen *s = data->backing; + u_int at, start; + + if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 && + data->searchmark[start] != 0) { + while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) { + if (data->searchmark[at] != data->searchmark[start]) + break; + /* Stop if not wrapping and at the end of the grid. */ + if (!wrapflag && + *fx == screen_size_x(s) - 1 && + *fy == screen_hsize(s) + screen_size_y(s) - 1) + break; + + window_copy_move_right(s, fx, fy, wrapflag); + } + } +} + /* * Search in for text searchstr. If direction is 0 then search up, otherwise * 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) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -3067,12 +3103,15 @@ 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, i, foundlen; - int wrapflag, cis, found, visible_only; + u_int at, endline, fx, fy, start; + int cis, found, keys, visible_only; + int wrapflag; if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') regex = 0; + data->searchdirection = direction; + if (data->timeout) return (0); @@ -3097,27 +3136,94 @@ 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) + keys = options_get_number(wp->window->options, "mode-keys"); + + if (direction) { + /* + * Behave according to mode-keys. If it is emacs, search forward + * leaves the cursor after the match. If it is vi, the cursor + * remains at the beginning of the match, regardless of + * direction, which means that we need to start the next search + * after the term the cursor is currently on when searching + * forward. + */ + if (keys == MODEKEY_VI) { + if (data->searchmark != NULL) + window_copy_move_after_search_mark(data, &fx, + &fy, wrapflag); + else { + /* + * When there are no search marks, start the + * search after the current cursor position. + */ + window_copy_move_right(s, &fx, &fy, wrapflag); + } + } endline = gd->hsize + gd->sy - 1; + } else { - if (again) - window_copy_move_left(s, &fx, &fy, wrapflag); + 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, &foundlen); + wrapflag, direction, regex); if (found) { window_copy_search_marks(wme, &ss, regex, visible_only); - if (foundlen != 0) { - /* Adjust for wrapped lines eating one right. */ - i = data->cx + foundlen; - while (i > gd->sx - 1) { - i -= gd->sx; - window_copy_cursor_right(wme, 1); + fx = data->cx; + fy = screen_hsize(data->backing) - data->oy + data->cy; + + /* + * When searching forward, if the cursor is not at the beginning + * of the mark, search again. + */ + if (direction && + window_copy_search_mark_at(data, fx, fy, &at) == 0 && + at > 0 && + data->searchmark[at] == data->searchmark[at - 1]) { + window_copy_move_after_search_mark(data, &fx, &fy, + wrapflag); + window_copy_search_jump(wme, gd, ss.grid, fx, + fy, endline, cis, wrapflag, direction, + regex); + fx = data->cx; + fy = screen_hsize(data->backing) - data->oy + data->cy; + } + + if (direction) { + /* + * When in Emacs mode, position the cursor just after + * the mark. + */ + if (keys == MODEKEY_EMACS) { + window_copy_move_after_search_mark(data, &fx, + &fy, wrapflag); + data->cx = fx; + data->cy = fy - screen_hsize(data->backing) + + data-> oy; + } + } + else { + /* + * When searching backward, position the cursor at the + * beginning of the mark. + */ + if (window_copy_search_mark_at(data, fx, fy, + &start) == 0) { + while (window_copy_search_mark_at(data, fx, fy, + &at) == 0 && + data->searchmark[at] == + data->searchmark[start]) { + data->cx = fx; + data->cy = fy - + screen_hsize(data->backing) + + data-> oy; + if (at == 0) + break; + + window_copy_move_left(s, &fx, &fy, 0); + } } - for (i = 0; i < foundlen; i++) - window_copy_cursor_right(wme, 1); } } window_copy_redraw_screen(wme); @@ -3302,15 +3408,15 @@ window_copy_clear_marks(struct window_mode_entry *wme) } static int -window_copy_search_up(struct window_mode_entry *wme, int regex, int again) +window_copy_search_up(struct window_mode_entry *wme, int regex) { - return (window_copy_search(wme, 0, regex, again)); + return (window_copy_search(wme, 0, regex)); } static int -window_copy_search_down(struct window_mode_entry *wme, int regex, int again) +window_copy_search_down(struct window_mode_entry *wme, int regex) { - return (window_copy_search(wme, 1, regex, again)); + return (window_copy_search(wme, 1, regex)); } static void @@ -3396,9 +3502,11 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, struct grid_cell *gc, const struct grid_cell *mgc, const struct grid_cell *cgc, const struct grid_cell *mkgc) { + struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; u_int mark, start, end, cy, cursor, current; int inv = 0, found = 0; + int keys; if (data->showmark && fy == data->my) { gc->attr = mkgc->attr; @@ -3425,13 +3533,16 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, cy = screen_hsize(data->backing) - data->oy + data->cy; 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) + keys = options_get_number(wp->window->options, "mode-keys"); + if (cursor != 0 && + keys == MODEKEY_EMACS && + data->searchdirection) { + if (data->searchmark[cursor - 1] == mark) { + cursor--; found = 1; - } + } + } else if (data->searchmark[cursor] == mark) + found = 1; if (found) { window_copy_match_start_end(data, cursor, &start, &end); if (current >= start && current <= end) { From 5900b164a47689620db962e69f6d040bb82885b0 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 5 Apr 2021 08:43:48 +0000 Subject: [PATCH 0742/1006] Fix a couple of edge cases with the jump-back-xxx commands, and also update back-to-indentation to use grid_reader, thereby fixing line wrapping issues. From Anindya Mukherjee, GitHub issue 2633. --- grid-reader.c | 28 ++++++++++++++++++++++++++-- tmux.h | 3 ++- window-copy.c | 46 ++++++++++++++++++---------------------------- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/grid-reader.c b/grid-reader.c index ae2f4d2b..89fe90fb 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -71,7 +71,7 @@ grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all) /* Move cursor back one position. */ void -grid_reader_cursor_left(struct grid_reader *gr) +grid_reader_cursor_left(struct grid_reader *gr, int wrap) { struct grid_cell gc; @@ -81,7 +81,9 @@ grid_reader_cursor_left(struct grid_reader *gr) break; gr->cx--; } - if (gr->cx == 0 && gr->cy > 0) { + if (gr->cx == 0 && gr->cy > 0 && + (wrap || + grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) { grid_reader_cursor_up(gr); grid_reader_cursor_end_of_line(gr, 0, 0); } else if (gr->cx > 0) @@ -363,3 +365,25 @@ grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc) } return 0; } + +/* Jump back to the first non-blank character of the line. */ +void +grid_reader_cursor_back_to_indentation(struct grid_reader *gr) +{ + struct grid_cell gc; + u_int px, py, xx, yy; + + yy = gr->gd->hsize + gr->gd->sy - 1; + grid_reader_cursor_start_of_line(gr, 1); + + for (py = gr->cy; py <= yy; py++) { + xx = grid_line_length(gr->gd, py); + for (px = 0; px < xx; px++) { + grid_get_cell(gr->gd, px, py, &gc); + if (gc.data.size != 1 || *gc.data.data != ' ') + break; + } + if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED) + break; + } +} diff --git a/tmux.h b/tmux.h index e50f9fe7..0c7636f8 100644 --- a/tmux.h +++ b/tmux.h @@ -2583,7 +2583,7 @@ void grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *); u_int grid_reader_line_length(struct grid_reader *); int grid_reader_in_set(struct grid_reader *, const char *); void grid_reader_cursor_right(struct grid_reader *, int, int); -void grid_reader_cursor_left(struct grid_reader *); +void grid_reader_cursor_left(struct grid_reader *, int); void grid_reader_cursor_down(struct grid_reader *); void grid_reader_cursor_up(struct grid_reader *); void grid_reader_cursor_start_of_line(struct grid_reader *, int); @@ -2596,6 +2596,7 @@ int grid_reader_cursor_jump(struct grid_reader *, const struct utf8_data *); int grid_reader_cursor_jump_back(struct grid_reader *, const struct utf8_data *); +void grid_reader_cursor_back_to_indentation(struct grid_reader *); /* grid-view.c */ void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); diff --git a/window-copy.c b/window-copy.c index 6276c8f9..423cce8f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -4291,23 +4291,19 @@ static void window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; - u_int px, py, xx; - struct grid_cell gc; + struct screen *back_s = data->backing; + struct grid_reader gr; + u_int px, py, oldy, hsize; - px = 0; - py = screen_hsize(data->backing) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); + px = data->cx; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + oldy = data->cy; - while (px < xx) { - grid_get_cell(data->backing->grid, px, py, &gc); - if (gc.data.size != 1 || *gc.data.data != ' ') - break; - px++; - } - - window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); + grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_back_to_indentation(&gr); + grid_reader_get_cursor(&gr, &px, &py); + window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } static void @@ -4398,7 +4394,7 @@ window_copy_cursor_left(struct window_mode_entry *wme) oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); - grid_reader_cursor_left(&gr); + grid_reader_cursor_left(&gr, 1); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } @@ -4583,10 +4579,8 @@ window_copy_cursor_jump_back(struct window_mode_entry *wme) py = hsize + data->cy - data->oy; oldy = data->cy; - if (px > 0) - px--; - grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_left(&gr, 0); if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, @@ -4609,7 +4603,7 @@ window_copy_cursor_jump_to(struct window_mode_entry *wme) grid_reader_start(&gr, back_s->grid, px, py); if (grid_reader_cursor_jump(&gr, data->jumpchar)) { - grid_reader_cursor_left(&gr); + grid_reader_cursor_left(&gr, 1); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), data->oy, oldy, px, py, 0); @@ -4629,13 +4623,9 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme) py = hsize + data->cy - data->oy; oldy = data->cy; - if (px > 0) - px--; - - if (px > 0) - px--; - grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_left(&gr, 0); + grid_reader_cursor_left(&gr, 0); if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { grid_reader_cursor_right(&gr, 1, 0); grid_reader_get_cursor(&gr, &px, &py); @@ -4688,7 +4678,7 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, grid_reader_cursor_right(&gr, 0, 0); grid_reader_cursor_next_word_end(&gr, separators); if (keys == MODEKEY_VI) - grid_reader_cursor_left(&gr); + grid_reader_cursor_left(&gr, 1); grid_reader_get_cursor(&gr, &px, &py); *ppx = px; *ppy = py; @@ -4718,7 +4708,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, grid_reader_cursor_right(&gr, 0, 0); grid_reader_cursor_next_word_end(&gr, separators); if (keys == MODEKEY_VI) - grid_reader_cursor_left(&gr); + grid_reader_cursor_left(&gr, 1); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), data->oy, oldy, px, py, no_reset); From 10470cea67eeb2ff34acbb37aac292bc6468ba07 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 5 Apr 2021 14:11:05 +0000 Subject: [PATCH 0743/1006] Move client-detached into server_client_lost so it is fired even if a client is closed unexpectedly. --- server-client.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server-client.c b/server-client.c index adeea5dd..219fdf3a 100644 --- a/server-client.c +++ b/server-client.c @@ -299,6 +299,9 @@ server_client_lost(struct client *c) TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); + if (c->flags & CLIENT_ATTACHED) + notify_client("client-detached", c); + if (c->flags & CLIENT_CONTROL) control_stop(c); if (c->flags & CLIENT_TERMINAL) @@ -1769,9 +1772,6 @@ server_client_check_exit(struct client *c) if (EVBUFFER_LENGTH(cf->buffer) != 0) return; } - - if (c->flags & CLIENT_ATTACHED) - notify_client("client-detached", c); c->flags |= CLIENT_EXITED; switch (c->exit_type) { From ba999966768b3ede648a361dd9b17ca81bdda173 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 7 Apr 2021 07:30:02 +0000 Subject: [PATCH 0744/1006] Fixes for extended keys: 1) allow C-x and C-X to be bound separately since some terminals report them differently 2) use the "backspace" option to translate backspace 3) map ctrl which are have the ctrl implied (such as C-x) properly when the terminal reports both the key and the modifier. Note that any key bindings for C-X where C-x is meant must now be changed. --- key-string.c | 18 +++++++++--------- tty-keys.c | 53 ++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/key-string.c b/key-string.c index 194fdef2..0633ed22 100644 --- a/key-string.c +++ b/key-string.c @@ -45,9 +45,9 @@ static const struct { { "F11", KEYC_F11|KEYC_IMPLIED_META }, { "F12", KEYC_F12|KEYC_IMPLIED_META }, { "IC", KEYC_IC|KEYC_IMPLIED_META }, - { "Insert", KEYC_IC|KEYC_IMPLIED_META }, + { "Insert", KEYC_IC|KEYC_IMPLIED_META }, { "DC", KEYC_DC|KEYC_IMPLIED_META }, - { "Delete", KEYC_DC|KEYC_IMPLIED_META }, + { "Delete", KEYC_DC|KEYC_IMPLIED_META }, { "Home", KEYC_HOME|KEYC_IMPLIED_META }, { "End", KEYC_END|KEYC_IMPLIED_META }, { "NPage", KEYC_NPAGE|KEYC_IMPLIED_META }, @@ -70,7 +70,7 @@ static const struct { { "Right", KEYC_RIGHT|KEYC_CURSOR|KEYC_IMPLIED_META }, /* Numeric keypad. */ - { "KP/", KEYC_KP_SLASH|KEYC_KEYPAD }, + { "KP/", KEYC_KP_SLASH|KEYC_KEYPAD }, { "KP*", KEYC_KP_STAR|KEYC_KEYPAD }, { "KP-", KEYC_KP_MINUS|KEYC_KEYPAD }, { "KP7", KEYC_KP_SEVEN|KEYC_KEYPAD }, @@ -164,7 +164,7 @@ key_string_get_modifiers(const char **string) key_code key_string_lookup_string(const char *string) { - static const char *other = "!#()+,-.0123456789:;<=>'\r\t"; + static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177"; key_code key, modifiers; u_int u, i; struct utf8_data ud, *udp; @@ -181,8 +181,8 @@ key_string_lookup_string(const char *string) /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { - if (sscanf(string + 2, "%x", &u) != 1) - return (KEYC_UNKNOWN); + if (sscanf(string + 2, "%x", &u) != 1) + return (KEYC_UNKNOWN); mlen = wctomb(m, u); if (mlen <= 0 || mlen > MB_LEN_MAX) return (KEYC_UNKNOWN); @@ -238,11 +238,11 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && !strchr(other, key)) { + if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && + strchr(other, key) == NULL && + (key < 64 || key > 95)) { if (key >= 97 && key <= 122) key -= 96; - else if (key >= 64 && key <= 95) - key -= 64; else if (key == 32) key = 0; else if (key == 63) diff --git a/tty-keys.c b/tty-keys.c index 59426772..16cd09f2 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -61,7 +61,7 @@ static int tty_keys_extended_device_attributes(struct tty *, const char *, /* Default raw keys. */ struct tty_default_key_raw { const char *string; - key_code key; + key_code key; }; static const struct tty_default_key_raw tty_default_raw_keys[] = { /* Application escape. */ @@ -262,7 +262,7 @@ static const key_code tty_default_xterm_modifiers[] = { */ struct tty_default_key_code { enum tty_code_code code; - key_code key; + key_code key; }; static const struct tty_default_key_code tty_default_code_keys[] = { /* Function keys. */ @@ -420,7 +420,7 @@ tty_keys_add(struct tty *tty, const char *s, key_code key) { struct tty_key *tk; size_t size; - const char *keystr; + const char *keystr; keystr = key_string_lookup_key(key, 1); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { @@ -477,7 +477,7 @@ tty_keys_build(struct tty *tty) const struct tty_default_key_raw *tdkr; const struct tty_default_key_xterm *tdkx; const struct tty_default_key_code *tdkc; - u_int i, j; + u_int i, j; const char *s; struct options_entry *o; struct options_array_item *a; @@ -869,6 +869,8 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, size_t end; u_int number, modifiers; char tmp[64]; + cc_t bspace; + key_code nkey; *size = 0; @@ -911,38 +913,61 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, } *size = end + 1; - /* Store the key and modifiers. */ - *key = number; + /* Store the key. */ + bspace = tty->tio.c_cc[VERASE]; + if (bspace != _POSIX_VDISABLE && number == bspace) + nkey = KEYC_BSPACE; + else + nkey = number; + + /* Update the modifiers. */ switch (modifiers) { case 2: - (*key) |= KEYC_SHIFT; + nkey |= KEYC_SHIFT; break; case 3: - (*key) |= (KEYC_META|KEYC_IMPLIED_META); + nkey |= (KEYC_META|KEYC_IMPLIED_META); break; case 4: - (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META); + nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META); break; case 5: - (*key) |= KEYC_CTRL; + nkey |= KEYC_CTRL; break; case 6: - (*key) |= (KEYC_SHIFT|KEYC_CTRL); + nkey |= (KEYC_SHIFT|KEYC_CTRL); break; case 7: - (*key) |= (KEYC_META|KEYC_CTRL); + nkey |= (KEYC_META|KEYC_CTRL); break; case 8: - (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); + nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); break; default: *key = KEYC_NONE; break; } + + /* Don't allow both KEYC_CTRL and implied. */ + if ((nkey & KEYC_CTRL) && (nkey & KEYC_MASK_KEY) < 32) + nkey &= ~KEYC_CTRL; + if ((nkey & KEYC_MASK_MODIFIERS) == KEYC_CTRL) { + nkey &= KEYC_MASK_KEY; + if (nkey >= 97 && nkey <= 122) + nkey -= 96; + else if (nkey == 32) + nkey = 0; + else if (nkey == 63) + nkey = 127; + else + nkey |= KEYC_CTRL; + } + if (log_get_level() != 0) { log_debug("%s: extended key %.*s is %llx (%s)", c->name, - (int)*size, buf, *key, key_string_lookup_key(*key, 1)); + (int)*size, buf, nkey, key_string_lookup_key(nkey, 1)); } + *key = nkey; return (0); } From 1ac47400d296ad3a56e9740f9249175bb59953d5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 7 Apr 2021 12:49:33 +0000 Subject: [PATCH 0745/1006] When display-message used in config file, show the message after the config file finishes. GitHub issue 2637. --- cmd-display-message.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index fc9c4851..301c1a5e 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -43,7 +43,7 @@ const struct cmd_entry cmd_display_message_entry = { .usage = "[-aIpv] [-c target-client] [-d delay] [-F format] " CMD_TARGET_PANE_USAGE " [message]", - .target = { 't', CMD_FIND_PANE, 0 }, + .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL, .exec = cmd_display_message_exec @@ -73,6 +73,8 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) int flags; if (args_has(args, 'I')) { + if (wp == NULL) + return (CMD_RETURN_NORMAL); if (window_pane_start_input(wp, item, &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); @@ -109,8 +111,10 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) */ if (tc != NULL && tc->session == s) c = tc; - else + else if (s != NULL) c = cmd_find_best_client(s); + else + c = NULL; if (args_has(args, 'v')) flags = FORMAT_VERBOSE; else @@ -124,7 +128,9 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) } msg = format_expand_time(ft, template); - if (args_has(args, 'p')) + if (cmdq_get_client(item) == NULL) + cmdq_error(item, "%s", msg); + else if (args_has(args, 'p')) cmdq_print(item, "%s", msg); else if (tc != NULL) status_message_set(tc, delay, 0, "%s", msg); From 71fc9f3ee8753a2bb163c85c784936a8d6d3e0ac Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 7 Apr 2021 12:50:12 +0000 Subject: [PATCH 0746/1006] Add a current_file format for the config file being parsed. Originally suggested by kn@, also GitHub issue 2638. --- cfg.c | 20 ++++++++++++++++++-- cmd-queue.c | 2 +- tmux.1 | 1 + 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/cfg.c b/cfg.c index 55c91bc4..cf6117f4 100644 --- a/cfg.c +++ b/cfg.c @@ -103,6 +103,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, struct cmd_parse_input pi; struct cmd_parse_result *pr; struct cmdq_item *new_item0; + struct cmdq_state *state; if (new_item != NULL) *new_item = NULL; @@ -136,12 +137,19 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, return (0); } - new_item0 = cmdq_get_command(pr->cmdlist, NULL); + if (item != NULL) + state = cmdq_copy_state(cmdq_get_state(item)); + else + state = cmdq_new_state(NULL, NULL, 0); + cmdq_add_format(state, "current_file", "%s", pi.file); + + new_item0 = cmdq_get_command(pr->cmdlist, state); if (item != NULL) new_item0 = cmdq_insert_after(item, new_item0); else new_item0 = cmdq_append(NULL, new_item0); cmd_list_free(pr->cmdlist); + cmdq_free_state(state); if (new_item != NULL) *new_item = new_item0; @@ -156,6 +164,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path, struct cmd_parse_input pi; struct cmd_parse_result *pr; struct cmdq_item *new_item0; + struct cmdq_state *state; if (new_item != NULL) *new_item = NULL; @@ -182,12 +191,19 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path, return (0); } - new_item0 = cmdq_get_command(pr->cmdlist, NULL); + if (item != NULL) + state = cmdq_copy_state(cmdq_get_state(item)); + else + state = cmdq_new_state(NULL, NULL, 0); + cmdq_add_format(state, "current_file", "%s", pi.file); + + new_item0 = cmdq_get_command(pr->cmdlist, state); if (item != NULL) new_item0 = cmdq_insert_after(item, new_item0); else new_item0 = cmdq_append(NULL, new_item0); cmd_list_free(pr->cmdlist); + cmdq_free_state(state); if (new_item != NULL) *new_item = new_item0; diff --git a/cmd-queue.c b/cmd-queue.c index 05f439f5..a0d80c34 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -276,7 +276,7 @@ cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft) const struct cmd_entry *entry; if (item->cmd != NULL) { - entry = cmd_get_entry (item->cmd); + entry = cmd_get_entry(item->cmd); format_add(ft, "command", "%s", entry->name); } if (item->state->formats != NULL) diff --git a/tmux.1 b/tmux.1 index d76cadd3..00b5cd84 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4859,6 +4859,7 @@ The following variables are available, where appropriate: .It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode" .It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode" .It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode" +.It Li "current_file" Ta "" Ta "Current configuration file" .It Li "cursor_character" Ta "" Ta "Character at cursor in pane" .It Li "cursor_flag" Ta "" Ta "Pane cursor flag" .It Li "cursor_x" Ta "" Ta "Cursor X position in pane" From efb5e58c381a5faf7751d916a60f256ede19c0e8 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 7 Apr 2021 15:46:12 +0000 Subject: [PATCH 0747/1006] Restore previous behaviour so that C-X remains the same as C-x. Instead, translate incoming extended keys so that they are consistent. --- key-string.c | 5 +++-- tty-keys.c | 34 +++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/key-string.c b/key-string.c index 0633ed22..8d60f132 100644 --- a/key-string.c +++ b/key-string.c @@ -239,10 +239,11 @@ key_string_lookup_string(const char *string) /* Convert the standard control keys. */ if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && - strchr(other, key) == NULL && - (key < 64 || key > 95)) { + strchr(other, key) == NULL) { if (key >= 97 && key <= 122) key -= 96; + else if (key >= 64 && key <= 95) + key -= 64; else if (key == 32) key = 0; else if (key == 63) diff --git a/tty-keys.c b/tty-keys.c index 16cd09f2..e88ff227 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -871,6 +871,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, char tmp[64]; cc_t bspace; key_code nkey; + key_code onlykey; *size = 0; @@ -948,19 +949,26 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, break; } - /* Don't allow both KEYC_CTRL and implied. */ - if ((nkey & KEYC_CTRL) && (nkey & KEYC_MASK_KEY) < 32) - nkey &= ~KEYC_CTRL; - if ((nkey & KEYC_MASK_MODIFIERS) == KEYC_CTRL) { - nkey &= KEYC_MASK_KEY; - if (nkey >= 97 && nkey <= 122) - nkey -= 96; - else if (nkey == 32) - nkey = 0; - else if (nkey == 63) - nkey = 127; - else - nkey |= KEYC_CTRL; + /* + * Don't allow both KEYC_CTRL and as an implied modifier. Also convert + * C-X into C-x and so on. + */ + if (nkey & KEYC_CTRL){ + onlykey = (nkey & KEYC_MASK_KEY); + if (onlykey < 32) + onlykey = (nkey & ~KEYC_CTRL); + else { + if (onlykey >= 97 && onlykey <= 122) + onlykey -= 96; + else if (onlykey >= 64 && onlykey <= 95) + onlykey -= 64; + else if (onlykey == 32) + onlykey = 0; + else if (onlykey == 63) + onlykey = 127; + onlykey |= ((nkey & KEYC_MASK_MODIFIERS) & ~KEYC_CTRL); + } + nkey = onlykey; } if (log_get_level() != 0) { From 30fb6283886cdccf183bb480a2c1976ba1a7e60e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 8 Apr 2021 14:16:12 +0000 Subject: [PATCH 0748/1006] Log the key written to the terminal as well as tmux's idea of what it is. --- input-keys.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/input-keys.c b/input-keys.c index 5cd43dc3..40567a49 100644 --- a/input-keys.c +++ b/input-keys.c @@ -429,6 +429,14 @@ input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) return (input_key(wp->screen, wp->event, key)); } +static void +input_key_write(const char *from, struct bufferevent *bev, const void *data, + size_t size) +{ + log_debug("%s: %.*s", from, (int)size, data); + bufferevent_write(bev, data, size); +} + /* Translate a key code into an output key sequence. */ int input_key(struct screen *s, struct bufferevent *bev, key_code key) @@ -445,7 +453,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) /* Literal keys go as themselves (can't be more than eight bits). */ if (key & KEYC_LITERAL) { ud.data[0] = (u_char)key; - bufferevent_write(bev, &ud.data[0], 1); + input_key_write(__func__, bev, &ud.data[0], 1); return (0); } @@ -464,16 +472,16 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META)); if (justkey <= 0x7f) { if (key & KEYC_META) - bufferevent_write(bev, "\033", 1); + input_key_write(__func__, bev, "\033", 1); ud.data[0] = justkey; - bufferevent_write(bev, &ud.data[0], 1); + input_key_write(__func__, bev, &ud.data[0], 1); return (0); } if (justkey > 0x7f && justkey < KEYC_BASE) { if (key & KEYC_META) - bufferevent_write(bev, "\033", 1); + input_key_write(__func__, bev, "\033", 1); utf8_to_data(justkey, &ud); - bufferevent_write(bev, ud.data, ud.size); + input_key_write(__func__, bev, ud.data, ud.size); return (0); } @@ -495,8 +503,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) if (ike != NULL) { log_debug("found key 0x%llx: \"%s\"", key, ike->data); if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META)) - bufferevent_write(bev, "\033", 1); - bufferevent_write(bev, ike->data, strlen(ike->data)); + input_key_write(__func__, bev, "\033", 1); + input_key_write(__func__, bev, ike->data, strlen(ike->data)); return (0); } @@ -561,7 +569,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) goto missing; } xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier); - bufferevent_write(bev, tmp, strlen(tmp)); + input_key_write(__func__, bev, tmp, strlen(tmp)); return (0); missing: @@ -657,5 +665,5 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) if (!input_key_get_mouse(s, m, x, y, &buf, &len)) return; log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id); - bufferevent_write(wp->event, buf, len); + input_key_write(__func__, wp->event, buf, len); } From 73cbe46f8d871b70310735276f34d9c207412587 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 9 Apr 2021 07:02:00 +0000 Subject: [PATCH 0749/1006] Change a type to fix a warning with some compilers. --- input-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index 40567a49..ab7d2212 100644 --- a/input-keys.c +++ b/input-keys.c @@ -430,7 +430,7 @@ input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) } static void -input_key_write(const char *from, struct bufferevent *bev, const void *data, +input_key_write(const char *from, struct bufferevent *bev, const char *data, size_t size) { log_debug("%s: %.*s", from, (int)size, data); From cd208c9d72df79a34024df6b8eb8f984613de8ef Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 12 Apr 2021 06:50:25 +0000 Subject: [PATCH 0750/1006] Permit shortcut keys in buffer, client, tree modes to be configured with a format; the default remains the line number. GitHub issue 2636. --- cmd-choose-tree.c | 18 ++++++------ format.c | 34 +++++++++++++++++------ mode-tree.c | 68 ++++++++++++++++++++++++++++++++-------------- tmux.1 | 34 ++++++++++++++++++++--- tmux.h | 3 +- window-buffer.c | 59 ++++++++++++++++++++++++++++++++++++++-- window-client.c | 43 +++++++++++++++++++++++++++-- window-customize.c | 4 +-- window-tree.c | 52 +++++++++++++++++++++++++++++++++-- 9 files changed, 261 insertions(+), 54 deletions(-) diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index a58469ac..81209ee3 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -30,9 +30,9 @@ const struct cmd_entry cmd_choose_tree_entry = { .name = "choose-tree", .alias = NULL, - .args = { "F:Gf:NO:rst:wZ", 0, 1 }, - .usage = "[-GNrswZ] [-F format] [-f filter] [-O sort-order] " - CMD_TARGET_PANE_USAGE " [template]", + .args = { "F:f:GK:NO:rst:wZ", 0, 1 }, + .usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] " + "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -44,9 +44,9 @@ const struct cmd_entry cmd_choose_client_entry = { .name = "choose-client", .alias = NULL, - .args = { "F:f:NO:rt:Z", 0, 1 }, - .usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] " - CMD_TARGET_PANE_USAGE " [template]", + .args = { "F:f:K:NO:rt:Z", 0, 1 }, + .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " + "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -58,9 +58,9 @@ const struct cmd_entry cmd_choose_buffer_entry = { .name = "choose-buffer", .alias = NULL, - .args = { "F:f:NO:rt:Z", 0, 1 }, - .usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] " - CMD_TARGET_PANE_USAGE " [template]", + .args = { "F:f:K:NO:rt:Z", 0, 1 }, + .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " + "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/format.c b/format.c index 80e72cf0..3a1385b2 100644 --- a/format.c +++ b/format.c @@ -100,6 +100,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_QUOTE_STYLE 0x2000 #define FORMAT_WINDOW_NAME 0x4000 #define FORMAT_SESSION_NAME 0x8000 +#define FORMAT_CHARACTER 0x10000 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 @@ -3522,7 +3523,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, /* * Modifiers are a ; separated list of the forms: - * l,m,C,b,d,n,t,w,q,E,T,S,W,P,<,> + * l,m,C,a,b,d,n,t,w,q,E,T,S,W,P,<,> * =a * =/a * =/a/ @@ -3539,7 +3540,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lbdnwETSWP<>", cp[0]) != NULL && + if (strchr("labdnwETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -3956,7 +3957,7 @@ format_replace_expression(struct format_modifier *mexp, mright = (long long)mright; } format_log(es, "expression left side is: %.*f", prec, mleft); - format_log(es, "expression right side is: %.*f", prec, mright); + format_log(es, "expression right side is: %.*f", prec, mright); switch (operator) { case ADD: @@ -4016,10 +4017,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, { struct format_tree *ft = es->ft; struct window_pane *wp = ft->wp; - const char *errptr, *copy, *cp, *marker = NULL; + const char *errstr, *copy, *cp, *marker = NULL; const char *time_format = NULL; char *copy0, *condition, *found, *new; - char *value, *left, *right; + char *value, *left, *right, c; size_t valuelen; int modifiers = 0, limit = 0, width = 0; int j; @@ -4063,8 +4064,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, if (fm->argc < 1) break; limit = strtonum(fm->argv[0], INT_MIN, INT_MAX, - &errptr); - if (errptr != NULL) + &errstr); + if (errstr != NULL) limit = 0; if (fm->argc >= 2 && fm->argv[1] != NULL) marker = fm->argv[1]; @@ -4073,8 +4074,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, if (fm->argc < 1) break; width = strtonum(fm->argv[0], INT_MIN, INT_MAX, - &errptr); - if (errptr != NULL) + &errstr); + if (errstr != NULL) width = 0; break; case 'w': @@ -4088,6 +4089,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'l': modifiers |= FORMAT_LITERAL; break; + case 'a': + modifiers |= FORMAT_CHARACTER; + break; case 'b': modifiers |= FORMAT_BASENAME; break; @@ -4154,6 +4158,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, goto done; } + /* Is this a character? */ + if (modifiers & FORMAT_CHARACTER) { + new = format_expand1(es, copy); + c = strtonum(new, 32, 126, &errstr); + if (errstr != NULL) + value = xstrdup(""); + else + xasprintf(&value, "%c", c); + free (new); + goto done; + } + /* Is this a loop, comparison or condition? */ if (modifiers & FORMAT_SESSIONS) { value = format_loop_sessions(es, copy); diff --git a/mode-tree.c b/mode-tree.c index a47c0c06..c0f85026 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -46,6 +46,7 @@ struct mode_tree_data { mode_tree_search_cb searchcb; mode_tree_menu_cb menucb; mode_tree_height_cb heightcb; + mode_tree_key_cb keycb; struct mode_tree_list children; struct mode_tree_list saved; @@ -74,6 +75,10 @@ struct mode_tree_item { void *itemdata; u_int line; + key_code key; + const char *keystr; + size_t keylen; + uint64_t tag; const char *name; const char *text; @@ -135,6 +140,7 @@ mode_tree_free_item(struct mode_tree_item *mti) free((void *)mti->name); free((void *)mti->text); + free((void *)mti->keystr); free(mti); } @@ -193,6 +199,26 @@ mode_tree_build_lines(struct mode_tree_data *mtd, flat = 0; if (mti->expanded) mode_tree_build_lines(mtd, &mti->children, depth + 1); + + if (mtd->keycb != NULL) { + mti->key = mtd->keycb(mtd->modedata, mti->itemdata, + mti->line); + if (mti->key == KEYC_UNKNOWN) + mti->key = KEYC_NONE; + } else if (mti->line < 10) + mti->key = '0' + mti->line; + else if (mti->line < 36) + mti->key = KEYC_META|('a' + mti->line - 10); + else + mti->key = KEYC_NONE; + if (mti->key != KEYC_NONE) { + mti->keystr = xstrdup(key_string_lookup_key(mti->key, + 0)); + mti->keylen = strlen(mti->keystr); + } else { + mti->keystr = NULL; + mti->keylen = 0; + } } TAILQ_FOREACH(mti, mtl, entry) { for (i = 0; i < mtd->line_size; i++) { @@ -363,7 +389,7 @@ struct mode_tree_data * mode_tree_start(struct window_pane *wp, struct args *args, mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb, mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, - mode_tree_height_cb heightcb, void *modedata, + mode_tree_height_cb heightcb, mode_tree_key_cb keycb, void *modedata, const struct menu_item *menu, const char **sort_list, u_int sort_size, struct screen **s) { @@ -402,6 +428,7 @@ mode_tree_start(struct window_pane *wp, struct args *args, mtd->searchcb = searchcb; mtd->menucb = menucb; mtd->heightcb = heightcb; + mtd->keycb = keycb; TAILQ_INIT(&mtd->children); @@ -596,10 +623,10 @@ mode_tree_draw(struct mode_tree_data *mtd) struct screen_write_ctx ctx; struct grid_cell gc0, gc; u_int w, h, i, j, sy, box_x, box_y, width; - char *text, *start, key[7]; + char *text, *start, *key; const char *tag, *symbol; size_t size, n; - int keylen; + int keylen, pad; if (mtd->line_size == 0) return; @@ -614,28 +641,30 @@ mode_tree_draw(struct mode_tree_data *mtd) screen_write_start(&ctx, s); screen_write_clearscreen(&ctx, 8); - if (mtd->line_size > 10) - keylen = 6; - else - keylen = 4; + keylen = 0; + for (i = 0; i < mtd->line_size; i++) { + mti = mtd->line_list[i].item; + if (mti->key == KEYC_NONE) + continue; + if ((int)mti->keylen + 3 > keylen) + keylen = mti->keylen + 3; + } for (i = 0; i < mtd->line_size; i++) { if (i < mtd->offset) continue; if (i > mtd->offset + h - 1) break; - line = &mtd->line_list[i]; mti = line->item; screen_write_cursormove(&ctx, 0, i - mtd->offset, 0); - if (i < 10) - snprintf(key, sizeof key, "(%c) ", '0' + i); - else if (i < 36) - snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10)); + pad = keylen - 2 - mti->keylen; + if (mti->key != KEYC_NONE) + xasprintf(&key, "(%s)%*s", mti->keystr, pad, ""); else - *key = '\0'; + key = xstrdup(""); if (line->flat) symbol = ""; @@ -698,6 +727,7 @@ mode_tree_draw(struct mode_tree_data *mtd) } } free(text); + free(key); if (mti->tagged) { gc.attr ^= GRID_ATTR_BRIGHT; @@ -951,7 +981,6 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, struct mode_tree_item *current, *parent, *mti; u_int i, x, y; int choice; - key_code tmp; if (KEYC_IS_MOUSE(*key) && m != NULL) { if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) { @@ -993,12 +1022,11 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, current = line->item; choice = -1; - if (*key >= '0' && *key <= '9') - choice = (*key) - '0'; - else if (((*key) & KEYC_MASK_MODIFIERS) == KEYC_META) { - tmp = (*key) & KEYC_MASK_KEY; - if (tmp >= 'a' && tmp <= 'z') - choice = 10 + (tmp - 'a'); + for (i = 0; i < mtd->line_size; i++) { + if (*key == mtd->line_list[i].item->key) { + choice = i; + break; + } } if (choice != -1) { if ((u_int)choice > mtd->line_size - 1) { diff --git a/tmux.1 b/tmux.1 index 00b5cd84..74cb5f00 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1985,12 +1985,17 @@ The default is to capture only the visible contents of the pane. .Op Fl NrZ .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl K Ar key-format .Op Fl O Ar sort-order .Op Fl t Ar target-pane .Op Ar template .Xc Put a pane into client mode, allowing a client to be selected interactively from a list. +Each client is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the list may be navigated and an item chosen or otherwise manipulated using +the keys below. .Fl Z zooms the pane. The following keys may be used in client mode: @@ -2040,7 +2045,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. If a filter would lead to an empty list, it is ignored. .Fl F -specifies the format for each item in the list. +specifies the format for each item in the list and +.Fl K +a format for each shortcut key; both are evaluated once for each line. .Fl N starts without the preview. This command works only if at least one client is attached. @@ -2049,12 +2056,17 @@ This command works only if at least one client is attached. .Op Fl GNrswZ .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl K Ar key-format .Op Fl O Ar sort-order .Op Fl t Ar target-pane .Op Ar template .Xc Put a pane into tree mode, where a session, window or pane may be chosen -interactively from a list. +interactively from a tree. +Each session, window or pane is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the tree may be navigated and an item chosen or otherwise manipulated using +the keys below. .Fl s starts with sessions collapsed and .Fl w @@ -2113,7 +2125,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. If a filter would lead to an empty list, it is ignored. .Fl F -specifies the format for each item in the tree. +specifies the format for each item in the tree and +.Fl K +a format for each shortcut key; both are evaluated once for each line. .Fl N starts without the preview. .Fl G @@ -4663,6 +4677,11 @@ For example, multiplies 5.5 by 3 for a result with four decimal places and .Ql #{e|%%:7,3} returns the modulus of 7 and 3. +.Ql a +replaces a numeric argument by its ASCII equivalent, so +.Ql #{a:98} +results in +.Ql b . .Pp A limit may be placed on the length of the resultant string by prefixing it by an @@ -5681,12 +5700,17 @@ The buffer commands are as follows: .Op Fl NZr .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl K Ar key-format .Op Fl O Ar sort-order .Op Fl t Ar target-pane .Op Ar template .Xc Put a pane into buffer mode, where a buffer may be chosen interactively from a list. +Each buffer is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the list may be navigated and an item chosen or otherwise manipulated using +the keys below. .Fl Z zooms the pane. The following keys may be used in buffer mode: @@ -5734,7 +5758,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. If a filter would lead to an empty list, it is ignored. .Fl F -specifies the format for each item in the list. +specifies the format for each item in the list and +.Fl K +a format for each shortcut key; both are evaluated once for each line. .Fl N starts without the preview. This command works only if at least one client is attached. diff --git a/tmux.h b/tmux.h index 0c7636f8..9160ef92 100644 --- a/tmux.h +++ b/tmux.h @@ -2855,6 +2855,7 @@ typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *, typedef int (*mode_tree_search_cb)(void *, void *, const char *); typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code); typedef u_int (*mode_tree_height_cb)(void *, u_int); +typedef key_code (*mode_tree_key_cb)(void *, void *, u_int); typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code); u_int mode_tree_count_tagged(struct mode_tree_data *); void *mode_tree_get_current(struct mode_tree_data *); @@ -2869,7 +2870,7 @@ void mode_tree_up(struct mode_tree_data *, int); void mode_tree_down(struct mode_tree_data *, int); struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *, mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb, - mode_tree_menu_cb, mode_tree_height_cb, void *, + mode_tree_menu_cb, mode_tree_height_cb, mode_tree_key_cb, void *, const struct menu_item *, const char **, u_int, struct screen **); void mode_tree_zoom(struct mode_tree_data *, struct args *); void mode_tree_build(struct mode_tree_data *); diff --git a/window-buffer.c b/window-buffer.c index 4599cbc5..5e4dd699 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -41,6 +41,17 @@ static void window_buffer_key(struct window_mode_entry *, #define WINDOW_BUFFER_DEFAULT_FORMAT \ "#{t/p:buffer_created}: #{buffer_sample}" +#define WINDOW_BUFFER_DEFAULT_KEY_FORMAT \ + "#{?#{e|<:#{line},10}," \ + "#{line}" \ + "," \ + "#{?#{e|<:#{line},36}," \ + "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ + "," \ + "" \ + "}" \ + "}" + static const struct menu_item window_buffer_menu_items[] = { { "Paste", 'p', NULL }, { "Paste Tagged", 'P', NULL }, @@ -93,6 +104,7 @@ struct window_buffer_modedata { struct mode_tree_data *data; char *command; char *format; + char *key_format; struct window_buffer_itemdata **item_list; u_int item_size; @@ -232,7 +244,8 @@ window_buffer_draw(__unused void *modedata, void *itemdata, while (end != pdata + psize && *end != '\n') end++; buf = xreallocarray(buf, 4, end - start + 1); - utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_CSTYLE|VIS_TAB); + utf8_strvis(buf, start, end - start, + VIS_OCTAL|VIS_CSTYLE|VIS_TAB); if (*buf != '\0') { screen_write_cursormove(ctx, cx, cy + i, 0); screen_write_nputs(ctx, sx, &grid_default_cell, "%s", @@ -275,6 +288,41 @@ window_buffer_menu(void *modedata, struct client *c, key_code key) window_buffer_key(wme, c, NULL, NULL, key, NULL); } +static key_code +window_buffer_get_key(void *modedata, void *itemdata, u_int line) +{ + struct window_buffer_modedata *data = modedata; + struct window_buffer_itemdata *item = itemdata; + struct format_tree *ft; + struct session *s; + struct winlink *wl; + struct window_pane *wp; + struct paste_buffer *pb; + char *expanded; + key_code key; + + if (cmd_find_valid_state(&data->fs)) { + s = data->fs.s; + wl = data->fs.wl; + wp = data->fs.wp; + } + pb = paste_get_name(item->name); + if (pb == NULL) + return KEYC_NONE; + + ft = format_create(NULL, NULL, FORMAT_NONE, 0); + format_defaults(ft, NULL, NULL, 0, NULL); + format_defaults(ft, NULL, s, wl, wp); + format_defaults_paste_buffer(ft, pb); + format_add(ft, "line", "%u", line); + + expanded = format_expand(ft, data->key_format); + key = key_string_lookup_string(expanded); + free(expanded); + format_free(ft); + return key; +} + static struct screen * window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) @@ -291,6 +339,10 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT); else data->format = xstrdup(args_get(args, 'F')); + if (args == NULL || !args_has(args, 'K')) + data->key_format = xstrdup(WINDOW_BUFFER_DEFAULT_KEY_FORMAT); + else + data->key_format = xstrdup(args_get(args, 'K')); if (args == NULL || args->argc == 0) data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND); else @@ -298,8 +350,8 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->data = mode_tree_start(wp, args, window_buffer_build, window_buffer_draw, window_buffer_search, window_buffer_menu, NULL, - data, window_buffer_menu_items, window_buffer_sort_list, - nitems(window_buffer_sort_list), &s); + window_buffer_get_key, data, window_buffer_menu_items, + window_buffer_sort_list, nitems(window_buffer_sort_list), &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); @@ -324,6 +376,7 @@ window_buffer_free(struct window_mode_entry *wme) free(data->item_list); free(data->format); + free(data->key_format); free(data->command); free(data); diff --git a/window-client.c b/window-client.c index ec3c646a..db7c6dcc 100644 --- a/window-client.c +++ b/window-client.c @@ -40,6 +40,17 @@ static void window_client_key(struct window_mode_entry *, #define WINDOW_CLIENT_DEFAULT_FORMAT \ "#{t/p:client_activity}: session #{session_name}" +#define WINDOW_CLIENT_DEFAULT_KEY_FORMAT \ + "#{?#{e|<:#{line},10}," \ + "#{line}" \ + "," \ + "#{?#{e|<:#{line},36}," \ + "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ + "," \ + "" \ + "}" \ + "}" + static const struct menu_item window_client_menu_items[] = { { "Detach", 'd', NULL }, { "Detach Tagged", 'D', NULL }, @@ -87,6 +98,7 @@ struct window_client_modedata { struct mode_tree_data *data; char *format; + char *key_format; char *command; struct window_client_itemdata **item_list; @@ -252,6 +264,26 @@ window_client_menu(void *modedata, struct client *c, key_code key) window_client_key(wme, c, NULL, NULL, key, NULL); } +static key_code +window_client_get_key(void *modedata, void *itemdata, u_int line) +{ + struct window_client_modedata *data = modedata; + struct window_client_itemdata *item = itemdata; + struct format_tree *ft; + char *expanded; + key_code key; + + ft = format_create(NULL, NULL, FORMAT_NONE, 0); + format_defaults(ft, item->c, NULL, 0, NULL); + format_add(ft, "line", "%u", line); + + expanded = format_expand(ft, data->key_format); + key = key_string_lookup_string(expanded); + free(expanded); + format_free(ft); + return key; +} + static struct screen * window_client_init(struct window_mode_entry *wme, __unused struct cmd_find_state *fs, struct args *args) @@ -267,15 +299,19 @@ window_client_init(struct window_mode_entry *wme, data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT); else data->format = xstrdup(args_get(args, 'F')); + if (args == NULL || !args_has(args, 'K')) + data->key_format = xstrdup(WINDOW_CLIENT_DEFAULT_KEY_FORMAT); + else + data->key_format = xstrdup(args_get(args, 'K')); if (args == NULL || args->argc == 0) data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND); else data->command = xstrdup(args->argv[0]); data->data = mode_tree_start(wp, args, window_client_build, - window_client_draw, NULL, window_client_menu, NULL, data, - window_client_menu_items, window_client_sort_list, - nitems(window_client_sort_list), &s); + window_client_draw, NULL, window_client_menu, NULL, + window_client_get_key, data, window_client_menu_items, + window_client_sort_list, nitems(window_client_sort_list), &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); @@ -300,6 +336,7 @@ window_client_free(struct window_mode_entry *wme) free(data->item_list); free(data->format); + free(data->key_format); free(data->command); free(data); diff --git a/window-customize.c b/window-customize.c index a1f191b5..fce002fd 100644 --- a/window-customize.c +++ b/window-customize.c @@ -890,8 +890,8 @@ window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->data = mode_tree_start(wp, args, window_customize_build, window_customize_draw, NULL, window_customize_menu, - window_customize_height, data, window_customize_menu_items, NULL, 0, - &s); + window_customize_height, NULL, data, window_customize_menu_items, + NULL, 0, &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); diff --git a/window-tree.c b/window-tree.c index 1498b337..881896e7 100644 --- a/window-tree.c +++ b/window-tree.c @@ -56,6 +56,17 @@ static void window_tree_key(struct window_mode_entry *, "}" \ "}" +#define WINDOW_TREE_DEFAULT_KEY_FORMAT \ + "#{?#{e|<:#{line},10}," \ + "#{line}" \ + "," \ + "#{?#{e|<:#{line},36}," \ + "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ + "," \ + "" \ + "}" \ + "}" + static const struct menu_item window_tree_menu_items[] = { { "Select", '\r', NULL }, { "Expand", KEYC_RIGHT, NULL }, @@ -117,6 +128,7 @@ struct window_tree_modedata { struct mode_tree_data *data; char *format; + char *key_format; char *command; int squash_groups; @@ -856,6 +868,35 @@ window_tree_menu(void *modedata, struct client *c, key_code key) window_tree_key(wme, c, NULL, NULL, key, NULL); } +static key_code +window_tree_get_key(void *modedata, void *itemdata, u_int line) +{ + struct window_tree_modedata *data = modedata; + struct window_tree_itemdata *item = itemdata; + struct format_tree *ft; + struct session *s; + struct winlink *wl; + struct window_pane *wp; + char *expanded; + key_code key; + + ft = format_create(NULL, NULL, FORMAT_NONE, 0); + window_tree_pull_item(item, &s, &wl, &wp); + if (item->type == WINDOW_TREE_SESSION) + format_defaults(ft, NULL, s, NULL, NULL); + else if (item->type == WINDOW_TREE_WINDOW) + format_defaults(ft, NULL, s, wl, NULL); + else + format_defaults(ft, NULL, s, wl, wp); + format_add(ft, "line", "%u", line); + + expanded = format_expand(ft, data->key_format); + key = key_string_lookup_string(expanded); + free(expanded); + format_free(ft); + return key; +} + static struct screen * window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) @@ -880,6 +921,10 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); else data->format = xstrdup(args_get(args, 'F')); + if (args == NULL || !args_has(args, 'K')) + data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT); + else + data->key_format = xstrdup(args_get(args, 'K')); if (args == NULL || args->argc == 0) data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); else @@ -887,9 +932,9 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->squash_groups = !args_has(args, 'G'); data->data = mode_tree_start(wp, args, window_tree_build, - window_tree_draw, window_tree_search, window_tree_menu, NULL, data, - window_tree_menu_items, window_tree_sort_list, - nitems(window_tree_sort_list), &s); + window_tree_draw, window_tree_search, window_tree_menu, NULL, + window_tree_get_key, data, window_tree_menu_items, + window_tree_sort_list, nitems(window_tree_sort_list), &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); @@ -913,6 +958,7 @@ window_tree_destroy(struct window_tree_modedata *data) free(data->item_list); free(data->format); + free(data->key_format); free(data->command); free(data); From e6abe55134df1b9dc3b7dd7f3a65dff272a35bb7 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 12 Apr 2021 09:36:12 +0000 Subject: [PATCH 0751/1006] Add a flag to disable keys to close a message, GitHub issue 2625. --- alerts.c | 9 +++++---- cmd-display-message.c | 10 ++++++---- cmd-list-keys.c | 7 ++++--- cmd-queue.c | 2 +- cmd-run-shell.c | 2 +- mode-tree.c | 2 +- server-client.c | 6 +++++- status.c | 7 +++++-- tmux.1 | 4 +++- tmux.h | 3 ++- window-customize.c | 4 ++-- 11 files changed, 35 insertions(+), 21 deletions(-) diff --git a/alerts.c b/alerts.c index 0f2eb179..b5ea0cf4 100644 --- a/alerts.c +++ b/alerts.c @@ -315,10 +315,11 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option) tty_putcode(&c->tty, TTYC_BEL); if (visual == VISUAL_OFF) continue; - if (c->session->curw == wl) - status_message_set(c, -1, 1, "%s in current window", type); - else { - status_message_set(c, -1, 1, "%s in window %d", type, + if (c->session->curw == wl) { + status_message_set(c, -1, 1, 0, "%s in current window", + type); + } else { + status_message_set(c, -1, 1, 0, "%s in window %d", type, wl->idx); } } diff --git a/cmd-display-message.c b/cmd-display-message.c index 301c1a5e..0522d37f 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 = { "acd:Ipt:F:v", 0, 1 }, - .usage = "[-aIpv] [-c target-client] [-d delay] [-F format] " + .args = { "acd:INpt:F:v", 0, 1 }, + .usage = "[-aINpv] [-c target-client] [-d delay] [-F format] " CMD_TARGET_PANE_USAGE " [message]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -132,8 +132,10 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "%s", msg); else if (args_has(args, 'p')) cmdq_print(item, "%s", msg); - else if (tc != NULL) - status_message_set(tc, delay, 0, "%s", msg); + else if (tc != NULL) { + status_message_set(tc, delay, 0, args_has(args, 'N'), "%s", + msg); + } free(msg); format_free(ft); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index dd82e57e..ca4bf752 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -113,9 +113,10 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, else note = xstrdup(bd->note); tmp = utf8_padcstr(key, keywidth + 1); - if (args_has(args, '1') && tc != NULL) - status_message_set(tc, -1, 1, "%s%s%s", prefix, tmp, note); - else + if (args_has(args, '1') && tc != NULL) { + status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp, + note); + } else cmdq_print(item, "%s%s%s", prefix, tmp, note); free(tmp); free(note); diff --git a/cmd-queue.c b/cmd-queue.c index a0d80c34..54163919 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -862,7 +862,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) c->retval = 1; } else { *msg = toupper((u_char) *msg); - status_message_set(c, -1, 1, "%s", msg); + status_message_set(c, -1, 1, 0, "%s", msg); } free(msg); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 73ed79f4..56d5f723 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -193,7 +193,7 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) if (status == CMD_PARSE_ERROR) { if (cdata->item == NULL) { *error = toupper((u_char)*error); - status_message_set(c, -1, 1, "%s", error); + status_message_set(c, -1, 1, 0, "%s", error); } else cmdq_error(cdata->item, "%s", error); free(error); diff --git a/mode-tree.c b/mode-tree.c index c0f85026..ca7f33a4 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1204,7 +1204,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, 1, "%s", error); + status_message_set(c, -1, 1, 0, "%s", error); } free(error); } diff --git a/server-client.c b/server-client.c index 219fdf3a..25d61376 100644 --- a/server-client.c +++ b/server-client.c @@ -1311,7 +1311,11 @@ server_client_handle_key(struct client *c, struct key_event *event) * immediately rather than queued. */ if (~c->flags & CLIENT_READONLY) { - status_message_clear(c); + if (c->message_string != NULL) { + if (c->message_ignore_keys) + return (0); + status_message_clear(c); + } if (c->overlay_key != NULL) { switch (c->overlay_key(c, event)) { case 0: diff --git a/status.c b/status.c index 154d9452..f9786f4b 100644 --- a/status.c +++ b/status.c @@ -424,7 +424,7 @@ status_redraw(struct client *c) /* Set a status line message. */ void status_message_set(struct client *c, int delay, int ignore_styles, - const char *fmt, ...) + int ignore_keys, const char *fmt, ...) { struct timeval tv; va_list ap; @@ -433,7 +433,6 @@ status_message_set(struct client *c, int delay, int ignore_styles, status_push_screen(c); va_start(ap, fmt); - c->message_ignore_styles = ignore_styles; xvasprintf(&c->message_string, fmt, ap); va_end(ap); @@ -456,6 +455,10 @@ status_message_set(struct client *c, int delay, int ignore_styles, evtimer_add(&c->message_timer, &tv); } + if (delay != 0) + c->message_ignore_keys = ignore_keys; + c->message_ignore_styles = ignore_styles; + c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_REDRAWSTATUS; } diff --git a/tmux.1 b/tmux.1 index 74cb5f00..ba82a101 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5565,7 +5565,7 @@ The following keys are also available: .It Li "q" Ta "Exit menu" .El .It Xo Ic display-message -.Op Fl aIpv +.Op Fl aINpv .Op Fl c Ar target-client .Op Fl d Ar delay .Op Fl t Ar target-pane @@ -5585,6 +5585,8 @@ If is not given, the .Ic message-time option is used; a delay of zero waits for a key press. +.Ql N +ignores key presses and closes only after the delay expires. The format of .Ar message is described in the diff --git a/tmux.h b/tmux.h index 9160ef92..fb2f35f9 100644 --- a/tmux.h +++ b/tmux.h @@ -1687,6 +1687,7 @@ struct client { uint64_t redraw_panes; + int message_ignore_keys; int message_ignore_styles; char *message_string; struct event message_timer; @@ -2490,7 +2491,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 status_message_set(struct client *, int, int, const char *, ...); +void status_message_set(struct client *, int, 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 fce002fd..34a13f73 100644 --- a/window-customize.c +++ b/window-customize.c @@ -999,7 +999,7 @@ window_customize_set_option_callback(struct client *c, void *itemdata, fail: *cause = toupper((u_char)*cause); - status_message_set(c, -1, 1, "%s", cause); + status_message_set(c, -1, 1, 0, "%s", cause); free(cause); return (0); } @@ -1205,7 +1205,7 @@ window_customize_set_command_callback(struct client *c, void *itemdata, fail: *error = toupper((u_char)*error); - status_message_set(c, -1, 1, "%s", error); + status_message_set(c, -1, 1, 0, "%s", error); free(error); return (0); } From 715835510beff71cb666600e0c139f82fd14cc2d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Apr 2021 05:23:34 +0000 Subject: [PATCH 0752/1006] Handle C-Tab correctly with extended keys, GitHub issue 2642. --- tty-keys.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index e88ff227..c0aceb32 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -953,11 +953,14 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, * Don't allow both KEYC_CTRL and as an implied modifier. Also convert * C-X into C-x and so on. */ - if (nkey & KEYC_CTRL){ + if (nkey & KEYC_CTRL) { onlykey = (nkey & KEYC_MASK_KEY); - if (onlykey < 32) - onlykey = (nkey & ~KEYC_CTRL); - else { + if (onlykey < 32) { + if (onlykey != 9) + onlykey = (nkey & ~KEYC_CTRL); + else + onlykey = (9|KEYC_CTRL); + } else { if (onlykey >= 97 && onlykey <= 122) onlykey -= 96; else if (onlykey >= 64 && onlykey <= 95) From ff860e5fe41bdef7a4a0ba74f9be4a612c546656 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Apr 2021 05:25:05 +0000 Subject: [PATCH 0753/1006] Move mode set/reset after sync so cursor doesn't flicker, from Avi Halachmi. --- screen-redraw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 6ddabc52..cf3e29f6 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -604,8 +604,8 @@ screen_redraw_screen(struct client *c) return; screen_redraw_set_context(c, &ctx); - tty_update_mode(&c->tty, c->tty.mode, NULL); tty_sync_start(&c->tty); + tty_update_mode(&c->tty, c->tty.mode, NULL); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { log_debug("%s: redrawing borders", c->name); @@ -640,8 +640,8 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) return; screen_redraw_set_context(c, &ctx); - tty_update_mode(&c->tty, c->tty.mode, NULL); tty_sync_start(&c->tty); + tty_update_mode(&c->tty, c->tty.mode, NULL); screen_redraw_draw_pane(&ctx, wp); From 30cf20d6153924d031fefde1e4ca7cfe31b32d88 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 13 Apr 2021 06:28:16 +0100 Subject: [PATCH 0754/1006] Update CHANGES. --- CHANGES | 12 ++++++++++++ configure.ac | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index a81f160c..209d7d31 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,17 @@ CHANGES FROM 3.1c TO 3.2 +* Add a flag to disable keys to close a message. + +* Permit shortcut keys in buffer, client, tree modes to be configured with a + format (-K flag to choose-buffer, choose-client, choose-tree). + +* Add a current_file format for the config file being parsed. + +* When display-message used in config file, show the message after the config + file finishes. + +* Add client-detached notification in control mode. + * Improve performance of format evaluation. * Make jump command support UTF-8 in copy mode. diff --git a/configure.ac b/configure.ac index b8762e2f..c1915f50 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.2-rc5) +AC_INIT([tmux], 3.2) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From f29d3c7f74c10666c9970382ea735e11e12186c5 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Apr 2021 05:23:34 +0000 Subject: [PATCH 0755/1006] Handle C-Tab correctly with extended keys, GitHub issue 2642. --- tty-keys.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index e88ff227..c0aceb32 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -953,11 +953,14 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, * Don't allow both KEYC_CTRL and as an implied modifier. Also convert * C-X into C-x and so on. */ - if (nkey & KEYC_CTRL){ + if (nkey & KEYC_CTRL) { onlykey = (nkey & KEYC_MASK_KEY); - if (onlykey < 32) - onlykey = (nkey & ~KEYC_CTRL); - else { + if (onlykey < 32) { + if (onlykey != 9) + onlykey = (nkey & ~KEYC_CTRL); + else + onlykey = (9|KEYC_CTRL); + } else { if (onlykey >= 97 && onlykey <= 122) onlykey -= 96; else if (onlykey >= 64 && onlykey <= 95) From bc4681c83d612a3d9609dd609e7d89b035b25dd5 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Apr 2021 05:25:05 +0000 Subject: [PATCH 0756/1006] Move mode set/reset after sync so cursor doesn't flicker, from Avi Halachmi. --- screen-redraw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 6ddabc52..cf3e29f6 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -604,8 +604,8 @@ screen_redraw_screen(struct client *c) return; screen_redraw_set_context(c, &ctx); - tty_update_mode(&c->tty, c->tty.mode, NULL); tty_sync_start(&c->tty); + tty_update_mode(&c->tty, c->tty.mode, NULL); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { log_debug("%s: redrawing borders", c->name); @@ -640,8 +640,8 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) return; screen_redraw_set_context(c, &ctx); - tty_update_mode(&c->tty, c->tty.mode, NULL); tty_sync_start(&c->tty); + tty_update_mode(&c->tty, c->tty.mode, NULL); screen_redraw_draw_pane(&ctx, wp); From bbb3509bc5c368ac480fdc6af00d5884eb5c1edc Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Apr 2021 12:26:34 +0000 Subject: [PATCH 0757/1006] Change how extended ctrl keys are processed to fix C-S-Tab and C-;. --- tty-keys.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index c0aceb32..040e7005 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -955,23 +955,19 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, */ if (nkey & KEYC_CTRL) { onlykey = (nkey & KEYC_MASK_KEY); - if (onlykey < 32) { - if (onlykey != 9) - onlykey = (nkey & ~KEYC_CTRL); - else - onlykey = (9|KEYC_CTRL); - } else { - if (onlykey >= 97 && onlykey <= 122) - onlykey -= 96; - else if (onlykey >= 64 && onlykey <= 95) - onlykey -= 64; - else if (onlykey == 32) - onlykey = 0; - else if (onlykey == 63) - onlykey = 127; - onlykey |= ((nkey & KEYC_MASK_MODIFIERS) & ~KEYC_CTRL); - } - nkey = onlykey; + if (onlykey < 32 && onlykey != 9) + /* nothing */; + else if (onlykey >= 97 && onlykey <= 122) + onlykey -= 96; + else if (onlykey >= 64 && onlykey <= 95) + onlykey -= 64; + else if (onlykey == 32) + onlykey = 0; + else if (onlykey == 63) + onlykey = 127; + else + onlykey |= KEYC_CTRL; + nkey = onlykey|((nkey & KEYC_MASK_MODIFIERS) & ~KEYC_CTRL); } if (log_get_level() != 0) { From dfcc9f8cbc65debba01fc96af0811c7401d4db1d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Apr 2021 16:00:47 +0000 Subject: [PATCH 0758/1006] Include modifiers when looking up an individual key. --- cmd-list-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index ca4bf752..91715f93 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -165,7 +165,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "invalid key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } - only &= KEYC_MASK_KEY; + only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS); } tablename = args_get(args, 'T'); From 33f9b08bbb043e1fdf5638a6ae517ab7f5e4547a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 15 Apr 2021 05:38:11 +0000 Subject: [PATCH 0759/1006] %begin now has three arguments, not two. GitHubs issue 2646. --- tmux.1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index ba82a101..f5689bfa 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6116,12 +6116,13 @@ and matching .Em %end or .Em %error -have two arguments: an integer time (as seconds from epoch) and command number. +have three arguments: an integer time (as seconds from epoch), command number and +flags (currently not used). For example: .Bd -literal -offset indent -%begin 1363006971 2 +%begin 1363006971 2 1 0: ksh* (1 panes) [80x24] [layout b25f,80x24,0,0,2] @2 (active) -%end 1363006971 2 +%end 1363006971 2 1 .Ed .Pp The From bb6242675ad0c7447daef148fffced882e5b4a61 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 15 Apr 2021 06:45:19 +0100 Subject: [PATCH 0760/1006] Add crosscompiling fallbacks, from Hasso Tepper. --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 4175f5c8..79e0c4c0 100644 --- a/configure.ac +++ b/configure.ac @@ -163,6 +163,7 @@ AC_RUN_IFELSE([AC_LANG_PROGRAM( [return (reallocarray(NULL, 1, 1) == NULL);] )], AC_MSG_RESULT(yes), + [AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])], [AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])] ) AC_MSG_CHECKING([for working recallocarray]) @@ -171,6 +172,7 @@ AC_RUN_IFELSE([AC_LANG_PROGRAM( [return (recallocarray(NULL, 1, 1, 1) == NULL);] )], AC_MSG_RESULT(yes), + [AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])], [AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])] ) From 98650658a7c63dfe77200709288418078a42de3f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 15 Apr 2021 08:22:36 +0100 Subject: [PATCH 0761/1006] Handle modifier 9 as Meta, GitHub issue 2647. --- tty-keys.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index 040e7005..5bf4e4a5 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -252,7 +252,8 @@ static const key_code tty_default_xterm_modifiers[] = { KEYC_CTRL, KEYC_SHIFT|KEYC_CTRL, KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, - KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, + KEYC_META|KEYC_IMPLIED_META }; /* @@ -944,6 +945,9 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, case 8: nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); break; + case 9: + nkey |= (KEYC_META|KEYC_IMPLIED_META); + break; default: *key = KEYC_NONE; break; From dda3bf896be9ce87b4066636cc7f94ab8030133a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 15 Apr 2021 16:10:31 +0100 Subject: [PATCH 0762/1006] Revert "Add crosscompiling fallbacks, from Hasso Tepper." This reverts commit bb6242675ad0c7447daef148fffced882e5b4a61. --- configure.ac | 2 -- 1 file changed, 2 deletions(-) diff --git a/configure.ac b/configure.ac index 79e0c4c0..4175f5c8 100644 --- a/configure.ac +++ b/configure.ac @@ -163,7 +163,6 @@ AC_RUN_IFELSE([AC_LANG_PROGRAM( [return (reallocarray(NULL, 1, 1) == NULL);] )], AC_MSG_RESULT(yes), - [AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])], [AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])] ) AC_MSG_CHECKING([for working recallocarray]) @@ -172,7 +171,6 @@ AC_RUN_IFELSE([AC_LANG_PROGRAM( [return (recallocarray(NULL, 1, 1, 1) == NULL);] )], AC_MSG_RESULT(yes), - [AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])], [AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])] ) From 9af78c8e694dd3c05859c228856621a5a746de58 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 16 Apr 2021 11:59:08 +0100 Subject: [PATCH 0763/1006] Adjust latest client when a client detaches, GitHub issue 2657. --- server-client.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index d3ffd682..0f8ad687 100644 --- a/server-client.c +++ b/server-client.c @@ -43,6 +43,7 @@ static void server_client_check_modes(struct client *); static void server_client_set_title(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); +static void server_client_update_latest(struct client *); static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch_command(struct client *, struct imsg *); @@ -271,6 +272,40 @@ server_client_open(struct client *c, char **cause) return (0); } +/* Lost an attached client. */ +static void +server_client_attached_lost(struct client *c) +{ + struct session *s = c->session; + struct window *w; + struct client *loop; + struct client *found; + + log_debug("lost attached client %p", c); + + /* + * By this point the session in the client has been cleared so walk all + * windows to find any with this client as the latest. + */ + RB_FOREACH(w, windows, &windows) { + if (w->latest != c) + continue; + + found = NULL; + TAILQ_FOREACH(loop, &clients, entry) { + s = loop->session; + if (loop == c || s == NULL || s->curw->window != w) + continue; + if (found == NULL || + timercmp(&loop->activity_time, &found->activity_time, + >)) + found = loop; + } + if (found != NULL) + server_client_update_latest(found); + } +} + /* Lost a client. */ void server_client_lost(struct client *c) @@ -296,8 +331,10 @@ server_client_lost(struct client *c) TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); - if (c->flags & CLIENT_ATTACHED) + if (c->flags & CLIENT_ATTACHED) { + server_client_attached_lost(c); notify_client("client-detached", c); + } if (c->flags & CLIENT_CONTROL) control_stop(c); From b2588eed03640dc74ffdbebd10c719064b575291 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 16 Apr 2021 12:07:54 +0100 Subject: [PATCH 0764/1006] Apple have broken strtonum so check it works, from Teubel Gyorgy. --- configure.ac | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4175f5c8..b2a13c5e 100644 --- a/configure.ac +++ b/configure.ac @@ -150,10 +150,19 @@ AC_REPLACE_FUNCS([ \ strlcpy \ strndup \ strsep \ - strtonum \ ]) AC_FUNC_STRNLEN +# Check if strtonum works. +AC_MSG_CHECKING([for working strtonum]) +AC_RUN_IFELSE([AC_LANG_PROGRAM( + [#include ], + [return (strtonum("0", 0, 1, NULL) == 0 ? 0 : 1);] + )], + [AC_DEFINE(HAVE_STRTONUM) AC_MSG_RESULT(yes)], + [AC_LIBOBJ(strtonum) AC_MSG_RESULT(no)] +) + # Clang sanitizers wrap reallocarray even if it isn't available on the target # system. When compiled it always returns NULL and crashes the program. To # detect this we need a more complicated test. From 73cf767a35f2123cc0ba40080ae1d58da9ed6e35 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 16 Apr 2021 12:12:50 +0100 Subject: [PATCH 0765/1006] Fix display-menu -xR, from Alexis Hildebrandt. --- cmd-display-menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 5a5aabcd..de423e68 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -205,7 +205,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, if (xp == NULL || strcmp(xp, "C") == 0) xp = "#{popup_centre_x}"; else if (strcmp(xp, "R") == 0) - xp = "#{popup_right}"; + xp = "#{popup_pane_right}"; else if (strcmp(xp, "P") == 0) xp = "#{popup_pane_left}"; else if (strcmp(xp, "M") == 0) From 5fa8e5e13f86cccd3bff6d6118328d0772d6f147 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 18 Apr 2021 08:47:11 +0100 Subject: [PATCH 0766/1006] back-to-indentation fixes, from Anindya Mukherjee. --- grid-reader.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/grid-reader.c b/grid-reader.c index 89fe90fb..df0dd450 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -371,19 +371,26 @@ void grid_reader_cursor_back_to_indentation(struct grid_reader *gr) { struct grid_cell gc; - u_int px, py, xx, yy; + u_int px, py, xx, yy, oldx, oldy; yy = gr->gd->hsize + gr->gd->sy - 1; + oldx = gr->cx; + oldy = gr->cy; grid_reader_cursor_start_of_line(gr, 1); for (py = gr->cy; py <= yy; py++) { xx = grid_line_length(gr->gd, py); for (px = 0; px < xx; px++) { grid_get_cell(gr->gd, px, py, &gc); - if (gc.data.size != 1 || *gc.data.data != ' ') - break; + if (gc.data.size != 1 || *gc.data.data != ' ') { + gr->cx = px; + gr->cy = py; + return; + } } if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED) break; } + gr->cx = oldx; + gr->cy = oldy; } From 3a892228f45f744982fba827266cdf115db4464b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 18 Apr 2021 08:48:03 +0100 Subject: [PATCH 0767/1006] Use = not ==, from Leonardo Taccari. --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index b2a13c5e..8b13854f 100644 --- a/configure.ac +++ b/configure.ac @@ -34,10 +34,10 @@ AC_ARG_VAR( # Set up convenient fuzzing defaults before initializing compiler. if test "x$enable_fuzzing" = xyes; then AC_DEFINE(NEED_FUZZING) - test "x$CC" == x && CC=clang - test "x$FUZZING_LIBS" == x && \ + test "x$CC" = x && CC=clang + test "x$FUZZING_LIBS" = x && \ FUZZING_LIBS="-fsanitize=fuzzer" - test "x$SAVED_CFLAGS" == x && \ + test "x$SAVED_CFLAGS" = x && \ AM_CFLAGS="-g -fsanitize=fuzzer-no-link,address" fi From 88575a27e2e847dbb97e44780ebe0e415f6a4ba5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 20 Apr 2021 06:35:54 +0100 Subject: [PATCH 0768/1006] Add another couple of keys needed for extended keys, GitHub issue 2658. --- key-bindings.c | 6 ++++++ key-string.c | 10 +++++++--- tty-keys.c | 5 ++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index b47f6ff7..467c7f93 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -215,6 +215,9 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, if (repeat) bd->flags |= KEY_BINDING_REPEAT; bd->cmdlist = cmdlist; + + log_debug("%s: %#llx %s = %s", __func__, bd->key, + key_string_lookup_key(bd->key, 1), cmd_list_print(bd->cmdlist, 0)); } void @@ -231,6 +234,9 @@ key_bindings_remove(const char *name, key_code key) if (bd == NULL) return; + log_debug("%s: %#llx %s", __func__, bd->key, + key_string_lookup_key(bd->key, 1)); + RB_REMOVE(key_bindings, &table->key_bindings, bd); key_bindings_free(bd); diff --git a/key-string.c b/key-string.c index 8d60f132..c24a33fc 100644 --- a/key-string.c +++ b/key-string.c @@ -164,7 +164,7 @@ key_string_get_modifiers(const char **string) key_code key_string_lookup_string(const char *string) { - static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177"; + static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177`/"; key_code key, modifiers; u_int u, i; struct utf8_data ud, *udp; @@ -238,8 +238,12 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && - strchr(other, key) == NULL) { + if (key < KEYC_BASE && + (modifiers & KEYC_CTRL) && + strchr(other, key) == NULL && + key != 9 && + key != 13 && + key != 27) { if (key >= 97 && key <= 122) key -= 96; else if (key >= 64 && key <= 95) diff --git a/tty-keys.c b/tty-keys.c index 5bf4e4a5..3012be3d 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -959,7 +959,10 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, */ if (nkey & KEYC_CTRL) { onlykey = (nkey & KEYC_MASK_KEY); - if (onlykey < 32 && onlykey != 9) + if (onlykey < 32 && + onlykey != 9 && + onlykey != 13 && + onlykey != 27) /* nothing */; else if (onlykey >= 97 && onlykey <= 122) onlykey -= 96; From 6c2bf0e22119804022c8038563b9865999f5a026 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 20 Apr 2021 06:37:01 +0100 Subject: [PATCH 0769/1006] Minor CHANGES and tmux.1 fixed, from Daniel Hahler, GitHub issue 2664. --- CHANGES | 10 +++++----- tmux.1 | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 21226e79..42467dd3 100644 --- a/CHANGES +++ b/CHANGES @@ -33,7 +33,7 @@ CHANGES FROM 3.1c TO 3.2 * Add a -S flag to new-window to make it select the existing window if one with the given name already exists rather than failing with an error. -* Addd a format modifier to check if a window or session name exists (N/w or +* Add a format modifier to check if a window or session name exists (N/w or N/s). * Add compat clock_gettime for older macOS. @@ -65,7 +65,7 @@ CHANGES FROM 3.1c TO 3.2 an option on all panes. * Make replacement of ##s consistent when drawing formats, whether followed by - [ or not. Add a flag (e) to the q: format modifier to double up #s + [ or not. Add a flag (e) to the q: format modifier to double up #s. * Add -N flag to display-panes to ignore keys. @@ -269,7 +269,7 @@ CHANGES FROM 3.1c TO 3.2 * Wait until the initial command sequence is done before sending a device attributes request and other bits that prompt a reply from the terminal. This - means that stray relies are not left on the terminal if the command has + means that stray replies are not left on the terminal if the command has attached and then immediately detached and tmux will not be around to receive them. @@ -284,7 +284,7 @@ CHANGES FROM 3.1c TO 3.2 window-renamed window-unlinked - And these now pane options: + And these are now pane options: pane-died pane-exited @@ -359,7 +359,7 @@ CHANGES FROM 3.1c TO 3.2 * Add a default binding for button 2 to paste. * Add -d flag to run-shell to delay before running the command and allow it to - run without a command so it just delays. + be used without a command so it just delays. * Add C-g to cancel command prompt with vi keys as well as emacs, and q in command mode. diff --git a/tmux.1 b/tmux.1 index 84cddc55..c4fc06ba 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3501,8 +3501,8 @@ capabilities to be set instead, is intended for classes of functionality supported in a standard way but not reported by .Xr terminfo 5 . -Care must be taken only to configure this with features the terminal actually -support. +Care must be taken to configure this only with features the terminal actually +supports. .Pp This is an array option where each entry is a colon-separated string made up of a terminal type pattern (matched using From 8363c6af2e71cd99e6574e82f80bdb66973f0bd3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Apr 2021 09:32:48 +0100 Subject: [PATCH 0770/1006] Add an "always" value to the extended-keys option to always forward these keys to applications inside tmux. --- input.c | 2 ++ options-table.c | 6 +++++- screen.c | 4 +++- tmux.1 | 21 +++++++++++++++++---- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/input.c b/input.c index f6aeb027..b599f27d 100644 --- a/input.c +++ b/input.c @@ -1390,6 +1390,8 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_MODSET: n = input_get(ictx, 0, 0, 0); m = input_get(ictx, 1, 0, 0); + if (options_get_number(global_options, "extended-keys") == 2) + break; if (n == 0 || (n == 4 && m == 0)) screen_write_mode_clear(sctx, MODE_KEXTENDED); else if (n == 4 && (m == 1 || m == 2)) diff --git a/options-table.c b/options-table.c index b185969c..8264d1b7 100644 --- a/options-table.c +++ b/options-table.c @@ -74,6 +74,9 @@ static const char *options_table_remain_on_exit_list[] = { static const char *options_table_detach_on_destroy_list[] = { "off", "on", "no-detached", NULL }; +static const char *options_table_extended_keys_list[] = { + "off", "on", "always", NULL +}; /* Status line format. */ #define OPTIONS_TABLE_STATUS_FORMAT1 \ @@ -266,8 +269,9 @@ const struct options_table_entry options_table[] = { }, { .name = "extended-keys", - .type = OPTIONS_TABLE_FLAG, + .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SERVER, + .choices = options_table_extended_keys_list, .default_num = 0, .text = "Whether to request extended key sequences from terminals " "that support it." diff --git a/screen.c b/screen.c index 2b9d70de..2b83c285 100644 --- a/screen.c +++ b/screen.c @@ -100,7 +100,9 @@ screen_reinit(struct screen *s) s->rupper = 0; s->rlower = screen_size_y(s) - 1; - s->mode = MODE_CURSOR | MODE_WRAP; + s->mode = MODE_CURSOR|MODE_WRAP; + if (options_get_number(global_options, "extended-keys") == 2) + s->mode |= MODE_KEXTENDED; if (s->saved_grid != NULL) screen_alternate_off(s, NULL, 0); diff --git a/tmux.1 b/tmux.1 index c4fc06ba..6967c769 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3418,11 +3418,24 @@ sessions. .Xc If enabled, the server will exit when there are no attached clients. .It Xo Ic extended-keys -.Op Ic on | off +.Op Ic on | off | always .Xc -When enabled, extended keys are requested from the terminal and if supported -are recognised by -.Nm . +When +.Ic on +or +.Ic always , +the escape sequence to enable extended keys is sent to the terminal, if +.Nm +knows that it is supported. +.Nm +always recognises extended keys itself. +If this option is +.Ic on , +.Nm +will only forward extended keys to applications when they request them; if +.Ic always , +.Nm +will always forward the keys. .It Xo Ic focus-events .Op Ic on | off .Xc From cd6af4a52ef074754e25d5658f04c78f8353114e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Apr 2021 09:47:03 +0100 Subject: [PATCH 0771/1006] Include current client in size calcultion for new sessions, GitHub issue 2662. --- cmd-new-session.c | 1 + resize.c | 47 +++++++++++++++++++++++++++++++++-------------- status.c | 2 ++ window.c | 2 ++ 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index cc3494de..c47d66cd 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -269,6 +269,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; + sc.tc = c; sc.name = args_get(args, 'n'); sc.argc = args->argc; diff --git a/resize.c b/resize.c index 172cbcb5..66cb9430 100644 --- a/resize.c +++ b/resize.c @@ -108,17 +108,19 @@ clients_with_window(struct window *w) } 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) +clients_calculate_size(int type, int current, struct client *c, + 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) + if (type == WINDOW_SIZE_MANUAL) { + log_debug("%s: type is manual", __func__); return (0); + } /* * Start comparing with 0 for largest and UINT_MAX for smallest or @@ -139,18 +141,24 @@ clients_calculate_size(int type, int current, struct session *s, /* Loop over the clients and work out the size. */ TAILQ_FOREACH(loop, &clients, entry) { - if (ignore_client_size(loop)) + if (loop != c && ignore_client_size(loop)) { + log_debug("%s: ignoring %s", __func__, loop->name); continue; - if (skip_client(loop, type, current, s, w)) + } + if (loop != c && skip_client(loop, type, current, s, w)) { + log_debug("%s: skipping %s", __func__, loop->name); 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) + if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest) { + log_debug("%s: %s is not latest", __func__, loop->name); continue; + } /* Work out this client's size. */ cx = loop->tty.sx; @@ -175,16 +183,24 @@ clients_calculate_size(int type, int current, struct session *s, *xpixel = loop->tty.xpixel; *ypixel = loop->tty.ypixel; } + log_debug("%s: after %s (%ux%u), size is %ux%u", __func__, + loop->name, cx, cy, *sx, *sy); } /* Return whether a suitable size was found. */ - if (type == WINDOW_SIZE_LARGEST) + if (type == WINDOW_SIZE_LARGEST) { + log_debug("%s: type is largest", __func__); return (*sx != 0 && *sy != 0); + } + if (type == WINDOW_SIZE_LATEST) + log_debug("%s: type is latest", __func__); + else + log_debug("%s: type is smallest", __func__); return (*sx != UINT_MAX && *sy != UINT_MAX); } static int -default_window_size_skip_client (struct client *loop, int type, +default_window_size_skip_client(struct client *loop, int type, __unused int current, struct session *s, struct window *w) { /* @@ -221,23 +237,25 @@ default_window_size(struct client *c, struct session *s, struct window *w, *sy = c->tty.sy - status_line_size(c); *xpixel = c->tty.xpixel; *ypixel = c->tty.ypixel; + log_debug("%s: using %ux%u from %s", __func__, *sx, *sy, + c->name); goto done; } - if (w == NULL) - type = WINDOW_SIZE_MANUAL; } /* * 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, + if (!clients_calculate_size(type, 0, c, 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; } + log_debug("%s: using %ux%u from default-size", __func__, *sx, + *sy); } done: @@ -250,6 +268,7 @@ done: *sy = WINDOW_MINIMUM; if (*sy > WINDOW_MAXIMUM) *sy = WINDOW_MAXIMUM; + log_debug("%s: resulting size is %ux%u", __func__, *sx, *sy); } static int @@ -289,7 +308,7 @@ recalculate_size(struct window *w, int now) current = options_get_number(w->options, "aggressive-resize"); /* Look for a suitable client and get the new size. */ - changed = clients_calculate_size(type, current, NULL, w, + changed = clients_calculate_size(type, current, NULL, NULL, w, recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel); /* diff --git a/status.c b/status.c index f9786f4b..271e1afa 100644 --- a/status.c +++ b/status.c @@ -226,6 +226,8 @@ status_line_size(struct client *c) if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) return (0); + if (s == NULL) + return (options_get_number(global_s_options, "status")); return (s->statuslines); } diff --git a/window.c b/window.c index 38c1913c..d8cff590 100644 --- a/window.c +++ b/window.c @@ -329,6 +329,8 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) window_update_activity(w); + log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy, + w->xpixel, w->ypixel); return (w); } From 7a6446ac1788bb6676087001da83efc65cf5d096 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 22 Apr 2021 09:01:22 +0100 Subject: [PATCH 0772/1006] Three changes to fix problems with xterm in VT340 mode, reported by Thomas Sattler. 1) Do not include the DECSLRM or DECFRA features for xterm; they will be added instead if secondary DA responds as VT420 (this happens already). 2) Set or reset the individual flags after terminal-overrides is applied, so the user can properly disable them. 3) Add a capability for DECFRA ("Rect"). --- tmux.1 | 4 +++ tmux.h | 1 + tty-features.c | 13 +++++-- tty-term.c | 98 ++++++++++++++++++++++++++++++++------------------ 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/tmux.1 b/tmux.1 index 6967c769..522de231 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6049,6 +6049,10 @@ Disable and enable focus reporting. These are set automatically if the .Em XT capability is present. +.It Em \&Rect +Tell +.Nm +that the terminal supports rectangle operations. .It Em \&Smol Enable the overline attribute. .It Em \&Smulx diff --git a/tmux.h b/tmux.h index 6be5b302..a67b30b9 100644 --- a/tmux.h +++ b/tmux.h @@ -452,6 +452,7 @@ enum tty_code_code { TTYC_MS, TTYC_OL, TTYC_OP, + TTYC_RECT, TTYC_REV, TTYC_RGB, TTYC_RI, diff --git a/tty-features.c b/tty-features.c index f167a2d3..b42cf74a 100644 --- a/tty-features.c +++ b/tty-features.c @@ -218,9 +218,13 @@ static const struct tty_feature tty_feature_margins = { }; /* Terminal supports DECFRA rectangle fill. */ +static const char *tty_feature_rectfill_capabilities[] = { + "Rect", + NULL +}; static const struct tty_feature tty_feature_rectfill = { "rectfill", - NULL, + tty_feature_rectfill_capabilities, TERM_DECFRA }; @@ -351,8 +355,13 @@ tty_default_features(int *feat, const char *name, u_int version) ",cstyle,extkeys,margins,sync" }, { .name = "XTerm", + /* + * xterm also supports DECSLRM and DECFRA, but they can be + * disabled so not set it here - they will be added if + * secondary DA shows VT420. + */ .features = TTY_FEATURES_BASE_MODERN_XTERM - ",ccolour,cstyle,extkeys,focus,margins,rectfill" + ",ccolour,cstyle,extkeys,focus" } }; u_int i; diff --git a/tty-term.c b/tty-term.c index 1d9b36da..4a7e7415 100644 --- a/tty-term.c +++ b/tty-term.c @@ -251,6 +251,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_MS] = { TTYCODE_STRING, "Ms" }, [TTYC_OL] = { TTYCODE_STRING, "ol" }, [TTYC_OP] = { TTYCODE_STRING, "op" }, + [TTYC_RECT] = { TTYCODE_STRING, "Rect" }, [TTYC_REV] = { TTYCODE_STRING, "rev" }, [TTYC_RGB] = { TTYCODE_FLAG, "RGB" }, [TTYC_RIN] = { TTYCODE_STRING, "rin" }, @@ -434,10 +435,11 @@ tty_term_apply_overrides(struct tty_term *term) struct options_entry *o; struct options_array_item *a; union options_value *ov; - const char *s; + const char *s, *acs; size_t offset; char *first; + /* Update capabilities from the option. */ o = options_get_only(global_options, "terminal-overrides"); a = options_array_first(o); while (a != NULL) { @@ -450,6 +452,64 @@ tty_term_apply_overrides(struct tty_term *term) tty_term_apply(term, s + offset, 0); a = options_array_next(a); } + + /* Update the RGB flag if the terminal has RGB colours. */ + if (tty_term_has(term, TTYC_SETRGBF) && + tty_term_has(term, TTYC_SETRGBB)) + term->flags |= TERM_RGBCOLOURS; + else + term->flags &= ~TERM_RGBCOLOURS; + log_debug("RGBCOLOURS flag is %d", !!(term->flags & TERM_RGBCOLOURS)); + + /* + * Set or clear the DECSLRM flag if the terminal has the margin + * capabilities. + */ + if (tty_term_has(term, TTYC_CMG) && tty_term_has(term, TTYC_CLMG)) + term->flags |= TERM_DECSLRM; + else + term->flags &= ~TERM_DECSLRM; + log_debug("DECSLRM flag is %d", !!(term->flags & TERM_DECSLRM)); + + /* + * Set or clear the DECFRA flag if the terminal has the rectangle + * capability. + */ + if (tty_term_has(term, TTYC_RECT)) + term->flags |= TERM_DECFRA; + else + term->flags &= ~TERM_DECFRA; + log_debug("DECFRA flag is %d", !!(term->flags & TERM_DECFRA)); + + /* + * Terminals without am (auto right margin) wrap at at $COLUMNS - 1 + * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). + * + * Terminals without xenl (eat newline glitch) ignore a newline beyond + * the right edge of the terminal, but tmux doesn't care about this - + * it always uses absolute only moves the cursor with a newline when + * also sending a linefeed. + * + * This is irritating, most notably because it is painful to write to + * the very bottom-right of the screen without scrolling. + * + * Flag the terminal here and apply some workarounds in other places to + * do the best possible. + */ + if (!tty_term_flag(term, TTYC_AM)) + term->flags |= TERM_NOAM; + else + term->flags &= ~TERM_NOAM; + log_debug("NOAM flag is %d", !!(term->flags & TERM_NOAM)); + + /* Generate ACS table. If none is present, use nearest ASCII. */ + memset(term->acs, 0, sizeof term->acs); + if (tty_term_has(term, TTYC_ACSC)) + acs = tty_term_string(term, TTYC_ACSC); + else + acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y~."; + for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) + term->acs[(u_char) acs[0]][0] = acs[1]; } struct tty_term * @@ -463,7 +523,7 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps, struct options_array_item *a; union options_value *ov; u_int i, j; - const char *s, *acs, *value; + const char *s, *value; size_t offset, namelen; char *first; @@ -566,40 +626,10 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps, (!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); - tty_term_apply_overrides(term); - - /* - * Terminals without am (auto right margin) wrap at at $COLUMNS - 1 - * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). - * - * Terminals without xenl (eat newline glitch) ignore a newline beyond - * the right edge of the terminal, but tmux doesn't care about this - - * it always uses absolute only moves the cursor with a newline when - * also sending a linefeed. - * - * This is irritating, most notably because it is painful to write to - * the very bottom-right of the screen without scrolling. - * - * Flag the terminal here and apply some workarounds in other places to - * do the best possible. - */ - if (!tty_term_flag(term, TTYC_AM)) - term->flags |= TERM_NOAM; - - /* Generate ACS table. If none is present, use nearest ASCII. */ - memset(term->acs, 0, sizeof term->acs); - if (tty_term_has(term, TTYC_ACSC)) - acs = tty_term_string(term, TTYC_ACSC); - else - acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y~."; - for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) - term->acs[(u_char) acs[0]][0] = acs[1]; + if (tty_apply_features(term, *feat)) + tty_term_apply_overrides(term); /* Log the capabilities. */ for (i = 0; i < tty_term_ncodes(); i++) From cb9a0627f086c1837bb339252a28622c781a96a5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 23 Apr 2021 13:41:49 +0100 Subject: [PATCH 0773/1006] Do not count client if no window. --- resize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resize.c b/resize.c index 66cb9430..38cda23d 100644 --- a/resize.c +++ b/resize.c @@ -136,7 +136,7 @@ clients_calculate_size(int type, int current, struct client *c, * For latest, count the number of clients with this window. We only * care if there is more than one. */ - if (type == WINDOW_SIZE_LATEST) + if (type == WINDOW_SIZE_LATEST && w != NULL) n = clients_with_window(w); /* Loop over the clients and work out the size. */ From 43c292fa91b22fd8422cc1c4b67e2d2ac3fab767 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 23 Apr 2021 17:18:51 +0100 Subject: [PATCH 0774/1006] Revert "Revert "Add crosscompiling fallbacks, from Hasso Tepper."" This reverts commit dda3bf896be9ce87b4066636cc7f94ab8030133a. --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 8b13854f..f6bdd3c7 100644 --- a/configure.ac +++ b/configure.ac @@ -172,6 +172,7 @@ AC_RUN_IFELSE([AC_LANG_PROGRAM( [return (reallocarray(NULL, 1, 1) == NULL);] )], AC_MSG_RESULT(yes), + [AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])], [AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])] ) AC_MSG_CHECKING([for working recallocarray]) @@ -180,6 +181,7 @@ AC_RUN_IFELSE([AC_LANG_PROGRAM( [return (recallocarray(NULL, 1, 1, 1) == NULL);] )], AC_MSG_RESULT(yes), + [AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])], [AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])] ) From ce8c56cc978df994e650fd9ba9ec9cea3d766f6a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 27 Apr 2021 08:29:54 +0100 Subject: [PATCH 0775/1006] Mention S- for Shift, GitHub issue 2683. --- tmux.1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tmux.1 b/tmux.1 index 522de231..22982b11 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2953,6 +2953,8 @@ Ctrl keys may be prefixed with .Ql C- or .Ql ^ , +Shift keys with +.Ql S- and Alt (meta) with .Ql M- . In addition, the following special key names are accepted: From cf6034da92df1dec0a2ea6230bd7a424d0d181a8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 28 Apr 2021 09:16:30 +0100 Subject: [PATCH 0776/1006] Change resize timers and flags into one timer and a queue to fix problems with vim when resized multiple times. GitHub issue 2677. --- server-client.c | 117 +++++++++++++++++++++++------------------------- tmux.h | 25 ++++++++--- window.c | 51 +++++++++------------ 3 files changed, 94 insertions(+), 99 deletions(-) diff --git a/server-client.c b/server-client.c index 0f8ad687..e74d893d 100644 --- a/server-client.c +++ b/server-client.c @@ -1447,84 +1447,79 @@ server_client_resize_timer(__unused int fd, __unused short events, void *data) evtimer_del(&wp->resize_timer); } -/* Start the resize timer. */ -static void -server_client_start_resize_timer(struct window_pane *wp) -{ - struct timeval tv = { .tv_usec = 250000 }; - - log_debug("%s: %%%u resize timer started", __func__, wp->id); - evtimer_add(&wp->resize_timer, &tv); -} - -/* Force timer event. */ -static void -server_client_force_timer(__unused int fd, __unused short events, void *data) -{ - struct window_pane *wp = data; - - log_debug("%s: %%%u force timer expired", __func__, wp->id); - evtimer_del(&wp->force_timer); - wp->flags |= PANE_RESIZENOW; -} - -/* Start the force timer. */ -static void -server_client_start_force_timer(struct window_pane *wp) -{ - struct timeval tv = { .tv_usec = 10000 }; - - log_debug("%s: %%%u force timer started", __func__, wp->id); - evtimer_add(&wp->force_timer, &tv); -} - /* Check if pane should be resized. */ static void server_client_check_pane_resize(struct window_pane *wp) { + struct window_pane_resize *r; + struct window_pane_resize *r1; + struct window_pane_resize *first; + struct window_pane_resize *last; + struct timeval tv = { .tv_usec = 250000 }; + + if (TAILQ_EMPTY(&wp->resize_queue)) + return; + if (!event_initialized(&wp->resize_timer)) evtimer_set(&wp->resize_timer, server_client_resize_timer, wp); - if (!event_initialized(&wp->force_timer)) - evtimer_set(&wp->force_timer, server_client_force_timer, wp); - - if (~wp->flags & PANE_RESIZE) + if (evtimer_pending(&wp->resize_timer, NULL)) return; + log_debug("%s: %%%u needs to be resized", __func__, wp->id); - - if (evtimer_pending(&wp->resize_timer, NULL)) { - log_debug("%s: %%%u resize timer is running", __func__, wp->id); - return; + TAILQ_FOREACH(r, &wp->resize_queue, entry) { + log_debug("queued resize: %ux%u -> %ux%u", r->osx, r->osy, + r->sx, r->sy); } - server_client_start_resize_timer(wp); - if (~wp->flags & PANE_RESIZEFORCE) { - /* - * The timer is not running and we don't need to force a - * resize, so just resize immediately. - */ - log_debug("%s: resizing %%%u now", __func__, wp->id); - window_pane_send_resize(wp, 0); - wp->flags &= ~PANE_RESIZE; + /* + * There are three cases that matter: + * + * - Only one resize. It can just be applied. + * + * - Multiple resizes and the ending size is different from the + * starting size. We can discard all resizes except the most recent. + * + * - Multiple resizes and the ending size is the same as the starting + * size. We must resize at least twice to force the application to + * redraw. So apply the first and leave the last on the queue for + * next time. + */ + first = TAILQ_FIRST(&wp->resize_queue); + last = TAILQ_LAST(&wp->resize_queue, window_pane_resizes); + if (first == last) { + /* Only one resize. */ + window_pane_send_resize(wp, first->sx, first->sy); + TAILQ_REMOVE(&wp->resize_queue, first, entry); + free(first); + } else if (last->sx != first->osx || last->sy != first->osy) { + /* Multiple resizes ending up with a different size. */ + window_pane_send_resize(wp, last->sx, last->sy); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); + } } else { /* - * The timer is not running, but we need to force a resize. If - * the force timer has expired, resize to the real size now. - * Otherwise resize to the force size and start the timer. + * Multiple resizes ending up with the same size. There will + * not be more than one to the same size in succession so we + * can just use the last-but-one on the list and leave the last + * for later. We reduce the time until the next check to avoid + * a long delay between the resizes. */ - if (wp->flags & PANE_RESIZENOW) { - log_debug("%s: resizing %%%u after forced resize", - __func__, wp->id); - window_pane_send_resize(wp, 0); - wp->flags &= ~(PANE_RESIZE|PANE_RESIZEFORCE|PANE_RESIZENOW); - } else if (!evtimer_pending(&wp->force_timer, NULL)) { - log_debug("%s: forcing resize of %%%u", __func__, - wp->id); - window_pane_send_resize(wp, 1); - server_client_start_force_timer(wp); + r = TAILQ_PREV(last, window_pane_resizes, entry); + window_pane_send_resize(wp, r->sx, r->sy); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + if (r == last) + break; + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); } + tv.tv_usec = 10000; } + evtimer_add(&wp->resize_timer, &tv); } + /* Check pane buffer size. */ static void server_client_check_pane_buffer(struct window_pane *wp) diff --git a/tmux.h b/tmux.h index a67b30b9..1e496e6f 100644 --- a/tmux.h +++ b/tmux.h @@ -919,7 +919,7 @@ struct window_mode_entry { struct screen *screen; u_int prefix; - TAILQ_ENTRY (window_mode_entry) entry; + TAILQ_ENTRY(window_mode_entry) entry; }; /* Offsets into pane buffer. */ @@ -927,6 +927,18 @@ struct window_pane_offset { size_t used; }; +/* Queued pane resize. */ +struct window_pane_resize { + u_int sx; + u_int sy; + + u_int osx; + u_int osy; + + TAILQ_ENTRY(window_pane_resize) entry; +}; +TAILQ_HEAD(window_pane_resizes, window_pane_resize); + /* Child window structure. */ struct window_pane { u_int id; @@ -951,8 +963,8 @@ struct window_pane { #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 -#define PANE_RESIZE 0x8 -#define PANE_RESIZEFORCE 0x10 +/* 0x8 unused */ +/* 0x10 unused */ #define PANE_FOCUSPUSH 0x20 #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 @@ -961,7 +973,6 @@ struct window_pane { #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 #define PANE_STYLECHANGED 0x1000 -#define PANE_RESIZENOW 0x2000 int argc; char **argv; @@ -978,8 +989,8 @@ struct window_pane { struct window_pane_offset offset; size_t base_offset; + struct window_pane_resizes resize_queue; struct event resize_timer; - struct event force_timer; struct input_ctx *ictx; @@ -997,7 +1008,7 @@ struct window_pane { struct screen status_screen; size_t status_size; - TAILQ_HEAD (, window_mode_entry) modes; + TAILQ_HEAD(, window_mode_entry) modes; char *searchstr; int searchregex; @@ -2756,7 +2767,7 @@ void window_redraw_active_switch(struct window *, struct window_pane *window_add_pane(struct window *, struct window_pane *, u_int, int); void window_resize(struct window *, u_int, u_int, int, int); -void window_pane_send_resize(struct window_pane *, int); +void window_pane_send_resize(struct window_pane *, u_int, u_int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); int window_push_zoom(struct window *, int, int); diff --git a/window.c b/window.c index d8cff590..f21a4d5f 100644 --- a/window.c +++ b/window.c @@ -425,25 +425,18 @@ window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) } void -window_pane_send_resize(struct window_pane *wp, int force) +window_pane_send_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window *w = wp->window; struct winsize ws; - u_int sy; if (wp->fd == -1) return; - if (!force) - sy = wp->sy; - else if (wp->sy <= 1) - sy = wp->sy + 1; - else - sy = wp->sy - 1; - log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, sy); + log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, sx, sy); memset(&ws, 0, sizeof ws); - ws.ws_col = wp->sx; + ws.ws_col = sx; ws.ws_row = sy; ws.ws_xpixel = w->xpixel * ws.ws_col; ws.ws_ypixel = w->ypixel * ws.ws_row; @@ -867,29 +860,19 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); - wp->argc = 0; - wp->argv = NULL; - wp->shell = NULL; - wp->cwd = NULL; - wp->fd = -1; - wp->event = NULL; wp->fg = 8; wp->bg = 8; TAILQ_INIT(&wp->modes); - wp->layout_cell = NULL; - - wp->xoff = 0; - wp->yoff = 0; + TAILQ_INIT (&wp->resize_queue); wp->sx = sx; wp->sy = sy; wp->pipe_fd = -1; - wp->pipe_event = NULL; screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; @@ -905,6 +888,9 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) static void window_pane_destroy(struct window_pane *wp) { + struct window_pane_resize *r; + struct window_pane_resize *r1; + window_pane_reset_mode_all(wp); free(wp->searchstr); @@ -929,8 +915,10 @@ window_pane_destroy(struct window_pane *wp) if (event_initialized(&wp->resize_timer)) event_del(&wp->resize_timer); - if (event_initialized(&wp->force_timer)) - event_del(&wp->force_timer); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); + } RB_REMOVE(window_pane_tree, &all_window_panes, wp); @@ -999,9 +987,18 @@ void window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window_mode_entry *wme; + struct window_pane_resize *r; if (sx == wp->sx && sy == wp->sy) return; + + r = xmalloc (sizeof *r); + r->sx = sx; + r->sy = sy; + r->osx = wp->sx; + r->osy = wp->sy; + TAILQ_INSERT_TAIL (&wp->resize_queue, r, entry); + wp->sx = sx; wp->sy = sy; @@ -1011,14 +1008,6 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); - - /* - * If the pane has already been resized, set the force flag and make - * the application resize twice to force it to redraw. - */ - if (wp->flags & PANE_RESIZE) - wp->flags |= PANE_RESIZEFORCE; - wp->flags |= PANE_RESIZE; } void From 589d3eb48fcb30163c9ac3f4b8d5e802e3f45118 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 28 Apr 2021 09:15:11 +0100 Subject: [PATCH 0777/1006] Change cursor style handling so tmux understands which sequences contain blinking and sets the flag appropriately, means that it works whether cnorm disables blinking or not. GitHub issue 2682. --- screen.c | 32 +++++++++++++-- tmux.h | 14 +++++-- tty.c | 117 +++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 120 insertions(+), 43 deletions(-) diff --git a/screen.c b/screen.c index 2b83c285..464be1cb 100644 --- a/screen.c +++ b/screen.c @@ -80,7 +80,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->titles = NULL; s->path = NULL; - s->cstyle = 0; + s->cstyle = SCREEN_CURSOR_DEFAULT; s->ccolour = xstrdup(""); s->tabs = NULL; s->sel = NULL; @@ -155,9 +155,35 @@ screen_reset_tabs(struct screen *s) void screen_set_cursor_style(struct screen *s, u_int style) { - if (style <= 6) { - s->cstyle = style; + switch (style) + { + case 0: + s->cstyle = SCREEN_CURSOR_DEFAULT; + break; + case 1: + s->cstyle = SCREEN_CURSOR_BLOCK; + s->mode |= MODE_BLINKING; + break; + case 2: + s->cstyle = SCREEN_CURSOR_BLOCK; s->mode &= ~MODE_BLINKING; + break; + case 3: + s->cstyle = SCREEN_CURSOR_UNDERLINE; + s->mode |= MODE_BLINKING; + break; + case 4: + s->cstyle = SCREEN_CURSOR_UNDERLINE; + s->mode &= ~MODE_BLINKING; + break; + case 5: + s->cstyle = SCREEN_CURSOR_BAR; + s->mode |= MODE_BLINKING; + break; + case 6: + s->cstyle = SCREEN_CURSOR_BAR; + s->mode &= ~MODE_BLINKING; + break; } } diff --git a/tmux.h b/tmux.h index 1e496e6f..b0abb523 100644 --- a/tmux.h +++ b/tmux.h @@ -795,6 +795,14 @@ struct style { enum style_default_type default_type; }; +/* Cursor style. */ +enum screen_cursor_style { + SCREEN_CURSOR_DEFAULT, + SCREEN_CURSOR_BLOCK, + SCREEN_CURSOR_UNDERLINE, + SCREEN_CURSOR_BAR +}; + /* Virtual screen. */ struct screen_sel; struct screen_titles; @@ -808,8 +816,8 @@ struct screen { u_int cx; /* cursor x */ u_int cy; /* cursor y */ - u_int cstyle; /* cursor style */ - char *ccolour; /* cursor colour string */ + enum screen_cursor_style cstyle; /* cursor style */ + char *ccolour; /* cursor colour */ u_int rupper; /* scroll region top */ u_int rlower; /* scroll region bottom */ @@ -1297,7 +1305,7 @@ struct tty { u_int cx; u_int cy; - u_int cstyle; + enum screen_cursor_style cstyle; char *ccolour; int oflag; diff --git a/tty.c b/tty.c index e8a8cbaa..367f54d5 100644 --- a/tty.c +++ b/tty.c @@ -98,7 +98,7 @@ tty_init(struct tty *tty, struct client *c) memset(tty, 0, sizeof *tty); tty->client = c; - tty->cstyle = 0; + tty->cstyle = SCREEN_CURSOR_DEFAULT; tty->ccolour = xstrdup(""); if (tcgetattr(c->fd, &tty->tio) != 0) @@ -392,10 +392,10 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); - if (tty_term_has(tty->term, TTYC_SS) && tty->cstyle != 0) { + if (tty->cstyle != SCREEN_CURSOR_DEFAULT) { if (tty_term_has(tty->term, TTYC_SE)) tty_raw(tty, tty_term_string(tty->term, TTYC_SE)); - else + else if (tty_term_has(tty->term, TTYC_SS)) tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); } if (tty->mode & MODE_BRACKETPASTE) @@ -657,11 +657,9 @@ tty_force_cursor_colour(struct tty *tty, const char *ccolour) void tty_update_mode(struct tty *tty, int mode, struct screen *s) { - struct client *c = tty->client; - int changed; - - if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) - tty_force_cursor_colour(tty, s->ccolour); + struct client *c = tty->client; + int changed; + enum screen_cursor_style cstyle = tty->cstyle; if (tty->flags & TTY_NOCURSOR) mode &= ~MODE_CURSOR; @@ -670,38 +668,83 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) if (changed != 0) log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); - /* - * The cursor blinking flag can be reset by setting the cursor style, so - * set the style first. - */ - if (s != NULL && tty->cstyle != s->cstyle) { - if (tty_term_has(tty->term, TTYC_SS)) { - if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) - tty_putcode(tty, TTYC_SE); - else - tty_putcode1(tty, TTYC_SS, s->cstyle); - } - tty->cstyle = s->cstyle; - changed |= (MODE_CURSOR|MODE_BLINKING); + if (s != NULL) { + if (strcmp(s->ccolour, tty->ccolour) != 0) + tty_force_cursor_colour(tty, s->ccolour); + cstyle = s->cstyle; } - - /* - * Cursor invisible (RM ?25) overrides cursor blinking (SM ?12 or RM - * 34), and we need to be careful not send cnorm after cvvis since it - * can undo it. - */ - if (changed & (MODE_CURSOR|MODE_BLINKING)) { - log_debug("%s: cursor %s, %sblinking", __func__, - (mode & MODE_CURSOR) ? "on" : "off", - (mode & MODE_BLINKING) ? "" : "not "); - if (~mode & MODE_CURSOR) + if (~mode & MODE_CURSOR) { + /* Cursor now off - set as invisible. */ + if (changed & MODE_CURSOR) tty_putcode(tty, TTYC_CIVIS); - else if (mode & MODE_BLINKING) { - tty_putcode(tty, TTYC_CNORM); - if (tty_term_has(tty->term, TTYC_CVVIS)) + } else if ((changed & (MODE_CURSOR|MODE_BLINKING)) || + cstyle != tty->cstyle) { + /* + * Cursor now on, blinking flag changed or style changed. Start + * by setting the cursor to normal. + */ + tty_putcode(tty, TTYC_CNORM); + switch (cstyle) { + case SCREEN_CURSOR_DEFAULT: + /* + * If the old style wasn't default, then reset it to + * default. + */ + if (tty->cstyle != SCREEN_CURSOR_DEFAULT) { + if (tty_term_has(tty->term, TTYC_SE)) + tty_putcode(tty, TTYC_SE); + else + tty_putcode1(tty, TTYC_SS, 0); + } + + /* Set the cursor as very visible if necessary. */ + if (mode & MODE_BLINKING) tty_putcode(tty, TTYC_CVVIS); - } else - tty_putcode(tty, TTYC_CNORM); + break; + case SCREEN_CURSOR_BLOCK: + /* + * Set style to either block blinking (1) or steady (2) + * if supported, otherwise just check the blinking + * flag. + */ + if (tty_term_has(tty->term, TTYC_SS)) { + if (mode & MODE_BLINKING) + tty_putcode1(tty, TTYC_SS, 1); + else + tty_putcode1(tty, TTYC_SS, 2); + } else if (mode & MODE_BLINKING) + tty_putcode(tty, TTYC_CVVIS); + break; + case SCREEN_CURSOR_UNDERLINE: + /* + * Set style to either underline blinking (3) or steady + * (4) if supported, otherwise just check the blinking + * flag. + */ + if (tty_term_has(tty->term, TTYC_SS)) { + if (mode & MODE_BLINKING) + tty_putcode1(tty, TTYC_SS, 3); + else + tty_putcode1(tty, TTYC_SS, 4); + } else if (mode & MODE_BLINKING) + tty_putcode(tty, TTYC_CVVIS); + break; + case SCREEN_CURSOR_BAR: + /* + * Set style to either bar blinking (5) or steady (6) + * if supported, otherwise just check the blinking + * flag. + */ + if (tty_term_has(tty->term, TTYC_SS)) { + if (mode & MODE_BLINKING) + tty_putcode1(tty, TTYC_SS, 5); + else + tty_putcode1(tty, TTYC_SS, 6); + } else if (mode & MODE_BLINKING) + tty_putcode(tty, TTYC_CVVIS); + break; + } + tty->cstyle = cstyle; } if ((changed & ALL_MOUSE_MODES) && From e2d01795d2501f6097bb13129995a6d0a86af419 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 28 Apr 2021 09:18:04 +0100 Subject: [PATCH 0778/1006] Move "special" keys into the Unicode PUA rather than making them high a top bit set, some compilers cannot handle enums that are larger than int. GitHub issue 2673. --- input-keys.c | 2 +- key-string.c | 6 +++--- status.c | 2 +- tmux.h | 33 ++++++++++++++++++++++++--------- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/input-keys.c b/input-keys.c index a3252855..ffd2201c 100644 --- a/input-keys.c +++ b/input-keys.c @@ -476,7 +476,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) input_key_write(__func__, bev, &ud.data[0], 1); return (0); } - if (justkey > 0x7f && justkey < KEYC_BASE) { + if (KEYC_IS_UNICODE(justkey)) { if (key & KEYC_META) input_key_write(__func__, bev, "\033", 1); utf8_to_data(justkey, &ud); diff --git a/key-string.c b/key-string.c index c24a33fc..b41eaa73 100644 --- a/key-string.c +++ b/key-string.c @@ -238,7 +238,7 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (key < KEYC_BASE && + if (KEYC_IS_UNICODE(key) && (modifiers & KEYC_CTRL) && strchr(other, key) == NULL && key != 9 && @@ -368,8 +368,8 @@ key_string_lookup_key(key_code key, int with_flags) goto out; } - /* Is this a UTF-8 key? */ - if (key > 127 && key < KEYC_BASE) { + /* Is this a Unicode key? */ + if (KEYC_IS_UNICODE(key)) { utf8_to_data(key, &ud); off = strlen(out); memcpy(out + off, ud.data, ud.size); diff --git a/status.c b/status.c index 271e1afa..f4418500 100644 --- a/status.c +++ b/status.c @@ -1300,7 +1300,7 @@ process_key: return (0); append_key: - if (key <= 0x1f || key >= KEYC_BASE) + if (key <= 0x1f || (key >= KEYC_BASE && key < KEYC_BASE_END)) return (0); if (key <= 0x7f) utf8_set(&tmp, key); diff --git a/tmux.h b/tmux.h index b0abb523..e54eb2d0 100644 --- a/tmux.h +++ b/tmux.h @@ -109,11 +109,16 @@ struct winlink; #define VISUAL_ON 1 #define VISUAL_BOTH 2 -/* Special key codes. */ -#define KEYC_NONE 0x00ff000000000ULL -#define KEYC_UNKNOWN 0x00fe000000000ULL -#define KEYC_BASE 0x0001000000000ULL -#define KEYC_USER 0x0002000000000ULL +/* No key or unknown key. */ +#define KEYC_NONE 0x000ff000000000ULL +#define KEYC_UNKNOWN 0x000fe000000000ULL + +/* + * Base for special (that is, not Unicode) keys. An enum must be at most a + * signed int, so these are based in the highest Unicode PUA. + */ +#define KEYC_BASE 0x0000000010e000ULL +#define KEYC_USER 0x0000000010f000ULL /* Key modifier bits. */ #define KEYC_META 0x00100000000000ULL @@ -136,8 +141,15 @@ struct winlink; #define KEYC_NUSER 1000 /* Is this a mouse key? */ -#define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ - ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) +#define KEYC_IS_MOUSE(key) \ + (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ + ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) + +/* Is this a Unicode key? */ +#define KEYC_IS_UNICODE(key) \ + (((key) & KEYC_MASK_KEY) > 0x7f && \ + (((key) & KEYC_MASK_KEY) < KEYC_BASE || \ + ((key) & KEYC_MASK_KEY) >= KEYC_BASE_END)) /* Multiple click timeout. */ #define KEYC_CLICK_TIMEOUT 300 @@ -159,8 +171,8 @@ struct winlink; { #s "Border", KEYC_ ## name ## _BORDER } /* - * A single key. This can be ASCII or Unicode or one of the keys starting at - * KEYC_BASE. + * A single key. This can be ASCII or Unicode or one of the keys between + * KEYC_BASE and KEYC_BASE_END. */ typedef unsigned long long key_code; @@ -253,6 +265,9 @@ enum { KEYC_KP_ENTER, KEYC_KP_ZERO, KEYC_KP_PERIOD, + + /* End of special keys. */ + KEYC_BASE_END }; /* Termcap codes. */ From 32c97a7f2f12a6689b8ffc5596484860ac8fbbec Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 28 Apr 2021 20:20:53 +0100 Subject: [PATCH 0779/1006] Ctrl keys are < 0x7f, not Unicode. --- key-string.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-string.c b/key-string.c index b41eaa73..406d26dd 100644 --- a/key-string.c +++ b/key-string.c @@ -238,7 +238,7 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (KEYC_IS_UNICODE(key) && + if (key <= 127 && (modifiers & KEYC_CTRL) && strchr(other, key) == NULL && key != 9 && From 2e7ec8c0b91dc51e6e22b2f55cc4511610044673 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 30 Apr 2021 20:14:10 +0100 Subject: [PATCH 0780/1006] Improve logging of screen mode changes. --- screen-write.c | 6 ++++++ screen.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++-- server-client.c | 5 ++++- tmux.h | 1 + tty.c | 8 ++++++-- 5 files changed, 67 insertions(+), 5 deletions(-) diff --git a/screen-write.c b/screen-write.c index 8e9ec582..e351a5e5 100644 --- a/screen-write.c +++ b/screen-write.c @@ -769,6 +769,9 @@ screen_write_mode_set(struct screen_write_ctx *ctx, int mode) struct screen *s = ctx->s; s->mode |= mode; + + if (log_get_level() != 0) + log_debug("%s: %s", __func__, screen_mode_to_string(mode)); } /* Clear a mode. */ @@ -778,6 +781,9 @@ screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) struct screen *s = ctx->s; s->mode &= ~mode; + + if (log_get_level() != 0) + log_debug("%s: %s", __func__, screen_mode_to_string(mode)); } /* Cursor up by ny. */ diff --git a/screen.c b/screen.c index 464be1cb..30b448e8 100644 --- a/screen.c +++ b/screen.c @@ -155,8 +155,8 @@ screen_reset_tabs(struct screen *s) void screen_set_cursor_style(struct screen *s, u_int style) { - switch (style) - { + log_debug("%s: new %u, was %u", __func__, style, s->cstyle); + switch (style) { case 0: s->cstyle = SCREEN_CURSOR_DEFAULT; break; @@ -652,3 +652,51 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) if (s->cy > screen_size_y(s) - 1) s->cy = screen_size_y(s) - 1; } + +/* Get mode as a string. */ +const char * +screen_mode_to_string(int mode) +{ + static char tmp[1024]; + + if (mode == 0) + return "NONE"; + if (mode == ALL_MODES) + return "ALL"; + + *tmp = '\0'; + if (mode & MODE_CURSOR) + strlcat(tmp, "CURSOR,", sizeof tmp); + if (mode & MODE_INSERT) + strlcat(tmp, "INSERT,", sizeof tmp); + if (mode & MODE_KCURSOR) + strlcat(tmp, "KCURSOR,", sizeof tmp); + if (mode & MODE_KKEYPAD) + strlcat(tmp, "KKEYPAD,", sizeof tmp); + if (mode & MODE_WRAP) + strlcat(tmp, "WRAP,", sizeof tmp); + if (mode & MODE_MOUSE_STANDARD) + strlcat(tmp, "STANDARD,", sizeof tmp); + if (mode & MODE_MOUSE_BUTTON) + strlcat(tmp, "BUTTON,", sizeof tmp); + if (mode & MODE_BLINKING) + strlcat(tmp, "BLINKING,", sizeof tmp); + if (mode & MODE_MOUSE_UTF8) + strlcat(tmp, "UTF8,", sizeof tmp); + if (mode & MODE_MOUSE_SGR) + strlcat(tmp, "SGR,", sizeof tmp); + if (mode & MODE_BRACKETPASTE) + strlcat(tmp, "BRACKETPASTE,", sizeof tmp); + if (mode & MODE_FOCUSON) + strlcat(tmp, "FOCUSON,", sizeof tmp); + if (mode & MODE_MOUSE_ALL) + strlcat(tmp, "ALL,", sizeof tmp); + if (mode & MODE_ORIGIN) + strlcat(tmp, "ORIGIN,", sizeof tmp); + if (mode & MODE_CRLF) + strlcat(tmp, "CRLF,", sizeof tmp); + if (mode & MODE_KEXTENDED) + strlcat(tmp, "KEXTENDED,", sizeof tmp); + tmp[strlen (tmp) - 1] = '\0'; + return (tmp); +} diff --git a/server-client.c b/server-client.c index e74d893d..e3a690bd 100644 --- a/server-client.c +++ b/server-client.c @@ -1691,7 +1691,10 @@ server_client_reset_state(struct client *c) s = wp->screen; if (s != NULL) mode = s->mode; - log_debug("%s: client %s mode %x", __func__, c->name, mode); + if (log_get_level() != 0) { + log_debug("%s: client %s mode %s", __func__, c->name, + screen_mode_to_string(mode)); + } /* Reset region and margin. */ tty_region_off(tty); diff --git a/tmux.h b/tmux.h index e54eb2d0..3fa7dfb3 100644 --- a/tmux.h +++ b/tmux.h @@ -2750,6 +2750,7 @@ void screen_select_cell(struct screen *, struct grid_cell *, const struct grid_cell *); void screen_alternate_on(struct screen *, struct grid_cell *, int); void screen_alternate_off(struct screen *, struct grid_cell *, int); +const char *screen_mode_to_string(int); /* window.c */ extern struct windows windows; diff --git a/tty.c b/tty.c index 367f54d5..45868fc1 100644 --- a/tty.c +++ b/tty.c @@ -665,8 +665,12 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; - if (changed != 0) - log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); + if (log_get_level() != 0 && changed != 0) { + log_debug("%s: current mode %s", c->name, + screen_mode_to_string(tty->mode)); + log_debug("%s: setting mode %s", c->name, + screen_mode_to_string(mode)); + } if (s != NULL) { if (strcmp(s->ccolour, tty->ccolour) != 0) From 40467726e3a985f0d7c064c6dcf7121f3266ab70 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 3 May 2021 06:39:17 +0100 Subject: [PATCH 0781/1006] Fix warnings, from Jan Tache in GitHub issue 2692. --- format.c | 2 +- server.c | 3 ++- tty-term.c | 2 +- window-buffer.c | 6 +++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/format.c b/format.c index 5a12502e..c7960819 100644 --- a/format.c +++ b/format.c @@ -4199,7 +4199,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, value = xstrdup("0"); } else { format_log(es, "search '%s' pane %%%u", new, wp->id); - value = format_search(fm, wp, new); + value = format_search(search, wp, new); } free(new); } else if (cmp != NULL) { diff --git a/server.c b/server.c index 0b878d9e..29bcf88a 100644 --- a/server.c +++ b/server.c @@ -163,7 +163,8 @@ server_tidy_event(__unused int fd, __unused short events, __unused void *data) malloc_trim(0); #endif - log_debug("%s: took %llu milliseconds", __func__, get_timer() - t); + log_debug("%s: took %llu milliseconds", __func__, + (unsigned long long)(get_timer() - t)); evtimer_add(&server_ev_tidy, &tv); } diff --git a/tty-term.c b/tty-term.c index 4a7e7415..add71d89 100644 --- a/tty-term.c +++ b/tty-term.c @@ -697,7 +697,7 @@ tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps, ent = &tty_term_codes[i]; switch (ent->type) { case TTYCODE_NONE: - break; + continue; case TTYCODE_STRING: s = tigetstr((char *)ent->name); if (s == NULL || s == (char *)-1) diff --git a/window-buffer.c b/window-buffer.c index 8d93558a..c1ddcc74 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -294,9 +294,9 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line) struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item = itemdata; struct format_tree *ft; - struct session *s; - struct winlink *wl; - struct window_pane *wp; + struct session *s = NULL; + struct winlink *wl = NULL; + struct window_pane *wp = NULL; struct paste_buffer *pb; char *expanded; key_code key; From c03b57465bdf850026b3a5c478a63ca62212720f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 3 May 2021 07:39:32 +0100 Subject: [PATCH 0782/1006] Add different command historys for different types of prompts ("command", "search" etc). From Anindya Mukherjee. --- Makefile.am | 1 + cmd-command-prompt.c | 39 ++++---- cmd-confirm-before.c | 2 +- cmd-show-prompt-history.c | 108 ++++++++++++++++++++++ cmd.c | 4 + key-bindings.c | 12 +-- mode-tree.c | 4 +- options-table.c | 9 ++ status.c | 184 +++++++++++++++++++++++++++----------- tmux.1 | 47 ++++++++-- tmux.h | 22 +++-- window-customize.c | 14 +-- window-tree.c | 6 +- 13 files changed, 356 insertions(+), 96 deletions(-) create mode 100644 cmd-show-prompt-history.c diff --git a/Makefile.am b/Makefile.am index 3e15204f..9bb5b89e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -125,6 +125,7 @@ dist_tmux_SOURCES = \ cmd-show-environment.c \ cmd-show-messages.c \ cmd-show-options.c \ + cmd-show-prompt-history.c \ cmd-source-file.c \ cmd-split-window.c \ cmd-swap-pane.c \ diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index c82235c5..c2a2dec7 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,25 +40,26 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1kiI:Np:Tt:W", 0, 1 }, - .usage = "[-1kiNTW] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " - "[template]", + .args = { "1kiI:Np:t:T:", 0, 1 }, + .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE + " [-T type] [template]", .flags = CMD_CLIENT_TFLAG, .exec = cmd_command_prompt_exec }; struct cmd_command_prompt_cdata { - int flags; + int flags; + enum prompt_type prompt_type; - char *inputs; - char *next_input; + char *inputs; + char *next_input; - char *prompts; - char *next_prompt; + char *prompts; + char *next_prompt; - char *template; - int idx; + char *template; + int idx; }; static enum cmd_retval @@ -67,7 +68,7 @@ cmd_command_prompt_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_find_state *target = cmdq_get_target(item); - const char *inputs, *prompts; + const char *inputs, *prompts, *type; struct cmd_command_prompt_cdata *cdata; char *prompt, *ptr, *input = NULL; size_t n; @@ -114,6 +115,16 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) input = strsep(&cdata->next_input, ","); } + /* Get prompt type. */ + if ((type = args_get(args, 'T')) != NULL) { + cdata->prompt_type = status_prompt_type(type); + if (cdata->prompt_type == PROMPT_TYPE_INVALID) { + cmdq_error(item, "unknown type: %s", type); + return (CMD_RETURN_ERROR); + } + } else + cdata->prompt_type = PROMPT_TYPE_COMMAND; + if (args_has(args, '1')) cdata->flags |= PROMPT_SINGLE; else if (args_has(args, 'N')) @@ -122,13 +133,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_INCREMENTAL; else if (args_has(args, 'k')) cdata->flags |= PROMPT_KEY; - else if (args_has(args, 'W')) - cdata->flags |= PROMPT_WINDOW; - else if (args_has(args, 'T')) - cdata->flags |= PROMPT_TARGET; status_prompt_set(tc, target, prompt, input, cmd_command_prompt_callback, cmd_command_prompt_free, cdata, - cdata->flags); + cdata->flags, cdata->prompt_type); free(prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 0c562836..0b490b17 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -74,7 +74,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) status_prompt_set(tc, target, new_prompt, NULL, cmd_confirm_before_callback, cmd_confirm_before_free, cdata, - PROMPT_SINGLE); + PROMPT_SINGLE, PROMPT_TYPE_COMMAND); free(new_prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-show-prompt-history.c b/cmd-show-prompt-history.c new file mode 100644 index 00000000..5711755e --- /dev/null +++ b/cmd-show-prompt-history.c @@ -0,0 +1,108 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 Anindya Mukherjee + * + * 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 "tmux.h" + +#include + +/* + * Show or clear prompt history. + */ + +static enum cmd_retval cmd_show_prompt_history_exec(struct cmd *, + struct cmdq_item *); + +const struct cmd_entry cmd_show_prompt_history_entry = { + .name = "show-prompt-history", + .alias = "showphist", + + .args = { "T:", 0, 0 }, + .usage = "[-T type]", + + .flags = CMD_AFTERHOOK, + .exec = cmd_show_prompt_history_exec +}; + +const struct cmd_entry cmd_clear_prompt_history_entry = { + .name = "clear-prompt-history", + .alias = "clearphist", + + .args = { "T:", 0, 0 }, + .usage = "[-T type]", + + .flags = CMD_AFTERHOOK, + .exec = cmd_show_prompt_history_exec +}; + +static enum cmd_retval +cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + const char *typestr = args_get(args, 'T'); + enum prompt_type type; + u_int tidx, hidx; + + if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) { + if (typestr == NULL) { + for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) { + free(status_prompt_hlist[tidx]); + status_prompt_hlist[tidx] = NULL; + status_prompt_hsize[tidx] = 0; + } + } else { + type = status_prompt_type(typestr); + if (type == PROMPT_TYPE_INVALID) { + cmdq_error(item, "invalid type: %s", typestr); + return (CMD_RETURN_ERROR); + } + free(status_prompt_hlist[type]); + status_prompt_hlist[type] = NULL; + status_prompt_hsize[type] = 0; + } + + return (CMD_RETURN_NORMAL); + } + + if (typestr == NULL) { + for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) { + cmdq_print(item, "History for %s:\n", + status_prompt_type_string(tidx)); + for (hidx = 0; hidx < status_prompt_hsize[tidx]; + hidx++) { + cmdq_print(item, "%d: %s", hidx + 1, + status_prompt_hlist[tidx][hidx]); + } + cmdq_print(item, ""); + } + } else { + type = status_prompt_type(typestr); + if (type == PROMPT_TYPE_INVALID) { + cmdq_error(item, "invalid type: %s", typestr); + return (CMD_RETURN_ERROR); + } + cmdq_print(item, "History for %s:\n", + status_prompt_type_string(type)); + for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++) { + cmdq_print(item, "%d: %s", hidx + 1, + status_prompt_hlist[type][hidx]); + } + cmdq_print(item, ""); + } + + return (CMD_RETURN_NORMAL); +} diff --git a/cmd.c b/cmd.c index 775bdb73..f35186f4 100644 --- a/cmd.c +++ b/cmd.c @@ -35,6 +35,7 @@ extern const struct cmd_entry cmd_choose_buffer_entry; extern const struct cmd_entry cmd_choose_client_entry; extern const struct cmd_entry cmd_choose_tree_entry; extern const struct cmd_entry cmd_clear_history_entry; +extern const struct cmd_entry cmd_clear_prompt_history_entry; extern const struct cmd_entry cmd_clock_mode_entry; extern const struct cmd_entry cmd_command_prompt_entry; extern const struct cmd_entry cmd_confirm_before_entry; @@ -104,6 +105,7 @@ extern const struct cmd_entry cmd_show_environment_entry; extern const struct cmd_entry cmd_show_hooks_entry; extern const struct cmd_entry cmd_show_messages_entry; extern const struct cmd_entry cmd_show_options_entry; +extern const struct cmd_entry cmd_show_prompt_history_entry; extern const struct cmd_entry cmd_show_window_options_entry; extern const struct cmd_entry cmd_source_file_entry; extern const struct cmd_entry cmd_split_window_entry; @@ -126,6 +128,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_choose_client_entry, &cmd_choose_tree_entry, &cmd_clear_history_entry, + &cmd_clear_prompt_history_entry, &cmd_clock_mode_entry, &cmd_command_prompt_entry, &cmd_confirm_before_entry, @@ -194,6 +197,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_show_hooks_entry, &cmd_show_messages_entry, &cmd_show_options_entry, + &cmd_show_prompt_history_entry, &cmd_show_window_options_entry, &cmd_source_file_entry, &cmd_split_window_entry, diff --git a/key-bindings.c b/key-bindings.c index 467c7f93..b380f2cd 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -350,12 +350,12 @@ key_bindings_init(void) "bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"", "bind -N 'Split window horizontally' % split-window -h", "bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window", - "bind -N 'Prompt for window index to select' \"'\" command-prompt -Wpindex \"select-window -t ':%%'\"", + "bind -N 'Prompt for window index to select' \"'\" command-prompt -T window-target -pindex \"select-window -t ':%%'\"", "bind -N 'Switch to previous client' ( switch-client -p", "bind -N 'Switch to next client' ) switch-client -n", "bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"", "bind -N 'Delete the most recent paste buffer' - delete-buffer", - "bind -N 'Move the current window' . command-prompt -T \"move-window -t '%%'\"", + "bind -N 'Move the current window' . command-prompt -T target \"move-window -t '%%'\"", "bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'", "bind -N 'Select window 0' 0 select-window -t:=0", "bind -N 'Select window 1' 1 select-window -t:=1", @@ -479,8 +479,8 @@ key_bindings_init(void) "bind -Tcopy-mode C-k send -X copy-end-of-line", "bind -Tcopy-mode C-n send -X cursor-down", "bind -Tcopy-mode C-p send -X cursor-up", - "bind -Tcopy-mode C-r command-prompt -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", - "bind -Tcopy-mode C-s command-prompt -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'", + "bind -Tcopy-mode C-r command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", + "bind -Tcopy-mode C-s command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'", "bind -Tcopy-mode C-v send -X page-down", "bind -Tcopy-mode C-w send -X copy-pipe-and-cancel", "bind -Tcopy-mode Escape send -X cancel", @@ -559,7 +559,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi Space send -X begin-selection", "bind -Tcopy-mode-vi '$' send -X end-of-line", "bind -Tcopy-mode-vi , send -X jump-reverse", - "bind -Tcopy-mode-vi / command-prompt -p'(search down)' 'send -X search-forward \"%%%\"'", + "bind -Tcopy-mode-vi / command-prompt -T search -p'(search down)' 'send -X search-forward \"%%%\"'", "bind -Tcopy-mode-vi 0 send -X start-of-line", "bind -Tcopy-mode-vi 1 command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 2 command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"'", @@ -572,7 +572,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi 9 command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"'", "bind -Tcopy-mode-vi : command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode-vi \\; send -X jump-again", - "bind -Tcopy-mode-vi ? command-prompt -p'(search up)' 'send -X search-backward \"%%%\"'", + "bind -Tcopy-mode-vi ? command-prompt -T search -p'(search up)' 'send -X search-backward \"%%%\"'", "bind -Tcopy-mode-vi A send -X append-selection-and-cancel", "bind -Tcopy-mode-vi B send -X previous-space", "bind -Tcopy-mode-vi D send -X copy-end-of-line", diff --git a/mode-tree.c b/mode-tree.c index ca7f33a4..807c1dcb 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1168,7 +1168,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mtd->references++; status_prompt_set(c, NULL, "(search) ", "", mode_tree_search_callback, mode_tree_search_free, mtd, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH); break; case 'n': mode_tree_search_set(mtd); @@ -1177,7 +1177,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mtd->references++; status_prompt_set(c, NULL, "(filter) ", mtd->filter, mode_tree_filter_callback, mode_tree_filter_free, mtd, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH); break; case 'v': mtd->preview = !mtd->preview; diff --git a/options-table.c b/options-table.c index 8264d1b7..91b4d747 100644 --- a/options-table.c +++ b/options-table.c @@ -301,6 +301,15 @@ const struct options_table_entry options_table[] = { .text = "Maximum number of server messages to keep." }, + { .name = "prompt-history-limit", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 100, + .text = "Maximum number of commands to keep in history." + }, + { .name = "set-clipboard", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SERVER, diff --git a/status.c b/status.c index f4418500..7bcd2f38 100644 --- a/status.c +++ b/status.c @@ -33,9 +33,9 @@ static void status_message_callback(int, short, void *); static void status_timer_callback(int, short, void *); static char *status_prompt_find_history_file(void); -static const char *status_prompt_up_history(u_int *); -static const char *status_prompt_down_history(u_int *); -static void status_prompt_add_history(const char *); +static const char *status_prompt_up_history(u_int *, u_int); +static const char *status_prompt_down_history(u_int *, u_int); +static void status_prompt_add_history(const char *, u_int); static char *status_prompt_complete(struct client *, const char *, u_int); static char *status_prompt_complete_window_menu(struct client *, @@ -49,10 +49,16 @@ struct status_prompt_menu { char flag; }; +static const char *prompt_type_strings[] = { + "command", + "search", + "target", + "window-target" +}; + /* Status prompt history. */ -#define PROMPT_HISTORY 100 -static char **status_prompt_hlist; -static u_int status_prompt_hsize; +char **status_prompt_hlist[PROMPT_NTYPES]; +u_int status_prompt_hsize[PROMPT_NTYPES]; /* Find the history file to load/save from/to. */ static char * @@ -75,6 +81,28 @@ status_prompt_find_history_file(void) return (path); } +/* Add loaded history item to the appropriate list. */ +static void +status_prompt_add_typed_history(char *line) +{ + char *typestr; + enum prompt_type type = PROMPT_TYPE_INVALID; + + typestr = strsep(&line, ":"); + if (line != NULL) + type = status_prompt_type(typestr); + if (type == PROMPT_TYPE_INVALID) { + /* + * Invalid types are not expected, but this provides backward + * compatibility with old history files. + */ + if (line != NULL) + *(--line) = ':'; + status_prompt_add_history(typestr, PROMPT_TYPE_COMMAND); + } else + status_prompt_add_history(line, type); +} + /* Load status prompt history from file. */ void status_prompt_load_history(void) @@ -102,12 +130,12 @@ status_prompt_load_history(void) if (length > 0) { if (line[length - 1] == '\n') { line[length - 1] = '\0'; - status_prompt_add_history(line); + status_prompt_add_typed_history(line); } else { tmp = xmalloc(length + 1); memcpy(tmp, line, length); tmp[length] = '\0'; - status_prompt_add_history(tmp); + status_prompt_add_typed_history(tmp); free(tmp); } } @@ -120,7 +148,7 @@ void status_prompt_save_history(void) { FILE *f; - u_int i; + u_int i, type; char *history_file; if ((history_file = status_prompt_find_history_file()) == NULL) @@ -135,9 +163,13 @@ status_prompt_save_history(void) } free(history_file); - for (i = 0; i < status_prompt_hsize; i++) { - fputs(status_prompt_hlist[i], f); - fputc('\n', f); + for (type = 0; type < PROMPT_NTYPES; type++) { + for (i = 0; i < status_prompt_hsize[type]; i++) { + fputs(prompt_type_strings[type], f); + fputc(':', f); + fputs(status_prompt_hlist[type][i], f); + fputc('\n', f); + } } fclose(f); @@ -545,7 +577,7 @@ status_message_redraw(struct client *c) void status_prompt_set(struct client *c, struct cmd_find_state *fs, const char *msg, const char *input, prompt_input_cb inputcb, - prompt_free_cb freecb, void *data, int flags) + prompt_free_cb freecb, void *data, int flags, enum prompt_type prompt_type) { struct format_tree *ft; char *tmp; @@ -581,9 +613,10 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, c->prompt_freecb = freecb; c->prompt_data = data; - c->prompt_hindex = 0; + memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); c->prompt_flags = flags; + c->prompt_type = prompt_type; c->prompt_mode = PROMPT_ENTRY; if (~flags & PROMPT_INCREMENTAL) @@ -644,7 +677,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input) c->prompt_buffer = utf8_fromcstr(tmp); c->prompt_index = utf8_strlen(c->prompt_buffer); - c->prompt_hindex = 0; + memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); c->flags |= CLIENT_REDRAWSTATUS; @@ -768,7 +801,7 @@ status_prompt_space(const struct utf8_data *ud) } /* - * Translate key from emacs to vi. Return 0 to drop key, 1 to process the key + * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key * as an emacs key; return 2 to append to the buffer. */ static int @@ -1222,7 +1255,8 @@ process_key: goto changed; case KEYC_UP: case '\020': /* C-p */ - histstr = status_prompt_up_history(&c->prompt_hindex); + histstr = status_prompt_up_history(c->prompt_hindex, + c->prompt_type); if (histstr == NULL) break; free(c->prompt_buffer); @@ -1231,7 +1265,8 @@ process_key: goto changed; case KEYC_DOWN: case '\016': /* C-n */ - histstr = status_prompt_down_history(&c->prompt_hindex); + histstr = status_prompt_down_history(c->prompt_hindex, + c->prompt_type); if (histstr == NULL) break; free(c->prompt_buffer); @@ -1259,7 +1294,7 @@ process_key: case '\n': s = utf8_tocstr(c->prompt_buffer); if (*s != '\0') - status_prompt_add_history(s); + status_prompt_add_history(s, c->prompt_type); if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) status_prompt_clear(c); free(s); @@ -1348,54 +1383,78 @@ changed: /* Get previous line from the history. */ static const char * -status_prompt_up_history(u_int *idx) +status_prompt_up_history(u_int *idx, u_int type) { /* * History runs from 0 to size - 1. Index is from 0 to size. Zero is * empty. */ - if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) + if (status_prompt_hsize[type] == 0 || + idx[type] == status_prompt_hsize[type]) return (NULL); - (*idx)++; - return (status_prompt_hlist[status_prompt_hsize - *idx]); + idx[type]++; + return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]); } /* Get next line from the history. */ static const char * -status_prompt_down_history(u_int *idx) +status_prompt_down_history(u_int *idx, u_int type) { - if (status_prompt_hsize == 0 || *idx == 0) + if (status_prompt_hsize[type] == 0 || idx[type] == 0) return (""); - (*idx)--; - if (*idx == 0) + idx[type]--; + if (idx[type] == 0) return (""); - return (status_prompt_hlist[status_prompt_hsize - *idx]); + return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]); } /* Add line to the history. */ static void -status_prompt_add_history(const char *line) +status_prompt_add_history(const char *line, u_int type) { - size_t size; + u_int i, oldsize, newsize, freecount, hlimit, new = 1; + size_t movesize; - if (status_prompt_hsize > 0 && - strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) - return; + oldsize = status_prompt_hsize[type]; + if (oldsize > 0 && + strcmp(status_prompt_hlist[type][oldsize - 1], line) == 0) + new = 0; - if (status_prompt_hsize == PROMPT_HISTORY) { - free(status_prompt_hlist[0]); - - size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; - memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); - - status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); - return; + hlimit = options_get_number(global_options, "prompt-history-limit"); + if (hlimit > oldsize) { + if (new == 0) + return; + newsize = oldsize + new; + } else { + newsize = hlimit; + freecount = oldsize + new - newsize; + if (freecount > oldsize) + freecount = oldsize; + if (freecount == 0) + return; + for (i = 0; i < freecount; i++) + free(status_prompt_hlist[type][i]); + movesize = (oldsize - freecount) * + sizeof *status_prompt_hlist[type]; + if (movesize > 0) { + memmove(&status_prompt_hlist[type][0], + &status_prompt_hlist[type][freecount], movesize); + } } - status_prompt_hlist = xreallocarray(status_prompt_hlist, - status_prompt_hsize + 1, sizeof *status_prompt_hlist); - status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); + if (newsize == 0) { + free(status_prompt_hlist[type]); + status_prompt_hlist[type] = NULL; + } else if (newsize != oldsize) { + status_prompt_hlist[type] = + xreallocarray(status_prompt_hlist[type], newsize, + sizeof *status_prompt_hlist[type]); + } + + if (new == 1 && newsize > 0) + status_prompt_hlist[type][newsize - 1] = xstrdup(line); + status_prompt_hsize[type] = newsize; } /* Build completion list. */ @@ -1501,7 +1560,7 @@ status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key, s = xstrdup(spm->list[idx]); else xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); - if (c->prompt_flags & PROMPT_WINDOW) { + if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { free(c->prompt_buffer); c->prompt_buffer = utf8_fromcstr(s); c->prompt_index = utf8_strlen(c->prompt_buffer); @@ -1609,7 +1668,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, } list = xreallocarray(list, size + 1, sizeof *list); - if (c->prompt_flags & PROMPT_WINDOW) { + if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); xasprintf(&list[size++], "%d", wl->idx); } else { @@ -1717,10 +1776,12 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) u_int size = 0, i; if (*word == '\0' && - ((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0)) + c->prompt_type != PROMPT_TYPE_TARGET && + c->prompt_type != PROMPT_TYPE_WINDOW_TARGET) return (NULL); - if (((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0) && + if (c->prompt_type != PROMPT_TYPE_TARGET && + c->prompt_type != PROMPT_TYPE_WINDOW_TARGET && strncmp(word, "-t", 2) != 0 && strncmp(word, "-s", 2) != 0) { list = status_prompt_complete_list(&size, word, offset == 0); @@ -1733,7 +1794,8 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) goto found; } - if (c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) { + if (c->prompt_type == PROMPT_TYPE_TARGET || + c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { s = word; flag = '\0'; } else { @@ -1743,7 +1805,7 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) } /* If this is a window completion, open the window menu. */ - if (c->prompt_flags & PROMPT_WINDOW) { + if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { out = status_prompt_complete_window_menu(c, c->session, s, offset, '\0'); goto found; @@ -1793,3 +1855,25 @@ found: } return (out); } + +/* Return the type of the prompt as an enum. */ +enum prompt_type +status_prompt_type(const char *type) +{ + u_int i; + + for (i = 0; i < PROMPT_NTYPES; i++) { + if (strcmp(type, status_prompt_type_string(i)) == 0) + return (i); + } + return (PROMPT_TYPE_INVALID); +} + +/* Accessor for prompt_type_strings. */ +const char * +status_prompt_type_string(u_int type) +{ + if (type >= PROMPT_NTYPES) + return ("invalid"); + return (prompt_type_strings[type]); +} diff --git a/tmux.1 b/tmux.1 index 22982b11..30a6e75b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3453,7 +3453,9 @@ will write command prompt history on exit and load it from on start. .It Ic message-limit Ar number Set the number of error or information messages to save in the message log for each client. -The default is 100. +.It Ic prompt-history-limit Ar number +Set the number of history items to save in the history file for each type of +command prompt. .It Xo Ic set-clipboard .Op Ic on | external | off .Xc @@ -5376,11 +5378,25 @@ session option. .Pp Commands related to the status line are as follows: .Bl -tag -width Ds +.It Xo Ic clear-prompt-history +.Op Fl T Ar prompt-type +.Xc +.D1 (alias: Ic clrphist) +Clear status prompt history for prompt type +.Ar prompt-type . +If +.Fl T +is omitted, then clear history for all types. +See +.Ic command-prompt +for possible values for +.Ar prompt-type . .It Xo Ic command-prompt -.Op Fl 1ikNTW +.Op Fl 1ikN .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client +.Op Fl T Ar prompt-type .Op Ar template .Xc Open the command prompt in a client. @@ -5436,14 +5452,20 @@ makes the prompt only accept numeric key presses. .Fl i executes the command every time the prompt input changes instead of when the user exits the command prompt. +.Pp .Fl T tells .Nm -that the prompt is for a target which affects what completions are offered when +the prompt type. +This affects what completions are offered when .Em Tab -is pressed; -.Fl W -is similar but indicates the prompt is for a window. +is pressed. +Available types are: +.Ql command , +.Ql search , +.Ql target +and +.Ql window-target . .Pp The following keys have a special meaning in the command prompt, depending on the value of the @@ -5665,6 +5687,19 @@ If omitted, half of the terminal size is used. The .Fl C flag closes any popup on the client. +.It Xo Ic show-prompt-history +.Op Fl T Ar prompt-type +.Xc +.D1 (alias: Ic showphist) +Display status prompt history for prompt type +.Ar prompt-type . +If +.Fl T +is omitted, then show history for all types. +See +.Ic command-prompt +for possible values for +.Ar prompt-type . .El .Sh BUFFERS .Nm diff --git a/tmux.h b/tmux.h index 3fa7dfb3..e7cd205a 100644 --- a/tmux.h +++ b/tmux.h @@ -1572,6 +1572,16 @@ struct status_line { struct status_line_entry entries[STATUS_LINES_LIMIT]; }; +/* Prompt type. */ +#define PROMPT_NTYPES 4 +enum prompt_type { + PROMPT_TYPE_COMMAND, + PROMPT_TYPE_SEARCH, + PROMPT_TYPE_TARGET, + PROMPT_TYPE_WINDOW_TARGET, + PROMPT_TYPE_INVALID = 0xff +}; + /* File in client. */ typedef void (*client_file_cb) (struct client *, const char *, int, int, struct evbuffer *, void *); @@ -1735,18 +1745,16 @@ struct client { prompt_input_cb prompt_inputcb; prompt_free_cb prompt_freecb; void *prompt_data; - u_int prompt_hindex; + u_int prompt_hindex[PROMPT_NTYPES]; enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; struct utf8_data *prompt_saved; - #define PROMPT_SINGLE 0x1 #define PROMPT_NUMERIC 0x2 #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 #define PROMPT_KEY 0x10 -#define PROMPT_WINDOW 0x20 -#define PROMPT_TARGET 0x40 int prompt_flags; + enum prompt_type prompt_type; struct session *session; struct session *last_session; @@ -2518,6 +2526,8 @@ void server_check_unattached(void); void server_unzoom_window(struct window *); /* status.c */ +extern char **status_prompt_hlist[]; +extern u_int status_prompt_hsize[]; void status_timer_start(struct client *); void status_timer_start_all(void); void status_update_cache(struct session *); @@ -2532,13 +2542,15 @@ void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, struct cmd_find_state *, const char *, const char *, prompt_input_cb, prompt_free_cb, - void *, int); + void *, int, enum prompt_type); void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); int status_prompt_key(struct client *, key_code); void status_prompt_update(struct client *, const char *, const char *); void status_prompt_load_history(void); void status_prompt_save_history(void); +const char *status_prompt_type_string(u_int); +enum prompt_type status_prompt_type(const char *type); /* resize.c */ void resize_window(struct window *, u_int, u_int, int, int); diff --git a/window-customize.c b/window-customize.c index 34a13f73..782542f7 100644 --- a/window-customize.c +++ b/window-customize.c @@ -1123,7 +1123,7 @@ window_customize_set_option(struct client *c, status_prompt_set(c, NULL, prompt, value, window_customize_set_option_callback, window_customize_free_item_callback, new_item, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); free(value); @@ -1264,7 +1264,7 @@ window_customize_set_key(struct client *c, status_prompt_set(c, NULL, prompt, value, window_customize_set_command_callback, window_customize_free_item_callback, new_item, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); free(value); } else if (strcmp(s, "Note") == 0) { @@ -1281,7 +1281,7 @@ window_customize_set_key(struct client *c, (bd->note == NULL ? "" : bd->note), window_customize_set_note_callback, window_customize_free_item_callback, new_item, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); } } @@ -1458,7 +1458,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_current_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'D': @@ -1471,7 +1471,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_tagged_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'u': @@ -1487,7 +1487,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_current_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'U': @@ -1500,7 +1500,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_tagged_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'H': diff --git a/window-tree.c b/window-tree.c index 33af0413..1cfdcb70 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1296,7 +1296,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, data->references++; status_prompt_set(c, NULL, prompt, "", window_tree_kill_current_callback, window_tree_command_free, - data, PROMPT_SINGLE|PROMPT_NOFORMAT); + data, PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'X': @@ -1307,7 +1307,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, data->references++; status_prompt_set(c, NULL, prompt, "", window_tree_kill_tagged_callback, window_tree_command_free, - data, PROMPT_SINGLE|PROMPT_NOFORMAT); + data, PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case ':': @@ -1319,7 +1319,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, data->references++; status_prompt_set(c, NULL, prompt, "", window_tree_command_callback, window_tree_command_free, - data, PROMPT_NOFORMAT); + data, PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case '\r': From 8da45730880467ea5defe010c58ac1ec7ca74c4e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 3 May 2021 10:49:51 +0100 Subject: [PATCH 0783/1006] Fire check callback after cleaning up event so it does not get stuck, from Jeongho Jang in GitHub issue 2695. --- file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/file.c b/file.c index baa2bd58..974c8a37 100644 --- a/file.c +++ b/file.c @@ -503,14 +503,14 @@ file_write_error_callback(__unused struct bufferevent *bev, __unused short what, log_debug("write error file %d", cf->stream); - if (cf->cb != NULL) - cf->cb(NULL, NULL, 0, -1, NULL, cf->data); - bufferevent_free(cf->event); cf->event = NULL; close(cf->fd); cf->fd = -1; + + if (cf->cb != NULL) + cf->cb(NULL, NULL, 0, -1, NULL, cf->data); } /* Client file write callback. */ From f2951bd4a560692048e68bf29a5b0a288346a7d4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 5 May 2021 07:23:23 +0100 Subject: [PATCH 0784/1006] Remove old shift function keys which interfere with xterm keys now. GitHub issue 2696. --- input-keys.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/input-keys.c b/input-keys.c index ffd2201c..be83600e 100644 --- a/input-keys.c +++ b/input-keys.c @@ -94,30 +94,6 @@ static struct input_key_entry input_key_defaults[] = { { .key = KEYC_F12, .data = "\033[24~" }, - { .key = KEYC_F1|KEYC_SHIFT, - .data = "\033[25~" - }, - { .key = KEYC_F2|KEYC_SHIFT, - .data = "\033[26~" - }, - { .key = KEYC_F3|KEYC_SHIFT, - .data = "\033[28~" - }, - { .key = KEYC_F4|KEYC_SHIFT, - .data = "\033[29~" - }, - { .key = KEYC_F5|KEYC_SHIFT, - .data = "\033[31~" - }, - { .key = KEYC_F6|KEYC_SHIFT, - .data = "\033[32~" - }, - { .key = KEYC_F7|KEYC_SHIFT, - .data = "\033[33~" - }, - { .key = KEYC_F8|KEYC_SHIFT, - .data = "\033[34~" - }, { .key = KEYC_IC, .data = "\033[2~" }, From d00d6820691947b00a1049a435e702e32453bcbe Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 10 May 2021 07:42:35 +0100 Subject: [PATCH 0785/1006] Looks like evports on SunOS are broken also, disable them. GitHub issue 2702. --- osdep-sunos.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osdep-sunos.c b/osdep-sunos.c index 138e6bad..c3563ca4 100644 --- a/osdep-sunos.c +++ b/osdep-sunos.c @@ -96,5 +96,17 @@ osdep_get_cwd(int fd) struct event_base * osdep_event_init(void) { - return (event_init()); + struct event_base *base; + + /* + * On Illumos, evports don't seem to work properly. It is not clear if + * this a problem in libevent, with the way tmux uses file descriptors, + * or with some types of file descriptor. But using poll instead is + * fine. + */ + setenv("EVENT_NOEVPORT", "1", 1); + + base = event_init(); + unsetenv("EVENT_NOEVPORT"); + return (base); } From bde3829131bd851b8210fd4a35871b01286da1eb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 10 May 2021 07:51:30 +0100 Subject: [PATCH 0786/1006] Do not expand the file given with -f so it can contain :s. --- tmux.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tmux.c b/tmux.c index 3a49c803..29921781 100644 --- a/tmux.c +++ b/tmux.c @@ -328,7 +328,7 @@ main(int argc, char **argv) char *path = NULL, *label = NULL; char *cause, **var; const char *s, *cwd; - int opt, keys, feat = 0; + int opt, keys, feat = 0, fflag = 0; uint64_t flags = 0; const struct options_table_entry *oe; u_int i; @@ -373,10 +373,15 @@ main(int argc, char **argv) flags |= CLIENT_CONTROL; break; case 'f': - for (i = 0; i < cfg_nfiles; i++) - free(cfg_files[i]); - free(cfg_files); - expand_paths(optarg, &cfg_files, &cfg_nfiles, 0); + if (!fflag) { + fflag = 1; + for (i = 0; i < cfg_nfiles; i++) + free(cfg_files[i]); + cfg_nfiles = 0; + } + cfg_files = xreallocarray(cfg_files, cfg_nfiles + 1, + sizeof *cfg_files); + cfg_files[cfg_nfiles++] = xstrdup(optarg); cfg_quiet = 0; break; case 'V': From 4ca6b42c241c9bb6769417ca1dc4fada521f3db8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 10 May 2021 07:52:30 +0100 Subject: [PATCH 0787/1006] Add -F for command-prompt and use it to fix "Rename" on the window menu, GitHub issue 2699. --- cmd-command-prompt.c | 10 ++++++---- key-bindings.c | 2 +- tmux.1 | 7 ++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index c2a2dec7..a955ac32 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,8 +40,8 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1kiI:Np:t:T:", 0, 1 }, - .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE + .args = { "1FkiI:Np:t:T:", 0, 1 }, + .usage = "[-1FkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [-T type] [template]", .flags = CMD_CLIENT_TFLAG, @@ -59,7 +59,7 @@ struct cmd_command_prompt_cdata { char *next_prompt; char *template; - int idx; + int idx; }; static enum cmd_retval @@ -87,7 +87,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->template = NULL; cdata->idx = 1; - if (args->argc != 0) + if (args->argc != 0 && args_has(args, 'F')) + cdata->template = format_single_from_target(item, args->argv[0]); + else if (args->argc != 0) cdata->template = xstrdup(args->argv[0]); else cdata->template = xstrdup("%1"); diff --git a/key-bindings.c b/key-bindings.c index b380f2cd..de5c20ee 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -41,7 +41,7 @@ " 'Kill' 'X' {kill-window}" \ " 'Respawn' 'R' {respawn-window -k}" \ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ - " 'Rename' 'n' {command-prompt -I \"#W\" \"rename-window -- '%%'\"}" \ + " 'Rename' 'n' {command-prompt -FI \"#W\" \"rename-window -t#{window_id} -- '%%'\"}" \ " ''" \ " 'New After' 'w' {new-window -a}" \ " 'New At End' 'W' {new-window}" diff --git a/tmux.1 b/tmux.1 index 30a6e75b..e02e5728 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5392,7 +5392,7 @@ See for possible values for .Ar prompt-type . .It Xo Ic command-prompt -.Op Fl 1ikN +.Op Fl 1FikN .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client @@ -5407,6 +5407,11 @@ to execute commands interactively. If .Ar template is specified, it is used as the command. +With +.Fl F , +.Ar template +is expanded as a format. +.Pp If present, .Fl I is a comma-separated list of the initial text for each prompt. From f03b27c72be01ad1934079d1b6907f94efff9dea Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 12 May 2021 07:08:58 +0100 Subject: [PATCH 0788/1006] Do not use NULL client when source-file finishes, GitHub issue 2707. --- cmd-source-file.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd-source-file.c b/cmd-source-file.c index 1da59193..c1eeba58 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -67,7 +67,9 @@ cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata) struct cmdq_item *new_item; if (cfg_finished) { - if (cdata->retval == CMD_RETURN_ERROR && c->session == NULL) + if (cdata->retval == CMD_RETURN_ERROR && + c != NULL && + c->session == NULL) c->retval = 1; new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL); cmdq_insert_after(cdata->after, new_item); From 022d0210c5afa4e516183bf19715316ccca5d240 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 13 May 2021 08:49:58 +0100 Subject: [PATCH 0789/1006] More accurate vi(1) word navigation in copy mode and on the status line. This changes the meaning of the word-separators option - setting it to the empty string is equivalent to the previous behavior. From Will Noble in GitHub issue 2693. --- CHANGES | 4 +- grid-reader.c | 161 +++++++++++++++---------- options-table.c | 6 +- regress/copy-mode-test-emacs.sh | 116 ++++++++++++++++++ regress/copy-mode-test-vi.sh | 115 ++++++++++++++++++ regress/copy-mode-test.txt | 5 + status.c | 207 ++++++++++++++++++++++++-------- tmux.1 | 16 +-- tmux.h | 6 +- utf8.c | 2 +- window-copy.c | 102 ++++++++-------- 11 files changed, 564 insertions(+), 176 deletions(-) create mode 100644 regress/copy-mode-test-emacs.sh create mode 100644 regress/copy-mode-test-vi.sh create mode 100644 regress/copy-mode-test.txt diff --git a/CHANGES b/CHANGES index 42467dd3..8b23a0fb 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ CHANGES FROM 3.2 TO 3.3 -XXX +* More accurate vi(1) word navigation in copy mode and on the status line. This + changes the meaning of the word-separators option - setting it to the empty + string is equivalent to the previous behavior. CHANGES FROM 3.1c TO 3.2 diff --git a/grid-reader.c b/grid-reader.c index df0dd450..c14e3d33 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -153,6 +153,29 @@ grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all) gr->cx = grid_reader_line_length(gr); } +/* Handle line wrapping while moving the cursor. */ +static int +grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy) +{ + /* + * Make sure the cursor lies within the grid reader's bounding area, + * wrapping to the next line as necessary. Return zero if the cursor + * would wrap past the bottom of the grid. + */ + while (gr->cx > *xx) { + if (gr->cy == *yy) + return (0); + grid_reader_cursor_start_of_line(gr, 0); + grid_reader_cursor_down(gr); + + if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) + *xx = gr->gd->sx - 1; + else + *xx = grid_reader_line_length(gr); + } + return (1); +} + /* Check if character under cursor is in set. */ int grid_reader_in_set(struct grid_reader *gr, const char *set) @@ -170,7 +193,6 @@ void grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) { u_int xx, yy; - int expected = 0; /* Do not break up wrapped words. */ if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) @@ -180,33 +202,35 @@ grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) yy = gr->gd->hsize + gr->gd->sy - 1; /* - * If we started inside a word, skip over word characters. Then skip - * over separators till the next word. + * When navigating via spaces (for example with next-space) separators + * should be empty. * - * expected is initially set to 0 for the former and then 1 for the - * latter. It is finally set to 0 when the beginning of the next word is - * found. + * If we started on a separator that is not whitespace, skip over + * subsequent separators that are not whitespace. Otherwise, if we + * started on a non-whitespace character, skip over subsequent + * characters that are neither whitespace nor separators. Then, skip + * over whitespace (if any) until the next non-whitespace character. */ - do { - while (gr->cx > xx || - grid_reader_in_set(gr, separators) == expected) { - /* Move down if we are past the end of the line. */ - if (gr->cx > xx) { - if (gr->cy == yy) - return; - grid_reader_cursor_start_of_line(gr, 0); - grid_reader_cursor_down(gr); - - if (grid_get_line(gr->gd, gr->cy)->flags & - GRID_LINE_WRAPPED) - xx = gr->gd->sx - 1; - else - xx = grid_reader_line_length(gr); - } else + if (!grid_reader_handle_wrap(gr, &xx, &yy)) + return; + if (!grid_reader_in_set(gr, WHITESPACE)) { + if (grid_reader_in_set(gr, separators)) { + do gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + grid_reader_in_set(gr, separators) && + !grid_reader_in_set(gr, WHITESPACE)); + } else { + do + gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + !(grid_reader_in_set(gr, separators) || + grid_reader_in_set(gr, WHITESPACE))); } - expected = !expected; - } while (expected == 1); + } + while (grid_reader_handle_wrap(gr, &xx, &yy) && + grid_reader_in_set(gr, WHITESPACE)) + gr->cx++; } /* Move cursor to the end of the next word. */ @@ -214,7 +238,6 @@ void grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) { u_int xx, yy; - int expected = 1; /* Do not break up wrapped words. */ if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) @@ -224,49 +247,54 @@ grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) yy = gr->gd->hsize + gr->gd->sy - 1; /* - * If we started on a separator, skip over separators. Then skip over - * word characters till the next separator. + * When navigating via spaces (for example with next-space), separators + * should be empty in both modes. * - * expected is initially set to 1 for the former and then 1 for the - * latter. It is finally set to 1 when the end of the next word is - * found. + * If we started on a whitespace, move until reaching the first + * non-whitespace character. If that character is a separator, treat + * subsequent separators as a word, and continue moving until the first + * non-separator. Otherwise, continue moving until the first separator + * or whitespace. */ - do { - while (gr->cx > xx || - grid_reader_in_set(gr, separators) == expected) { - /* Move down if we are past the end of the line. */ - if (gr->cx > xx) { - if (gr->cy == yy) - return; - grid_reader_cursor_start_of_line(gr, 0); - grid_reader_cursor_down(gr); - if (grid_get_line(gr->gd, gr->cy)->flags & - GRID_LINE_WRAPPED) - xx = gr->gd->sx - 1; - else - xx = grid_reader_line_length(gr); - } else + while (grid_reader_handle_wrap(gr, &xx, &yy)) { + if (grid_reader_in_set(gr, WHITESPACE)) + gr->cx++; + else if (grid_reader_in_set(gr, separators)) { + do gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + grid_reader_in_set(gr, separators) && + !grid_reader_in_set(gr, WHITESPACE)); + return; + } else { + do + gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + !(grid_reader_in_set(gr, WHITESPACE) || + grid_reader_in_set(gr, separators))); + return; } - expected = !expected; - } while (expected == 0); + } } /* Move to the previous place where a word begins. */ void grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, - int already) + int already, int stop_at_eol) { - int oldx, oldy, r; + int oldx, oldy, at_eol, word_is_letters; /* Move back to the previous word character. */ - if (already || grid_reader_in_set(gr, separators)) { + if (already || grid_reader_in_set(gr, WHITESPACE)) { for (;;) { if (gr->cx > 0) { gr->cx--; - if (!grid_reader_in_set(gr, separators)) + if (!grid_reader_in_set(gr, WHITESPACE)) { + word_is_letters = + !grid_reader_in_set(gr, separators); break; + } } else { if (gr->cy == 0) return; @@ -274,17 +302,21 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, grid_reader_cursor_end_of_line(gr, 0, 0); /* Stop if separator at EOL. */ - if (gr->cx > 0) { + if (stop_at_eol && gr->cx > 0) { oldx = gr->cx; gr->cx--; - r = grid_reader_in_set(gr, separators); + at_eol = grid_reader_in_set(gr, + WHITESPACE); gr->cx = oldx; - if (r) + if (at_eol) { + word_is_letters = 0; break; + } } } } - } + } else + word_is_letters = !grid_reader_in_set(gr, separators); /* Move back to the beginning of this word. */ do { @@ -292,15 +324,16 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, oldy = gr->cy; if (gr->cx == 0) { if (gr->cy == 0 || - ~grid_get_line(gr->gd, gr->cy - 1)->flags & - GRID_LINE_WRAPPED) + (~grid_get_line(gr->gd, gr->cy - 1)->flags & + GRID_LINE_WRAPPED)) break; grid_reader_cursor_up(gr); grid_reader_cursor_end_of_line(gr, 0, 1); } if (gr->cx > 0) gr->cx--; - } while (!grid_reader_in_set(gr, separators)); + } while (!grid_reader_in_set(gr, WHITESPACE) && + word_is_letters != grid_reader_in_set(gr, separators)); gr->cx = oldx; gr->cy = oldy; } @@ -324,17 +357,17 @@ grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc) memcmp(gc.data.data, jc->data, gc.data.size) == 0) { gr->cx = px; gr->cy = py; - return 1; + return (1); } px++; } if (py == yy || !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)) - return 0; + return (0); px = 0; } - return 0; + return (0); } /* Jump back to character. */ @@ -354,16 +387,16 @@ grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc) memcmp(gc.data.data, jc->data, gc.data.size) == 0) { gr->cx = px - 1; gr->cy = py - 1; - return 1; + return (1); } } if (py == 1 || !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED)) - return 0; + return (0); xx = grid_line_length(gr->gd, py - 2); } - return 0; + return (0); } /* Jump back to the first non-blank character of the line. */ diff --git a/options-table.c b/options-table.c index 91b4d747..101490e7 100644 --- a/options-table.c +++ b/options-table.c @@ -754,7 +754,11 @@ const struct options_table_entry options_table[] = { { .name = "word-separators", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = " ", + /* + * The set of non-alphanumeric printable ASCII characters minus the + * underscore. + */ + .default_str = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", .text = "Characters considered to separate words." }, diff --git a/regress/copy-mode-test-emacs.sh b/regress/copy-mode-test-emacs.sh new file mode 100644 index 00000000..63293963 --- /dev/null +++ b/regress/copy-mode-test-emacs.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -f/dev/null -Ltest" +$TMUX kill-server 2>/dev/null + +$TMUX new -d -x40 -y10 \ + "cat copy-mode-test.txt; printf '\e[9;15H'; cat" || exit 1 +$TMUX set -g window-size manual || exit 1 + +# Enter copy mode and go to the first column of the first row. +$TMUX set-window-option -g mode-keys emacs +$TMUX set-window-option -g word-separators "" +$TMUX copy-mode +$TMUX send-keys -X history-top +$TMUX send-keys -X start-of-line + +# Test that `previous-word` and `previous-space` +# do not go past the start of text. +$TMUX send-keys -X begin-selection +$TMUX send-keys -X previous-word +$TMUX send-keys -X previous-space +$TMUX send-keys -X previous-word +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer 2>/dev/null)" = "" ] || exit 1 + +# Test that `next-word-end` does not skip single-letter words. +$TMUX send-keys -X next-word-end +$TMUX send-keys -X begin-selection +$TMUX send-keys -X previous-word +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "A" ] || exit 1 + +# Test that `next-word-end` wraps around indented line breaks. +$TMUX send-keys -X next-word +$TMUX send-keys -X next-word +$TMUX send-keys -X next-word +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word-end +$TMUX send-keys -X next-word-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "$(echo -e "words\n Indented")" ] || exit 1 + +# Test that `next-word` wraps around un-indented line breaks. +$TMUX send-keys -X next-word +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "$(echo -e "line\n")" ] || exit 1 + +# Test that `next-word-end` treats periods as letters. +$TMUX send-keys -X next-word +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "line..." ] || exit 1 + +# Test that `previous-word` and `next-word` treat periods as letters. +$TMUX send-keys -X previous-word +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "$(echo -e "line...\n")" ] || exit 1 + +# Test that `previous-space` and `next-space` treat periods as letters. +$TMUX send-keys -X previous-space +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-space +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "$(echo -e "line...\n")" ] || exit 1 + +# Test that `next-word` and `next-word-end` treat other symbols as letters. +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word +$TMUX send-keys -X next-word +$TMUX send-keys -X next-word-end +$TMUX send-keys -X next-word-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "... @nd then \$ym_bols[]{}" ] || exit 1 + +# Test that `previous-word` treats other symbols as letters +# and `next-word` wraps around for indented symbols +$TMUX send-keys -X previous-word +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "$(echo -e "\$ym_bols[]{}\n ")" ] || exit 1 + +# Test that `next-word-end` treats digits as letters +$TMUX send-keys -X next-word-end +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = " 500xyz" ] || exit 1 + +# Test that `previous-word` treats digits as letters +$TMUX send-keys -X begin-selection +$TMUX send-keys -X previous-word +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "500xyz" ] || exit 1 + +# Test that `next-word` and `next-word-end` stop at the end of text. +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word +$TMUX send-keys -X next-word-end +$TMUX send-keys -X next-word +$TMUX send-keys -X next-space +$TMUX send-keys -X next-space-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "500xyz" ] || exit 1 + +$TMUX kill-server 2>/dev/null +exit 0 diff --git a/regress/copy-mode-test-vi.sh b/regress/copy-mode-test-vi.sh new file mode 100644 index 00000000..0eca3fa6 --- /dev/null +++ b/regress/copy-mode-test-vi.sh @@ -0,0 +1,115 @@ +#!/bin/sh + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -f/dev/null -Ltest" +$TMUX kill-server 2>/dev/null + +$TMUX new -d -x40 -y10 \ + "cat copy-mode-test.txt; printf '\e[9;15H'; cat" || exit 1 +$TMUX set -g window-size manual || exit 1 + +# Enter copy mode and go to the first column of the first row. +$TMUX set-window-option -g mode-keys vi +$TMUX copy-mode +$TMUX send-keys -X history-top +$TMUX send-keys -X start-of-line + +# Test that `previous-word` and `previous-space` +# do not go past the start of text. +$TMUX send-keys -X begin-selection +$TMUX send-keys -X previous-word +$TMUX send-keys -X previous-space +$TMUX send-keys -X previous-word +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "A" ] || exit 1 + +# Test that `next-word-end` skips single-letter words +# and `previous-word` does not skip multi-letter words. +$TMUX send-keys -X next-word-end +$TMUX send-keys -X begin-selection +$TMUX send-keys -X previous-word +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "line" ] || exit 1 + +# Test that `next-word-end` wraps around indented line breaks. +$TMUX send-keys -X next-word +$TMUX send-keys -X next-word +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word-end +$TMUX send-keys -X next-word-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "$(echo -e "words\n Indented")" ] || exit 1 + +# Test that `next-word` wraps around un-indented line breaks. +$TMUX send-keys -X next-word +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "$(echo -e "line\nA")" ] || exit 1 + +# Test that `next-word-end` does not treat periods as letters. +$TMUX send-keys -X next-word +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "line" ] || exit 1 + +# Test that `next-space-end` treats periods as letters. +$TMUX send-keys -X previous-word +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-space-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "line..." ] || exit 1 + +# Test that `previous-space` and `next-space` treat periods as letters. +$TMUX send-keys -X previous-space +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-space +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "$(echo -e "line...\n.")" ] || exit 1 + +# Test that `next-word` and `next-word-end` do not treat other symbols as letters. +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word +$TMUX send-keys -X next-word +$TMUX send-keys -X next-word-end +$TMUX send-keys -X next-word-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "... @nd then" ] || exit 1 + +# Test that `next-space` wraps around for indented symbols +$TMUX send-keys -X next-space +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-space +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "$(echo -e "\$ym_bols[]{}\n ?")" ] || exit 1 + +# Test that `next-word-end` treats digits as letters +$TMUX send-keys -X next-word-end +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "? 500xyz" ] || exit 1 + +# Test that `previous-word` treats digits as letters +$TMUX send-keys -X begin-selection +$TMUX send-keys -X previous-word +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "500xyz" ] || exit 1 + +# Test that `next-word`, `next-word-end`, +# `next-space`, and `next-space-end` stop at the end of text. +$TMUX send-keys -X begin-selection +$TMUX send-keys -X next-word +$TMUX send-keys -X next-word-end +$TMUX send-keys -X next-word +$TMUX send-keys -X next-space +$TMUX send-keys -X next-space-end +$TMUX send-keys -X copy-selection +[ "$($TMUX show-buffer)" = "500xyz" ] || exit 1 + +$TMUX kill-server 2>/dev/null +exit 0 diff --git a/regress/copy-mode-test.txt b/regress/copy-mode-test.txt new file mode 100644 index 00000000..a804f1f8 --- /dev/null +++ b/regress/copy-mode-test.txt @@ -0,0 +1,5 @@ +A line of words + Indented line +Another line... +... @nd then $ym_bols[]{} + ?? 500xyz diff --git a/status.c b/status.c index 7bcd2f38..7435b31a 100644 --- a/status.c +++ b/status.c @@ -876,17 +876,25 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) *new_key = KEYC_BSPACE; return (1); case 'b': - case 'B': *new_key = 'b'|KEYC_META; return (1); + case 'B': + *new_key = 'B'|KEYC_VI; + return (1); case 'd': *new_key = '\025'; return (1); case 'e': + *new_key = 'e'|KEYC_VI; + return (1); case 'E': + *new_key = 'E'|KEYC_VI; + return (1); case 'w': + *new_key = 'w'|KEYC_VI; + return (1); case 'W': - *new_key = 'f'|KEYC_META; + *new_key = 'W'|KEYC_VI; return (1); case 'p': *new_key = '\031'; /* C-y */ @@ -1061,16 +1069,125 @@ status_prompt_replace_complete(struct client *c, const char *s) return (1); } +/* Prompt forward to the next beginning of a word. */ +static void +status_prompt_forward_word(struct client *c, size_t size, int vi, + const char *separators) +{ + size_t idx = c->prompt_index; + int word_is_separators; + + /* In emacs mode, skip until the first non-whitespace character. */ + if (!vi) + while (idx != size && + status_prompt_space(&c->prompt_buffer[idx])) + idx++; + + /* Can't move forward if we're already at the end. */ + if (idx == size) { + c->prompt_index = idx; + return; + } + + /* Determine the current character class (separators or not). */ + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]) && + !status_prompt_space(&c->prompt_buffer[idx]); + + /* Skip ahead until the first space or opposite character class. */ + do { + idx++; + if (status_prompt_space(&c->prompt_buffer[idx])) { + /* In vi mode, go to the start of the next word. */ + if (vi) + while (idx != size && + status_prompt_space(&c->prompt_buffer[idx])) + idx++; + break; + } + } while (idx != size && word_is_separators == status_prompt_in_list( + separators, &c->prompt_buffer[idx])); + + c->prompt_index = idx; +} + +/* Prompt forward to the next end of a word. */ +static void +status_prompt_end_word(struct client *c, size_t size, const char *separators) +{ + size_t idx = c->prompt_index; + int word_is_separators; + + /* Can't move forward if we're already at the end. */ + if (idx == size) + return; + + /* Find the next word. */ + do { + idx++; + if (idx == size) { + c->prompt_index = idx; + return; + } + } while (status_prompt_space(&c->prompt_buffer[idx])); + + /* Determine the character class (separators or not). */ + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]); + + /* Skip ahead until the next space or opposite character class. */ + do { + idx++; + if (idx == size) + break; + } while (!status_prompt_space(&c->prompt_buffer[idx]) && + word_is_separators == status_prompt_in_list(separators, + &c->prompt_buffer[idx])); + + /* Back up to the previous character to stop at the end of the word. */ + c->prompt_index = idx - 1; +} + +/* Prompt backward to the previous beginning of a word. */ +static void +status_prompt_backward_word(struct client *c, const char *separators) +{ + size_t idx = c->prompt_index; + int word_is_separators; + + /* Find non-whitespace. */ + while (idx != 0) { + --idx; + if (!status_prompt_space(&c->prompt_buffer[idx])) + break; + } + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]); + + /* Find the character before the beginning of the word. */ + while (idx != 0) { + --idx; + if (status_prompt_space(&c->prompt_buffer[idx]) || + word_is_separators != status_prompt_in_list(separators, + &c->prompt_buffer[idx])) { + /* Go back to the word. */ + idx++; + break; + } + } + c->prompt_index = idx; +} + /* Handle keys in prompt. */ int status_prompt_key(struct client *c, key_code key) { struct options *oo = c->session->options; char *s, *cp, prefix = '='; - const char *histstr, *ws = NULL, *keystring; + const char *histstr, *separators = NULL, *keystring; size_t size, idx; struct utf8_data tmp; - int keys; + int keys, word_is_separators; if (c->prompt_flags & PROMPT_KEY) { keystring = key_string_lookup_key(key, 0); @@ -1173,20 +1290,24 @@ process_key: } break; case '\027': /* C-w */ - ws = options_get_string(oo, "word-separators"); + separators = options_get_string(oo, "word-separators"); idx = c->prompt_index; - /* Find a non-separator. */ + /* Find non-whitespace. */ while (idx != 0) { idx--; - if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) + if (!status_prompt_space(&c->prompt_buffer[idx])) break; } + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]); - /* Find the separator at the beginning of the word. */ + /* Find the character before the beginning of the word. */ while (idx != 0) { idx--; - if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) { + if (status_prompt_space(&c->prompt_buffer[idx]) || + word_is_separators != status_prompt_in_list( + separators, &c->prompt_buffer[idx])) { /* Go back to the word. */ idx++; break; @@ -1208,50 +1329,32 @@ process_key: c->prompt_index = idx; goto changed; - case 'f'|KEYC_META: case KEYC_RIGHT|KEYC_CTRL: - ws = options_get_string(oo, "word-separators"); - - /* Find a word. */ - while (c->prompt_index != size) { - idx = ++c->prompt_index; - if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) - break; - } - - /* Find the separator at the end of the word. */ - while (c->prompt_index != size) { - idx = ++c->prompt_index; - if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) - break; - } - - /* Back up to the end-of-word like vi. */ - if (options_get_number(oo, "status-keys") == MODEKEY_VI && - c->prompt_index != 0) - c->prompt_index--; - + case 'f'|KEYC_META: + separators = options_get_string(oo, "word-separators"); + status_prompt_forward_word(c, size, 0, separators); + goto changed; + case 'E'|KEYC_VI: + status_prompt_end_word(c, size, ""); + goto changed; + case 'e'|KEYC_VI: + separators = options_get_string(oo, "word-separators"); + status_prompt_end_word(c, size, separators); + goto changed; + case 'W'|KEYC_VI: + status_prompt_forward_word(c, size, 1, ""); + goto changed; + case 'w'|KEYC_VI: + separators = options_get_string(oo, "word-separators"); + status_prompt_forward_word(c, size, 1, separators); + goto changed; + case 'B'|KEYC_VI: + status_prompt_backward_word(c, ""); goto changed; - case 'b'|KEYC_META: case KEYC_LEFT|KEYC_CTRL: - ws = options_get_string(oo, "word-separators"); - - /* Find a non-separator. */ - while (c->prompt_index != 0) { - idx = --c->prompt_index; - if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) - break; - } - - /* Find the separator at the beginning of the word. */ - while (c->prompt_index != 0) { - idx = --c->prompt_index; - if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) { - /* Go back to the word. */ - c->prompt_index++; - break; - } - } + case 'b'|KEYC_META: + separators = options_get_string(oo, "word-separators"); + status_prompt_backward_word(c, separators); goto changed; case KEYC_UP: case '\020': /* C-p */ @@ -1339,8 +1442,10 @@ append_key: return (0); if (key <= 0x7f) utf8_set(&tmp, key); - else + else if (KEYC_IS_UNICODE(key)) utf8_to_data(key, &tmp); + else + return (0); c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2, sizeof *c->prompt_buffer); diff --git a/tmux.1 b/tmux.1 index e02e5728..c8170e3b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1782,19 +1782,18 @@ commands) or when the cursor reaches the bottom (for scrolling commands). .Ql -no-clear variants do not clear the selection. .Pp -The next and previous word keys use space and the -.Ql - , -.Ql _ -and -.Ql @ -characters as word delimiters by default, but this can be adjusted by -setting the +The next and previous word keys skip over whitespace and treat consecutive +runs of either word separators or other letters as words. +Word separators can be customized with the .Em word-separators session option. Next word moves to the start of the next word, next word end to the end of the next word and previous word to the start of the previous word. The three next and previous space keys work similarly but use a space alone as the word separator. +Setting +.Em word-separators +to the empty string makes next/previous word equivalent to next/previous space. .Pp The jump commands enable quick movement within a line. For instance, typing @@ -3974,9 +3973,6 @@ If set to both, a bell and a message are produced. Sets the session's conception of what characters are considered word separators, for the purposes of the next and previous word commands in copy mode. -The default is -.Ql \ -_@ . -.El .Pp Available window options are: .Pp diff --git a/tmux.h b/tmux.h index e7cd205a..b775c219 100644 --- a/tmux.h +++ b/tmux.h @@ -131,6 +131,7 @@ struct winlink; #define KEYC_CURSOR 0x04000000000000ULL #define KEYC_IMPLIED_META 0x08000000000000ULL #define KEYC_BUILD_MODIFIERS 0x10000000000000ULL +#define KEYC_VI 0x20000000000000ULL /* Masks for key bits. */ #define KEYC_MASK_MODIFIERS 0x00f00000000000ULL @@ -590,6 +591,9 @@ struct msg_write_close { int stream; }; +/* Character classes. */ +#define WHITESPACE " " + /* Mode keys. */ #define MODEKEY_EMACS 0 #define MODEKEY_VI 1 @@ -2640,7 +2644,7 @@ void grid_reader_cursor_end_of_line(struct grid_reader *, int, int); void grid_reader_cursor_next_word(struct grid_reader *, const char *); void grid_reader_cursor_next_word_end(struct grid_reader *, const char *); void grid_reader_cursor_previous_word(struct grid_reader *, const char *, - int); + int, int); int grid_reader_cursor_jump(struct grid_reader *, const struct utf8_data *); int grid_reader_cursor_jump_back(struct grid_reader *, diff --git a/utf8.c b/utf8.c index f43945e6..56f20cbb 100644 --- a/utf8.c +++ b/utf8.c @@ -64,7 +64,7 @@ static struct utf8_index_tree utf8_index_tree = RB_INITIALIZER(utf8_index_tree); static u_int utf8_next_index; #define UTF8_GET_SIZE(uc) (((uc) >> 24) & 0x1f) -#define UTF8_GET_WIDTH(flags) (((uc) >> 29) - 1) +#define UTF8_GET_WIDTH(uc) (((uc) >> 29) - 1) #define UTF8_SET_SIZE(size) (((utf8_char)(size)) << 24) #define UTF8_SET_WIDTH(width) ((((utf8_char)(width)) + 1) << 29) diff --git a/window-copy.c b/window-copy.c index 423cce8f..2f33a23a 100644 --- a/window-copy.c +++ b/window-copy.c @@ -128,7 +128,7 @@ static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *, static void window_copy_cursor_next_word_end(struct window_mode_entry *, const char *, int); static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, - const char *, int, u_int *, u_int *); + const char *, u_int *, u_int *); static void window_copy_cursor_previous_word(struct window_mode_entry *, const char *, int); static void window_copy_scroll_up(struct window_mode_entry *, u_int); @@ -255,7 +255,7 @@ struct window_copy_mode_data { SEL_LINE, /* select one line at a time */ } selflag; - const char *ws; /* word separators */ + const char *separators; /* word separators */ u_int dx; /* drag start position */ u_int dy; @@ -1321,7 +1321,7 @@ window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_previous_word(wme, "}]) ", 1); + window_copy_cursor_previous_word(wme, close, 1); } continue; } @@ -1433,8 +1433,7 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_next_word_end(wme, "{[( ", - 0); + window_copy_cursor_next_word_end(wme, open, 0); continue; } /* For vi, continue searching for bracket until EOL. */ @@ -1506,7 +1505,7 @@ window_copy_cmd_next_space(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_next_word(wme, " "); + window_copy_cursor_next_word(wme, ""); return (WINDOW_COPY_CMD_NOTHING); } @@ -1517,7 +1516,7 @@ window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, " ", 0); + window_copy_cursor_next_word_end(wme, "", 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1525,13 +1524,13 @@ static enum window_copy_cmd_action window_copy_cmd_next_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; u_int np = wme->prefix; - const char *ws; + const char *separators; + + separators = options_get_string(cs->s->options, "word-separators"); - ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_next_word(wme, ws); + window_copy_cursor_next_word(wme, separators); return (WINDOW_COPY_CMD_NOTHING); } @@ -1539,13 +1538,13 @@ static enum window_copy_cmd_action window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; u_int np = wme->prefix; - const char *ws; + const char *separators; + + separators = options_get_string(cs->s->options, "word-separators"); - ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, ws, 0); + window_copy_cursor_next_word_end(wme, separators, 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1618,7 +1617,7 @@ window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_previous_word(wme, " ", 1); + window_copy_cursor_previous_word(wme, "", 1); return (WINDOW_COPY_CMD_NOTHING); } @@ -1626,13 +1625,13 @@ static enum window_copy_cmd_action window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; u_int np = wme->prefix; - const char *ws; + const char *separators; + + separators = options_get_string(cs->s->options, "word-separators"); - ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_previous_word(wme, ws, 1); + window_copy_cursor_previous_word(wme, separators, 1); return (WINDOW_COPY_CMD_NOTHING); } @@ -1778,18 +1777,20 @@ static enum window_copy_cmd_action window_copy_cmd_select_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; + struct options *session_options = cs->s->options; struct window_copy_mode_data *data = wme->data; u_int px, py, nextx, nexty; + data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; data->selflag = SEL_WORD; data->dx = data->cx; data->dy = screen_hsize(data->backing) + data->cy - data->oy; - data->ws = options_get_string(s->options, "word-separators"); - window_copy_cursor_previous_word(wme, data->ws, 0); + data->separators = options_get_string(session_options, + "word-separators"); + window_copy_cursor_previous_word(wme, data->separators, 0); px = data->cx; py = screen_hsize(data->backing) + data->cy - data->oy; data->selrx = px; @@ -1805,8 +1806,8 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) nexty++; } if (px >= window_copy_find_length(wme, py) || - !window_copy_in_set(wme, nextx, nexty, data->ws)) - window_copy_cursor_next_word_end(wme, data->ws, 1); + !window_copy_in_set(wme, nextx, nexty, WHITESPACE)) + window_copy_cursor_next_word_end(wme, data->separators, 1); else { window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1, 1)) @@ -3730,8 +3731,8 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, begin = 0; if (data->dy > yy || (data->dy == yy && data->dx > xx)) { /* Right to left selection. */ - window_copy_cursor_previous_word_pos(wme, data->ws, 0, - &xx, &yy); + window_copy_cursor_previous_word_pos(wme, + data->separators, &xx, &yy); begin = 1; /* Reset the end. */ @@ -3740,9 +3741,10 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, } else { /* Left to right selection. */ if (xx >= window_copy_find_length(wme, yy) || - !window_copy_in_set(wme, xx + 1, yy, data->ws)) + !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) { window_copy_cursor_next_word_end_pos(wme, - data->ws, &xx, &yy); + data->separators, &xx, &yy); + } /* Reset the start. */ data->selx = data->selrx; @@ -4666,19 +4668,19 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, hsize; - int keys; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; grid_reader_start(&gr, back_s->grid, px, py); - keys = options_get_number(oo, "mode-keys"); - if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators)) - grid_reader_cursor_right(&gr, 0, 0); - grid_reader_cursor_next_word_end(&gr, separators); - if (keys == MODEKEY_VI) + if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { + if (!grid_reader_in_set(&gr, WHITESPACE)) + grid_reader_cursor_right(&gr, 0, 0); + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_cursor_left(&gr, 1); + } else + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_get_cursor(&gr, &px, &py); *ppx = px; *ppy = py; @@ -4695,7 +4697,6 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, oldy, hsize; - int keys; px = data->cx; hsize = screen_hsize(back_s); @@ -4703,12 +4704,13 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); - keys = options_get_number(oo, "mode-keys"); - if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators)) - grid_reader_cursor_right(&gr, 0, 0); - grid_reader_cursor_next_word_end(&gr, separators); - if (keys == MODEKEY_VI) + if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { + if (!grid_reader_in_set(&gr, WHITESPACE)) + grid_reader_cursor_right(&gr, 0, 0); + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_cursor_left(&gr, 1); + } else + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), data->oy, oldy, px, py, no_reset); @@ -4717,7 +4719,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, /* Compute the previous place where a word begins. */ static void window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, - const char *separators, int already, u_int *ppx, u_int *ppy) + const char *separators, u_int *ppx, u_int *ppy) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; @@ -4729,7 +4731,8 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, py = hsize + data->cy - data->oy; grid_reader_start(&gr, back_s->grid, px, py); - grid_reader_cursor_previous_word(&gr, separators, already); + grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0, + /* stop_at_eol= */ 1); grid_reader_get_cursor(&gr, &px, &py); *ppx = px; *ppy = py; @@ -4744,6 +4747,11 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, oldy, hsize; + int stop_at_eol; + + stop_at_eol = + options_get_number(wme->wp->window->options, "mode-keys") + == MODEKEY_EMACS; px = data->cx; hsize = screen_hsize(back_s); @@ -4751,7 +4759,7 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); - grid_reader_cursor_previous_word(&gr, separators, already); + grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } @@ -4893,10 +4901,10 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) data->selflag = SEL_CHAR; switch (data->selflag) { case SEL_WORD: - if (data->ws != NULL) { + if (data->separators != NULL) { window_copy_update_cursor(wme, x, y); - window_copy_cursor_previous_word_pos(wme, data->ws, 0, - &x, &y); + window_copy_cursor_previous_word_pos(wme, + data->separators, &x, &y); y -= screen_hsize(data->backing) - data->oy; } window_copy_update_cursor(wme, x, y); From 01ba6a23f2c407743ae2ed846987f88e7feff68f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 17 May 2021 06:58:45 +0100 Subject: [PATCH 0790/1006] Fix <= operator. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index c7960819..3e640a6f 100644 --- a/format.c +++ b/format.c @@ -3991,7 +3991,7 @@ format_replace_expression(struct format_modifier *mexp, result = (mleft < mright); break; case LESS_THAN_EQUAL: - result = (mleft > mright); + result = (mleft >= mright); break; } if (use_fp) From 83024f57a6dcad690da5bdd559df07ef10d44ccd Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 17 May 2021 06:59:29 +0100 Subject: [PATCH 0791/1006] Er, fix it properly. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index 3e640a6f..6ac086cb 100644 --- a/format.c +++ b/format.c @@ -3991,7 +3991,7 @@ format_replace_expression(struct format_modifier *mexp, result = (mleft < mright); break; case LESS_THAN_EQUAL: - result = (mleft >= mright); + result = (mleft <= mright); break; } if (use_fp) From 5f7ff732fa7c7299c365b283a62d0154899f49db Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 19 May 2021 09:04:45 +0100 Subject: [PATCH 0792/1006] Bump FORMAT_LOOOP_LIMIT and add a log message when hit, GitHub issue 2715. --- format.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 6ac086cb..512df009 100644 --- a/format.c +++ b/format.c @@ -103,7 +103,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_CHARACTER 0x10000 /* Limit on recursion. */ -#define FORMAT_LOOP_LIMIT 10 +#define FORMAT_LOOP_LIMIT 100 /* Format expand flags. */ #define FORMAT_EXPAND_TIME 0x1 @@ -4441,8 +4441,10 @@ format_expand1(struct format_expand_state *es, const char *fmt) if (fmt == NULL || *fmt == '\0') return (xstrdup("")); - if (es->loop == FORMAT_LOOP_LIMIT) + if (es->loop == FORMAT_LOOP_LIMIT) { + format_log(es, "reached loop limit (%u)", FORMAT_LOOP_LIMIT); return (xstrdup("")); + } es->loop++; format_log(es, "expanding format: %s", fmt); From 9ea971dc046ed1c5f241cf1f7b91b3a16e049225 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 19 May 2021 09:05:53 +0100 Subject: [PATCH 0793/1006] Fix rectangle selection, from Anindya Mukherjee, GitHub issue 2709. --- window-copy.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/window-copy.c b/window-copy.c index 2f33a23a..31875739 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1102,10 +1102,13 @@ static enum window_copy_cmd_action window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - for (; np != 0; np--) - window_copy_cursor_right(wme, 0); + for (; np != 0; np--) { + window_copy_cursor_right(wme, data->screen.sel != NULL && + data->rectflag); + } return (WINDOW_COPY_CMD_NOTHING); } @@ -4427,10 +4430,12 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int ox, oy, px, py; + int norectsel; + norectsel = data->screen.sel == NULL || !data->rectflag; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wme, oy); - if (data->cx != ox) { + if (norectsel && data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } @@ -4439,7 +4444,8 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) window_copy_other_end(wme); if (scroll_only || data->cy == 0) { - data->cx = data->lastcx; + if (norectsel) + data->cx = data->lastcx; window_copy_scroll_down(wme, 1); if (scroll_only) { if (data->cy == screen_size_y(s) - 1) @@ -4448,7 +4454,11 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) window_copy_redraw_lines(wme, data->cy, 2); } } else { - window_copy_update_cursor(wme, data->lastcx, data->cy - 1); + if (norectsel) { + window_copy_update_cursor(wme, data->lastcx, + data->cy - 1); + } else + window_copy_update_cursor(wme, data->cx, data->cy - 1); if (window_copy_update_selection(wme, 1, 0)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wme, data->cy, 1); @@ -4457,7 +4467,7 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) } } - if (data->screen.sel == NULL || !data->rectflag) { + if (norectsel) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || @@ -4494,10 +4504,12 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int ox, oy, px, py; + int norectsel; + norectsel = data->screen.sel == NULL || !data->rectflag; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wme, oy); - if (data->cx != ox) { + if (norectsel && data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } @@ -4506,17 +4518,22 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) window_copy_other_end(wme); if (scroll_only || data->cy == screen_size_y(s) - 1) { - data->cx = data->lastcx; + if (norectsel) + data->cx = data->lastcx; window_copy_scroll_up(wme, 1); if (scroll_only && data->cy > 0) window_copy_redraw_lines(wme, data->cy - 1, 2); } else { - window_copy_update_cursor(wme, data->lastcx, data->cy + 1); + if (norectsel) { + window_copy_update_cursor(wme, data->lastcx, + data->cy + 1); + } else + window_copy_update_cursor(wme, data->cx, data->cy + 1); if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy - 1, 2); } - if (data->screen.sel == NULL || !data->rectflag) { + if (norectsel) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || From 7a236869ba05b5a8ace0b57b08ec839fbafc71aa Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Jun 2021 10:49:40 +0100 Subject: [PATCH 0794/1006] Feature for the mouse since FreeBSD termcap does not have kmous. --- tmux.1 | 4 ++++ tty-features.c | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index c8170e3b..89d28a9c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3540,6 +3540,10 @@ Supports extended keys. Supports focus reporting. .It margins Supports DECSLRM margins. +.It mouse +Supports +.Xr xterm 1 +mouse sequences. .It overline Supports the overline SGR attribute. .It rectfill diff --git a/tty-features.c b/tty-features.c index b42cf74a..48ac51be 100644 --- a/tty-features.c +++ b/tty-features.c @@ -25,7 +25,6 @@ /* * Still hardcoded: - * - mouse (under kmous capability); * - default colours (under AX or op capabilities); * - AIX colours (under colors >= 16); * - alternate escape (if terminal is VT100-like). @@ -54,6 +53,17 @@ static const struct tty_feature tty_feature_title = { 0 }; +/* Terminal has mouse support. */ +static const char *tty_feature_mouse_capabilities[] = { + "kmous=\\E[M", + NULL +}; +static const struct tty_feature tty_feature_mouse = { + "mouse", + tty_feature_mouse_capabilities, + 0 +}; + /* Terminal can set the clipboard with OSC 52. */ static const char *tty_feature_clipboard_capabilities[] = { "Ms=\\E]52;%p1%s;%p2%s\\a", @@ -238,6 +248,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_extkeys, &tty_feature_focus, &tty_feature_margins, + &tty_feature_mouse, &tty_feature_overline, &tty_feature_rectfill, &tty_feature_rgb, @@ -338,7 +349,7 @@ tty_default_features(int *feat, const char *name, u_int version) const char *features; } table[] = { #define TTY_FEATURES_BASE_MODERN_XTERM \ - "256,RGB,bpaste,clipboard,strikethrough,title" + "256,RGB,bpaste,clipboard,mouse,strikethrough,title" { .name = "mintty", .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,margins,overline,usstyle" @@ -348,7 +359,7 @@ tty_default_features(int *feat, const char *name, u_int version) ",ccolour,cstyle,focus,overline,usstyle" }, { .name = "rxvt-unicode", - .features = "256,bpaste,ccolour,cstyle,title" + .features = "256,bpaste,ccolour,cstyle,mouse,title" }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM From 607e6b1c3339b97408be3d6c2c93a82fcee5251a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 9 Jun 2021 14:46:24 +0100 Subject: [PATCH 0795/1006] Do not clear region based on current cursor position, this is not necessary anymore and causes problems, GitHub issue 2735. --- tty.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tty.c b/tty.c index 45868fc1..4b1b6777 100644 --- a/tty.c +++ b/tty.c @@ -1005,13 +1005,8 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) return; } - if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { - for (i = ctx->ocy; i < ctx->sy; i++) - tty_draw_pane(tty, ctx, i); - } else { - for (i = ctx->orupper; i <= ctx->orlower; i++) - tty_draw_pane(tty, ctx, i); - } + for (i = ctx->orupper; i <= ctx->orlower; i++) + tty_draw_pane(tty, ctx, i); } /* Is this position visible in the pane? */ From 747423be6731b700cb53186a943d51cc2275d101 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Jun 2021 08:08:31 +0100 Subject: [PATCH 0796/1006] Fix empty format strings, from Jean-Philippe Menil. --- cmd-show-prompt-history.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-show-prompt-history.c b/cmd-show-prompt-history.c index 5711755e..2091ac9d 100644 --- a/cmd-show-prompt-history.c +++ b/cmd-show-prompt-history.c @@ -87,7 +87,7 @@ cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item) cmdq_print(item, "%d: %s", hidx + 1, status_prompt_hlist[tidx][hidx]); } - cmdq_print(item, ""); + cmdq_print(item, "%s", ""); } } else { type = status_prompt_type(typestr); @@ -101,7 +101,7 @@ cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item) cmdq_print(item, "%d: %s", hidx + 1, status_prompt_hlist[type][hidx]); } - cmdq_print(item, ""); + cmdq_print(item, "%s", ""); } return (CMD_RETURN_NORMAL); From e5106bfb9660d5c9470acf9defa1084c61a070d4 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:21:09 +0000 Subject: [PATCH 0797/1006] Add another couple of keys needed for extended keys, GitHub issue 2658. Handle modifier 9 as Meta, GitHub issue 2647. --- key-bindings.c | 6 ++++++ key-string.c | 10 +++++++--- tty-keys.c | 11 +++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index b47f6ff7..467c7f93 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -215,6 +215,9 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, if (repeat) bd->flags |= KEY_BINDING_REPEAT; bd->cmdlist = cmdlist; + + log_debug("%s: %#llx %s = %s", __func__, bd->key, + key_string_lookup_key(bd->key, 1), cmd_list_print(bd->cmdlist, 0)); } void @@ -231,6 +234,9 @@ key_bindings_remove(const char *name, key_code key) if (bd == NULL) return; + log_debug("%s: %#llx %s", __func__, bd->key, + key_string_lookup_key(bd->key, 1)); + RB_REMOVE(key_bindings, &table->key_bindings, bd); key_bindings_free(bd); diff --git a/key-string.c b/key-string.c index 8d60f132..c24a33fc 100644 --- a/key-string.c +++ b/key-string.c @@ -164,7 +164,7 @@ key_string_get_modifiers(const char **string) key_code key_string_lookup_string(const char *string) { - static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177"; + static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177`/"; key_code key, modifiers; u_int u, i; struct utf8_data ud, *udp; @@ -238,8 +238,12 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && - strchr(other, key) == NULL) { + if (key < KEYC_BASE && + (modifiers & KEYC_CTRL) && + strchr(other, key) == NULL && + key != 9 && + key != 13 && + key != 27) { if (key >= 97 && key <= 122) key -= 96; else if (key >= 64 && key <= 95) diff --git a/tty-keys.c b/tty-keys.c index 040e7005..3012be3d 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -252,7 +252,8 @@ static const key_code tty_default_xterm_modifiers[] = { KEYC_CTRL, KEYC_SHIFT|KEYC_CTRL, KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, - KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, + KEYC_META|KEYC_IMPLIED_META }; /* @@ -944,6 +945,9 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, case 8: nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); break; + case 9: + nkey |= (KEYC_META|KEYC_IMPLIED_META); + break; default: *key = KEYC_NONE; break; @@ -955,7 +959,10 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, */ if (nkey & KEYC_CTRL) { onlykey = (nkey & KEYC_MASK_KEY); - if (onlykey < 32 && onlykey != 9) + if (onlykey < 32 && + onlykey != 9 && + onlykey != 13 && + onlykey != 27) /* nothing */; else if (onlykey >= 97 && onlykey <= 122) onlykey -= 96; From c46a607dc10606047f685f29a71213501de819d1 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:21:46 +0000 Subject: [PATCH 0798/1006] Adjust latest client when a client detaches, GitHub issue 2657. --- server-client.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 25d61376..1114396d 100644 --- a/server-client.c +++ b/server-client.c @@ -46,6 +46,7 @@ static void server_client_check_modes(struct client *); static void server_client_set_title(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); +static void server_client_update_latest(struct client *); static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch_command(struct client *, struct imsg *); @@ -274,6 +275,40 @@ server_client_open(struct client *c, char **cause) return (0); } +/* Lost an attached client. */ +static void +server_client_attached_lost(struct client *c) +{ + struct session *s = c->session; + struct window *w; + struct client *loop; + struct client *found; + + log_debug("lost attached client %p", c); + + /* + * By this point the session in the client has been cleared so walk all + * windows to find any with this client as the latest. + */ + RB_FOREACH(w, windows, &windows) { + if (w->latest != c) + continue; + + found = NULL; + TAILQ_FOREACH(loop, &clients, entry) { + s = loop->session; + if (loop == c || s == NULL || s->curw->window != w) + continue; + if (found == NULL || + timercmp(&loop->activity_time, &found->activity_time, + >)) + found = loop; + } + if (found != NULL) + server_client_update_latest(found); + } +} + /* Lost a client. */ void server_client_lost(struct client *c) @@ -299,8 +334,10 @@ server_client_lost(struct client *c) TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); - if (c->flags & CLIENT_ATTACHED) + if (c->flags & CLIENT_ATTACHED) { + server_client_attached_lost(c); notify_client("client-detached", c); + } if (c->flags & CLIENT_CONTROL) control_stop(c); From 788f56b40a695b21d5ee861d2789f354608cda81 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:22:06 +0000 Subject: [PATCH 0799/1006] Fix display-menu -xR, from Alexis Hildebrandt. --- cmd-display-menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 27a4c1d7..f2100bdb 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -206,7 +206,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, if (xp == NULL || strcmp(xp, "C") == 0) xp = "#{popup_centre_x}"; else if (strcmp(xp, "R") == 0) - xp = "#{popup_right}"; + xp = "#{popup_pane_right}"; else if (strcmp(xp, "P") == 0) xp = "#{popup_pane_left}"; else if (strcmp(xp, "M") == 0) From 1ee231956ca9d1436e101350f4246abc1f157224 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:22:37 +0000 Subject: [PATCH 0800/1006] back-to-indentation fixes, from Anindya Mukherjee. --- grid-reader.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/grid-reader.c b/grid-reader.c index 89fe90fb..df0dd450 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -371,19 +371,26 @@ void grid_reader_cursor_back_to_indentation(struct grid_reader *gr) { struct grid_cell gc; - u_int px, py, xx, yy; + u_int px, py, xx, yy, oldx, oldy; yy = gr->gd->hsize + gr->gd->sy - 1; + oldx = gr->cx; + oldy = gr->cy; grid_reader_cursor_start_of_line(gr, 1); for (py = gr->cy; py <= yy; py++) { xx = grid_line_length(gr->gd, py); for (px = 0; px < xx; px++) { grid_get_cell(gr->gd, px, py, &gc); - if (gc.data.size != 1 || *gc.data.data != ' ') - break; + if (gc.data.size != 1 || *gc.data.data != ' ') { + gr->cx = px; + gr->cy = py; + return; + } } if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED) break; } + gr->cx = oldx; + gr->cy = oldy; } From 64c276c23bd729c03c2a3ff00c6f9e6ac4c644c6 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:24:10 +0000 Subject: [PATCH 0801/1006] Add an "always" value to the extended-keys option to always forward these keys to applications inside tmux. --- input.c | 2 ++ options-table.c | 6 +++++- screen.c | 4 +++- tmux.1 | 25 +++++++++++++++++++------ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/input.c b/input.c index f6aeb027..b599f27d 100644 --- a/input.c +++ b/input.c @@ -1390,6 +1390,8 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_MODSET: n = input_get(ictx, 0, 0, 0); m = input_get(ictx, 1, 0, 0); + if (options_get_number(global_options, "extended-keys") == 2) + break; if (n == 0 || (n == 4 && m == 0)) screen_write_mode_clear(sctx, MODE_KEXTENDED); else if (n == 4 && (m == 1 || m == 2)) diff --git a/options-table.c b/options-table.c index 8ea56918..4f4d9ff6 100644 --- a/options-table.c +++ b/options-table.c @@ -75,6 +75,9 @@ static const char *options_table_remain_on_exit_list[] = { static const char *options_table_detach_on_destroy_list[] = { "off", "on", "no-detached", NULL }; +static const char *options_table_extended_keys_list[] = { + "off", "on", "always", NULL +}; /* Status line format. */ #define OPTIONS_TABLE_STATUS_FORMAT1 \ @@ -267,8 +270,9 @@ const struct options_table_entry options_table[] = { }, { .name = "extended-keys", - .type = OPTIONS_TABLE_FLAG, + .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SERVER, + .choices = options_table_extended_keys_list, .default_num = 0, .text = "Whether to request extended key sequences from terminals " "that support it." diff --git a/screen.c b/screen.c index 97ccdeec..0b1047ab 100644 --- a/screen.c +++ b/screen.c @@ -101,7 +101,9 @@ screen_reinit(struct screen *s) s->rupper = 0; s->rlower = screen_size_y(s) - 1; - s->mode = MODE_CURSOR | MODE_WRAP; + s->mode = MODE_CURSOR|MODE_WRAP; + if (options_get_number(global_options, "extended-keys") == 2) + s->mode |= MODE_KEXTENDED; if (s->saved_grid != NULL) screen_alternate_off(s, NULL, 0); diff --git a/tmux.1 b/tmux.1 index f5689bfa..146c998f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3418,11 +3418,24 @@ sessions. .Xc If enabled, the server will exit when there are no attached clients. .It Xo Ic extended-keys -.Op Ic on | off +.Op Ic on | off | always .Xc -When enabled, extended keys are requested from the terminal and if supported -are recognised by -.Nm . +When +.Ic on +or +.Ic always , +the escape sequence to enable extended keys is sent to the terminal, if +.Nm +knows that it is supported. +.Nm +always recognises extended keys itself. +If this option is +.Ic on , +.Nm +will only forward extended keys to applications when they request them; if +.Ic always , +.Nm +will always forward the keys. .It Xo Ic focus-events .Op Ic on | off .Xc @@ -3501,8 +3514,8 @@ capabilities to be set instead, is intended for classes of functionality supported in a standard way but not reported by .Xr terminfo 5 . -Care must be taken only to configure this with features the terminal actually -support. +Care must be taken to configure this only with features the terminal actually +supports. .Pp This is an array option where each entry is a colon-separated string made up of a terminal type pattern (matched using From 9f38a8807c6c6b3b7596f06c2bb662242337ac48 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:24:45 +0000 Subject: [PATCH 0802/1006] Include current client in size calculation for new sessions, GitHub issue 2662. --- cmd-new-session.c | 1 + resize.c | 47 +++++++++++++++++++++++++++++++++-------------- status.c | 2 ++ window.c | 2 ++ 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index cc3494de..c47d66cd 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -269,6 +269,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; + sc.tc = c; sc.name = args_get(args, 'n'); sc.argc = args->argc; diff --git a/resize.c b/resize.c index 172cbcb5..66cb9430 100644 --- a/resize.c +++ b/resize.c @@ -108,17 +108,19 @@ clients_with_window(struct window *w) } 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) +clients_calculate_size(int type, int current, struct client *c, + 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) + if (type == WINDOW_SIZE_MANUAL) { + log_debug("%s: type is manual", __func__); return (0); + } /* * Start comparing with 0 for largest and UINT_MAX for smallest or @@ -139,18 +141,24 @@ clients_calculate_size(int type, int current, struct session *s, /* Loop over the clients and work out the size. */ TAILQ_FOREACH(loop, &clients, entry) { - if (ignore_client_size(loop)) + if (loop != c && ignore_client_size(loop)) { + log_debug("%s: ignoring %s", __func__, loop->name); continue; - if (skip_client(loop, type, current, s, w)) + } + if (loop != c && skip_client(loop, type, current, s, w)) { + log_debug("%s: skipping %s", __func__, loop->name); 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) + if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest) { + log_debug("%s: %s is not latest", __func__, loop->name); continue; + } /* Work out this client's size. */ cx = loop->tty.sx; @@ -175,16 +183,24 @@ clients_calculate_size(int type, int current, struct session *s, *xpixel = loop->tty.xpixel; *ypixel = loop->tty.ypixel; } + log_debug("%s: after %s (%ux%u), size is %ux%u", __func__, + loop->name, cx, cy, *sx, *sy); } /* Return whether a suitable size was found. */ - if (type == WINDOW_SIZE_LARGEST) + if (type == WINDOW_SIZE_LARGEST) { + log_debug("%s: type is largest", __func__); return (*sx != 0 && *sy != 0); + } + if (type == WINDOW_SIZE_LATEST) + log_debug("%s: type is latest", __func__); + else + log_debug("%s: type is smallest", __func__); return (*sx != UINT_MAX && *sy != UINT_MAX); } static int -default_window_size_skip_client (struct client *loop, int type, +default_window_size_skip_client(struct client *loop, int type, __unused int current, struct session *s, struct window *w) { /* @@ -221,23 +237,25 @@ default_window_size(struct client *c, struct session *s, struct window *w, *sy = c->tty.sy - status_line_size(c); *xpixel = c->tty.xpixel; *ypixel = c->tty.ypixel; + log_debug("%s: using %ux%u from %s", __func__, *sx, *sy, + c->name); goto done; } - if (w == NULL) - type = WINDOW_SIZE_MANUAL; } /* * 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, + if (!clients_calculate_size(type, 0, c, 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; } + log_debug("%s: using %ux%u from default-size", __func__, *sx, + *sy); } done: @@ -250,6 +268,7 @@ done: *sy = WINDOW_MINIMUM; if (*sy > WINDOW_MAXIMUM) *sy = WINDOW_MAXIMUM; + log_debug("%s: resulting size is %ux%u", __func__, *sx, *sy); } static int @@ -289,7 +308,7 @@ recalculate_size(struct window *w, int now) current = options_get_number(w->options, "aggressive-resize"); /* Look for a suitable client and get the new size. */ - changed = clients_calculate_size(type, current, NULL, w, + changed = clients_calculate_size(type, current, NULL, NULL, w, recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel); /* diff --git a/status.c b/status.c index f9786f4b..271e1afa 100644 --- a/status.c +++ b/status.c @@ -226,6 +226,8 @@ status_line_size(struct client *c) if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) return (0); + if (s == NULL) + return (options_get_number(global_s_options, "status")); return (s->statuslines); } diff --git a/window.c b/window.c index 36cf12d9..519f91b1 100644 --- a/window.c +++ b/window.c @@ -331,6 +331,8 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) window_update_activity(w); + log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy, + w->xpixel, w->ypixel); return (w); } From 0c5cbbbf5cd09598ff2f3d6d1ae99fa353ec5b40 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:28:45 +0000 Subject: [PATCH 0803/1006] Three changes to fix problems with xterm in VT340 mode, reported by Thomas Sattler. 1) Do not include the DECSLRM or DECFRA features for xterm; they will be added instead if secondary DA responds as VT420 (this happens already). 2) Set or reset the individual flags after terminal-overrides is applied, so the user can properly disable them. 3) Add a capability for DECFRA ("Rect"). --- tmux.1 | 4 +++ tmux.h | 1 + tty-features.c | 13 +++++-- tty-term.c | 98 ++++++++++++++++++++++++++++++++------------------ 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/tmux.1 b/tmux.1 index 146c998f..d2ad593f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6049,6 +6049,10 @@ Disable and enable focus reporting. These are set automatically if the .Em XT capability is present. +.It Em \&Rect +Tell +.Nm +that the terminal supports rectangle operations. .It Em \&Smol Enable the overline attribute. .It Em \&Smulx diff --git a/tmux.h b/tmux.h index fb2f35f9..1947009f 100644 --- a/tmux.h +++ b/tmux.h @@ -451,6 +451,7 @@ enum tty_code_code { TTYC_MS, TTYC_OL, TTYC_OP, + TTYC_RECT, TTYC_REV, TTYC_RGB, TTYC_RI, diff --git a/tty-features.c b/tty-features.c index f167a2d3..b42cf74a 100644 --- a/tty-features.c +++ b/tty-features.c @@ -218,9 +218,13 @@ static const struct tty_feature tty_feature_margins = { }; /* Terminal supports DECFRA rectangle fill. */ +static const char *tty_feature_rectfill_capabilities[] = { + "Rect", + NULL +}; static const struct tty_feature tty_feature_rectfill = { "rectfill", - NULL, + tty_feature_rectfill_capabilities, TERM_DECFRA }; @@ -351,8 +355,13 @@ tty_default_features(int *feat, const char *name, u_int version) ",cstyle,extkeys,margins,sync" }, { .name = "XTerm", + /* + * xterm also supports DECSLRM and DECFRA, but they can be + * disabled so not set it here - they will be added if + * secondary DA shows VT420. + */ .features = TTY_FEATURES_BASE_MODERN_XTERM - ",ccolour,cstyle,extkeys,focus,margins,rectfill" + ",ccolour,cstyle,extkeys,focus" } }; u_int i; diff --git a/tty-term.c b/tty-term.c index 9df50948..275efe2f 100644 --- a/tty-term.c +++ b/tty-term.c @@ -248,6 +248,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_MS] = { TTYCODE_STRING, "Ms" }, [TTYC_OL] = { TTYCODE_STRING, "ol" }, [TTYC_OP] = { TTYCODE_STRING, "op" }, + [TTYC_RECT] = { TTYCODE_STRING, "Rect" }, [TTYC_REV] = { TTYCODE_STRING, "rev" }, [TTYC_RGB] = { TTYCODE_FLAG, "RGB" }, [TTYC_RIN] = { TTYCODE_STRING, "rin" }, @@ -431,10 +432,11 @@ tty_term_apply_overrides(struct tty_term *term) struct options_entry *o; struct options_array_item *a; union options_value *ov; - const char *s; + const char *s, *acs; size_t offset; char *first; + /* Update capabilities from the option. */ o = options_get_only(global_options, "terminal-overrides"); a = options_array_first(o); while (a != NULL) { @@ -447,6 +449,64 @@ tty_term_apply_overrides(struct tty_term *term) tty_term_apply(term, s + offset, 0); a = options_array_next(a); } + + /* Update the RGB flag if the terminal has RGB colours. */ + if (tty_term_has(term, TTYC_SETRGBF) && + tty_term_has(term, TTYC_SETRGBB)) + term->flags |= TERM_RGBCOLOURS; + else + term->flags &= ~TERM_RGBCOLOURS; + log_debug("RGBCOLOURS flag is %d", !!(term->flags & TERM_RGBCOLOURS)); + + /* + * Set or clear the DECSLRM flag if the terminal has the margin + * capabilities. + */ + if (tty_term_has(term, TTYC_CMG) && tty_term_has(term, TTYC_CLMG)) + term->flags |= TERM_DECSLRM; + else + term->flags &= ~TERM_DECSLRM; + log_debug("DECSLRM flag is %d", !!(term->flags & TERM_DECSLRM)); + + /* + * Set or clear the DECFRA flag if the terminal has the rectangle + * capability. + */ + if (tty_term_has(term, TTYC_RECT)) + term->flags |= TERM_DECFRA; + else + term->flags &= ~TERM_DECFRA; + log_debug("DECFRA flag is %d", !!(term->flags & TERM_DECFRA)); + + /* + * Terminals without am (auto right margin) wrap at at $COLUMNS - 1 + * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). + * + * Terminals without xenl (eat newline glitch) ignore a newline beyond + * the right edge of the terminal, but tmux doesn't care about this - + * it always uses absolute only moves the cursor with a newline when + * also sending a linefeed. + * + * This is irritating, most notably because it is painful to write to + * the very bottom-right of the screen without scrolling. + * + * Flag the terminal here and apply some workarounds in other places to + * do the best possible. + */ + if (!tty_term_flag(term, TTYC_AM)) + term->flags |= TERM_NOAM; + else + term->flags &= ~TERM_NOAM; + log_debug("NOAM flag is %d", !!(term->flags & TERM_NOAM)); + + /* Generate ACS table. If none is present, use nearest ASCII. */ + memset(term->acs, 0, sizeof term->acs); + if (tty_term_has(term, TTYC_ACSC)) + acs = tty_term_string(term, TTYC_ACSC); + else + acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y~."; + for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) + term->acs[(u_char) acs[0]][0] = acs[1]; } struct tty_term * @@ -460,7 +520,7 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps, struct options_array_item *a; union options_value *ov; u_int i, j; - const char *s, *acs, *value; + const char *s, *value; size_t offset, namelen; char *first; @@ -557,40 +617,10 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps, (!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); - tty_term_apply_overrides(term); - - /* - * Terminals without am (auto right margin) wrap at at $COLUMNS - 1 - * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). - * - * Terminals without xenl (eat newline glitch) ignore a newline beyond - * the right edge of the terminal, but tmux doesn't care about this - - * it always uses absolute only moves the cursor with a newline when - * also sending a linefeed. - * - * This is irritating, most notably because it is painful to write to - * the very bottom-right of the screen without scrolling. - * - * Flag the terminal here and apply some workarounds in other places to - * do the best possible. - */ - if (!tty_term_flag(term, TTYC_AM)) - term->flags |= TERM_NOAM; - - /* Generate ACS table. If none is present, use nearest ASCII. */ - memset(term->acs, 0, sizeof term->acs); - if (tty_term_has(term, TTYC_ACSC)) - acs = tty_term_string(term, TTYC_ACSC); - else - acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y~."; - for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) - term->acs[(u_char) acs[0]][0] = acs[1]; + if (tty_apply_features(term, *feat)) + tty_term_apply_overrides(term); /* Log the capabilities. */ for (i = 0; i < tty_term_ncodes(); i++) From b573dbba9062fa9db3b60310cdb4d8776a0be3fd Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:29:45 +0000 Subject: [PATCH 0804/1006] Do not count client (and crash) if no window. --- resize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resize.c b/resize.c index 66cb9430..38cda23d 100644 --- a/resize.c +++ b/resize.c @@ -136,7 +136,7 @@ clients_calculate_size(int type, int current, struct client *c, * For latest, count the number of clients with this window. We only * care if there is more than one. */ - if (type == WINDOW_SIZE_LATEST) + if (type == WINDOW_SIZE_LATEST && w != NULL) n = clients_with_window(w); /* Loop over the clients and work out the size. */ From 84e22168a52f8fe0fa0ef0431d1752273f53e8bd Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:33:41 +0000 Subject: [PATCH 0805/1006] Change resize timers and flags into one timer and a queue which is simpler and fixes problems with vim when resized multiple times. GitHub issue 2677. --- server-client.c | 117 +++++++++++++++++++++++------------------------- tmux.1 | 2 + tmux.h | 25 ++++++++--- window.c | 51 +++++++++------------ 4 files changed, 96 insertions(+), 99 deletions(-) diff --git a/server-client.c b/server-client.c index 1114396d..9bd2ad19 100644 --- a/server-client.c +++ b/server-client.c @@ -1450,84 +1450,79 @@ server_client_resize_timer(__unused int fd, __unused short events, void *data) evtimer_del(&wp->resize_timer); } -/* Start the resize timer. */ -static void -server_client_start_resize_timer(struct window_pane *wp) -{ - struct timeval tv = { .tv_usec = 250000 }; - - log_debug("%s: %%%u resize timer started", __func__, wp->id); - evtimer_add(&wp->resize_timer, &tv); -} - -/* Force timer event. */ -static void -server_client_force_timer(__unused int fd, __unused short events, void *data) -{ - struct window_pane *wp = data; - - log_debug("%s: %%%u force timer expired", __func__, wp->id); - evtimer_del(&wp->force_timer); - wp->flags |= PANE_RESIZENOW; -} - -/* Start the force timer. */ -static void -server_client_start_force_timer(struct window_pane *wp) -{ - struct timeval tv = { .tv_usec = 10000 }; - - log_debug("%s: %%%u force timer started", __func__, wp->id); - evtimer_add(&wp->force_timer, &tv); -} - /* Check if pane should be resized. */ static void server_client_check_pane_resize(struct window_pane *wp) { + struct window_pane_resize *r; + struct window_pane_resize *r1; + struct window_pane_resize *first; + struct window_pane_resize *last; + struct timeval tv = { .tv_usec = 250000 }; + + if (TAILQ_EMPTY(&wp->resize_queue)) + return; + if (!event_initialized(&wp->resize_timer)) evtimer_set(&wp->resize_timer, server_client_resize_timer, wp); - if (!event_initialized(&wp->force_timer)) - evtimer_set(&wp->force_timer, server_client_force_timer, wp); - - if (~wp->flags & PANE_RESIZE) + if (evtimer_pending(&wp->resize_timer, NULL)) return; + log_debug("%s: %%%u needs to be resized", __func__, wp->id); - - if (evtimer_pending(&wp->resize_timer, NULL)) { - log_debug("%s: %%%u resize timer is running", __func__, wp->id); - return; + TAILQ_FOREACH(r, &wp->resize_queue, entry) { + log_debug("queued resize: %ux%u -> %ux%u", r->osx, r->osy, + r->sx, r->sy); } - server_client_start_resize_timer(wp); - if (~wp->flags & PANE_RESIZEFORCE) { - /* - * The timer is not running and we don't need to force a - * resize, so just resize immediately. - */ - log_debug("%s: resizing %%%u now", __func__, wp->id); - window_pane_send_resize(wp, 0); - wp->flags &= ~PANE_RESIZE; + /* + * There are three cases that matter: + * + * - Only one resize. It can just be applied. + * + * - Multiple resizes and the ending size is different from the + * starting size. We can discard all resizes except the most recent. + * + * - Multiple resizes and the ending size is the same as the starting + * size. We must resize at least twice to force the application to + * redraw. So apply the first and leave the last on the queue for + * next time. + */ + first = TAILQ_FIRST(&wp->resize_queue); + last = TAILQ_LAST(&wp->resize_queue, window_pane_resizes); + if (first == last) { + /* Only one resize. */ + window_pane_send_resize(wp, first->sx, first->sy); + TAILQ_REMOVE(&wp->resize_queue, first, entry); + free(first); + } else if (last->sx != first->osx || last->sy != first->osy) { + /* Multiple resizes ending up with a different size. */ + window_pane_send_resize(wp, last->sx, last->sy); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); + } } else { /* - * The timer is not running, but we need to force a resize. If - * the force timer has expired, resize to the real size now. - * Otherwise resize to the force size and start the timer. + * Multiple resizes ending up with the same size. There will + * not be more than one to the same size in succession so we + * can just use the last-but-one on the list and leave the last + * for later. We reduce the time until the next check to avoid + * a long delay between the resizes. */ - if (wp->flags & PANE_RESIZENOW) { - log_debug("%s: resizing %%%u after forced resize", - __func__, wp->id); - window_pane_send_resize(wp, 0); - wp->flags &= ~(PANE_RESIZE|PANE_RESIZEFORCE|PANE_RESIZENOW); - } else if (!evtimer_pending(&wp->force_timer, NULL)) { - log_debug("%s: forcing resize of %%%u", __func__, - wp->id); - window_pane_send_resize(wp, 1); - server_client_start_force_timer(wp); + r = TAILQ_PREV(last, window_pane_resizes, entry); + window_pane_send_resize(wp, r->sx, r->sy); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + if (r == last) + break; + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); } + tv.tv_usec = 10000; } + evtimer_add(&wp->resize_timer, &tv); } + /* Check pane buffer size. */ static void server_client_check_pane_buffer(struct window_pane *wp) diff --git a/tmux.1 b/tmux.1 index d2ad593f..47cf4f2c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2953,6 +2953,8 @@ Ctrl keys may be prefixed with .Ql C- or .Ql ^ , +Shift keys with +.Ql S- and Alt (meta) with .Ql M- . In addition, the following special key names are accepted: diff --git a/tmux.h b/tmux.h index 1947009f..508fddd3 100644 --- a/tmux.h +++ b/tmux.h @@ -918,7 +918,7 @@ struct window_mode_entry { struct screen *screen; u_int prefix; - TAILQ_ENTRY (window_mode_entry) entry; + TAILQ_ENTRY(window_mode_entry) entry; }; /* Offsets into pane buffer. */ @@ -926,6 +926,18 @@ struct window_pane_offset { size_t used; }; +/* Queued pane resize. */ +struct window_pane_resize { + u_int sx; + u_int sy; + + u_int osx; + u_int osy; + + TAILQ_ENTRY(window_pane_resize) entry; +}; +TAILQ_HEAD(window_pane_resizes, window_pane_resize); + /* Child window structure. */ struct window_pane { u_int id; @@ -950,8 +962,8 @@ struct window_pane { #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 -#define PANE_RESIZE 0x8 -#define PANE_RESIZEFORCE 0x10 +/* 0x8 unused */ +/* 0x10 unused */ #define PANE_FOCUSPUSH 0x20 #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 @@ -960,7 +972,6 @@ struct window_pane { #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 #define PANE_STYLECHANGED 0x1000 -#define PANE_RESIZENOW 0x2000 int argc; char **argv; @@ -977,8 +988,8 @@ struct window_pane { struct window_pane_offset offset; size_t base_offset; + struct window_pane_resizes resize_queue; struct event resize_timer; - struct event force_timer; struct input_ctx *ictx; @@ -996,7 +1007,7 @@ struct window_pane { struct screen status_screen; size_t status_size; - TAILQ_HEAD (, window_mode_entry) modes; + TAILQ_HEAD(, window_mode_entry) modes; char *searchstr; int searchregex; @@ -2755,7 +2766,7 @@ void window_redraw_active_switch(struct window *, struct window_pane *window_add_pane(struct window *, struct window_pane *, u_int, int); void window_resize(struct window *, u_int, u_int, int, int); -void window_pane_send_resize(struct window_pane *, int); +void window_pane_send_resize(struct window_pane *, u_int, u_int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); int window_push_zoom(struct window *, int, int); diff --git a/window.c b/window.c index 519f91b1..aa448e1f 100644 --- a/window.c +++ b/window.c @@ -427,25 +427,18 @@ window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) } void -window_pane_send_resize(struct window_pane *wp, int force) +window_pane_send_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window *w = wp->window; struct winsize ws; - u_int sy; if (wp->fd == -1) return; - if (!force) - sy = wp->sy; - else if (wp->sy <= 1) - sy = wp->sy + 1; - else - sy = wp->sy - 1; - log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, sy); + log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, sx, sy); memset(&ws, 0, sizeof ws); - ws.ws_col = wp->sx; + ws.ws_col = sx; ws.ws_row = sy; ws.ws_xpixel = w->xpixel * ws.ws_col; ws.ws_ypixel = w->ypixel * ws.ws_row; @@ -860,29 +853,19 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); - wp->argc = 0; - wp->argv = NULL; - wp->shell = NULL; - wp->cwd = NULL; - wp->fd = -1; - wp->event = NULL; wp->fg = 8; wp->bg = 8; TAILQ_INIT(&wp->modes); - wp->layout_cell = NULL; - - wp->xoff = 0; - wp->yoff = 0; + TAILQ_INIT (&wp->resize_queue); wp->sx = sx; wp->sy = sy; wp->pipe_fd = -1; - wp->pipe_event = NULL; screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; @@ -898,6 +881,9 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) static void window_pane_destroy(struct window_pane *wp) { + struct window_pane_resize *r; + struct window_pane_resize *r1; + window_pane_reset_mode_all(wp); free(wp->searchstr); @@ -919,8 +905,10 @@ window_pane_destroy(struct window_pane *wp) if (event_initialized(&wp->resize_timer)) event_del(&wp->resize_timer); - if (event_initialized(&wp->force_timer)) - event_del(&wp->force_timer); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); + } RB_REMOVE(window_pane_tree, &all_window_panes, wp); @@ -989,9 +977,18 @@ void window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window_mode_entry *wme; + struct window_pane_resize *r; if (sx == wp->sx && sy == wp->sy) return; + + r = xmalloc (sizeof *r); + r->sx = sx; + r->sy = sy; + r->osx = wp->sx; + r->osy = wp->sy; + TAILQ_INSERT_TAIL (&wp->resize_queue, r, entry); + wp->sx = sx; wp->sy = sy; @@ -1001,14 +998,6 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); - - /* - * If the pane has already been resized, set the force flag and make - * the application resize twice to force it to redraw. - */ - if (wp->flags & PANE_RESIZE) - wp->flags |= PANE_RESIZEFORCE; - wp->flags |= PANE_RESIZE; } void From f9f97c8145e97b0295f0792a643286126fe2d3f8 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:36:47 +0000 Subject: [PATCH 0806/1006] Change cursor style handling so tmux understands which sequences contain blinking and sets the flag appropriately, means that it works whether cnorm disables blinking or not. GitHub issue 2682. --- screen.c | 32 +++++++++++++-- tmux.h | 14 +++++-- tty.c | 117 +++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 120 insertions(+), 43 deletions(-) diff --git a/screen.c b/screen.c index 0b1047ab..2d770abb 100644 --- a/screen.c +++ b/screen.c @@ -81,7 +81,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->titles = NULL; s->path = NULL; - s->cstyle = 0; + s->cstyle = SCREEN_CURSOR_DEFAULT; s->ccolour = xstrdup(""); s->tabs = NULL; s->sel = NULL; @@ -156,9 +156,35 @@ screen_reset_tabs(struct screen *s) void screen_set_cursor_style(struct screen *s, u_int style) { - if (style <= 6) { - s->cstyle = style; + switch (style) + { + case 0: + s->cstyle = SCREEN_CURSOR_DEFAULT; + break; + case 1: + s->cstyle = SCREEN_CURSOR_BLOCK; + s->mode |= MODE_BLINKING; + break; + case 2: + s->cstyle = SCREEN_CURSOR_BLOCK; s->mode &= ~MODE_BLINKING; + break; + case 3: + s->cstyle = SCREEN_CURSOR_UNDERLINE; + s->mode |= MODE_BLINKING; + break; + case 4: + s->cstyle = SCREEN_CURSOR_UNDERLINE; + s->mode &= ~MODE_BLINKING; + break; + case 5: + s->cstyle = SCREEN_CURSOR_BAR; + s->mode |= MODE_BLINKING; + break; + case 6: + s->cstyle = SCREEN_CURSOR_BAR; + s->mode &= ~MODE_BLINKING; + break; } } diff --git a/tmux.h b/tmux.h index 508fddd3..96d829fb 100644 --- a/tmux.h +++ b/tmux.h @@ -794,6 +794,14 @@ struct style { enum style_default_type default_type; }; +/* Cursor style. */ +enum screen_cursor_style { + SCREEN_CURSOR_DEFAULT, + SCREEN_CURSOR_BLOCK, + SCREEN_CURSOR_UNDERLINE, + SCREEN_CURSOR_BAR +}; + /* Virtual screen. */ struct screen_sel; struct screen_titles; @@ -807,8 +815,8 @@ struct screen { u_int cx; /* cursor x */ u_int cy; /* cursor y */ - u_int cstyle; /* cursor style */ - char *ccolour; /* cursor colour string */ + enum screen_cursor_style cstyle; /* cursor style */ + char *ccolour; /* cursor colour */ u_int rupper; /* scroll region top */ u_int rlower; /* scroll region bottom */ @@ -1296,7 +1304,7 @@ struct tty { u_int cx; u_int cy; - u_int cstyle; + enum screen_cursor_style cstyle; char *ccolour; int oflag; diff --git a/tty.c b/tty.c index e8a8cbaa..367f54d5 100644 --- a/tty.c +++ b/tty.c @@ -98,7 +98,7 @@ tty_init(struct tty *tty, struct client *c) memset(tty, 0, sizeof *tty); tty->client = c; - tty->cstyle = 0; + tty->cstyle = SCREEN_CURSOR_DEFAULT; tty->ccolour = xstrdup(""); if (tcgetattr(c->fd, &tty->tio) != 0) @@ -392,10 +392,10 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); - if (tty_term_has(tty->term, TTYC_SS) && tty->cstyle != 0) { + if (tty->cstyle != SCREEN_CURSOR_DEFAULT) { if (tty_term_has(tty->term, TTYC_SE)) tty_raw(tty, tty_term_string(tty->term, TTYC_SE)); - else + else if (tty_term_has(tty->term, TTYC_SS)) tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); } if (tty->mode & MODE_BRACKETPASTE) @@ -657,11 +657,9 @@ tty_force_cursor_colour(struct tty *tty, const char *ccolour) void tty_update_mode(struct tty *tty, int mode, struct screen *s) { - struct client *c = tty->client; - int changed; - - if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) - tty_force_cursor_colour(tty, s->ccolour); + struct client *c = tty->client; + int changed; + enum screen_cursor_style cstyle = tty->cstyle; if (tty->flags & TTY_NOCURSOR) mode &= ~MODE_CURSOR; @@ -670,38 +668,83 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) if (changed != 0) log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); - /* - * The cursor blinking flag can be reset by setting the cursor style, so - * set the style first. - */ - if (s != NULL && tty->cstyle != s->cstyle) { - if (tty_term_has(tty->term, TTYC_SS)) { - if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) - tty_putcode(tty, TTYC_SE); - else - tty_putcode1(tty, TTYC_SS, s->cstyle); - } - tty->cstyle = s->cstyle; - changed |= (MODE_CURSOR|MODE_BLINKING); + if (s != NULL) { + if (strcmp(s->ccolour, tty->ccolour) != 0) + tty_force_cursor_colour(tty, s->ccolour); + cstyle = s->cstyle; } - - /* - * Cursor invisible (RM ?25) overrides cursor blinking (SM ?12 or RM - * 34), and we need to be careful not send cnorm after cvvis since it - * can undo it. - */ - if (changed & (MODE_CURSOR|MODE_BLINKING)) { - log_debug("%s: cursor %s, %sblinking", __func__, - (mode & MODE_CURSOR) ? "on" : "off", - (mode & MODE_BLINKING) ? "" : "not "); - if (~mode & MODE_CURSOR) + if (~mode & MODE_CURSOR) { + /* Cursor now off - set as invisible. */ + if (changed & MODE_CURSOR) tty_putcode(tty, TTYC_CIVIS); - else if (mode & MODE_BLINKING) { - tty_putcode(tty, TTYC_CNORM); - if (tty_term_has(tty->term, TTYC_CVVIS)) + } else if ((changed & (MODE_CURSOR|MODE_BLINKING)) || + cstyle != tty->cstyle) { + /* + * Cursor now on, blinking flag changed or style changed. Start + * by setting the cursor to normal. + */ + tty_putcode(tty, TTYC_CNORM); + switch (cstyle) { + case SCREEN_CURSOR_DEFAULT: + /* + * If the old style wasn't default, then reset it to + * default. + */ + if (tty->cstyle != SCREEN_CURSOR_DEFAULT) { + if (tty_term_has(tty->term, TTYC_SE)) + tty_putcode(tty, TTYC_SE); + else + tty_putcode1(tty, TTYC_SS, 0); + } + + /* Set the cursor as very visible if necessary. */ + if (mode & MODE_BLINKING) tty_putcode(tty, TTYC_CVVIS); - } else - tty_putcode(tty, TTYC_CNORM); + break; + case SCREEN_CURSOR_BLOCK: + /* + * Set style to either block blinking (1) or steady (2) + * if supported, otherwise just check the blinking + * flag. + */ + if (tty_term_has(tty->term, TTYC_SS)) { + if (mode & MODE_BLINKING) + tty_putcode1(tty, TTYC_SS, 1); + else + tty_putcode1(tty, TTYC_SS, 2); + } else if (mode & MODE_BLINKING) + tty_putcode(tty, TTYC_CVVIS); + break; + case SCREEN_CURSOR_UNDERLINE: + /* + * Set style to either underline blinking (3) or steady + * (4) if supported, otherwise just check the blinking + * flag. + */ + if (tty_term_has(tty->term, TTYC_SS)) { + if (mode & MODE_BLINKING) + tty_putcode1(tty, TTYC_SS, 3); + else + tty_putcode1(tty, TTYC_SS, 4); + } else if (mode & MODE_BLINKING) + tty_putcode(tty, TTYC_CVVIS); + break; + case SCREEN_CURSOR_BAR: + /* + * Set style to either bar blinking (5) or steady (6) + * if supported, otherwise just check the blinking + * flag. + */ + if (tty_term_has(tty->term, TTYC_SS)) { + if (mode & MODE_BLINKING) + tty_putcode1(tty, TTYC_SS, 5); + else + tty_putcode1(tty, TTYC_SS, 6); + } else if (mode & MODE_BLINKING) + tty_putcode(tty, TTYC_CVVIS); + break; + } + tty->cstyle = cstyle; } if ((changed & ALL_MOUSE_MODES) && From f02a6c34e0e8bf2c50ee1dfe9cfacf54370a1405 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:38:28 +0000 Subject: [PATCH 0807/1006] Move "special" keys into the Unicode PUA rather than making them top bit set, some compilers do not allow enums that are larger than int. GitHub issue 2673. --- input-keys.c | 2 +- key-string.c | 6 +++--- status.c | 2 +- tmux.h | 33 ++++++++++++++++++++++++--------- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/input-keys.c b/input-keys.c index ab7d2212..b975c1ed 100644 --- a/input-keys.c +++ b/input-keys.c @@ -477,7 +477,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) input_key_write(__func__, bev, &ud.data[0], 1); return (0); } - if (justkey > 0x7f && justkey < KEYC_BASE) { + if (KEYC_IS_UNICODE(justkey)) { if (key & KEYC_META) input_key_write(__func__, bev, "\033", 1); utf8_to_data(justkey, &ud); diff --git a/key-string.c b/key-string.c index c24a33fc..406d26dd 100644 --- a/key-string.c +++ b/key-string.c @@ -238,7 +238,7 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (key < KEYC_BASE && + if (key <= 127 && (modifiers & KEYC_CTRL) && strchr(other, key) == NULL && key != 9 && @@ -368,8 +368,8 @@ key_string_lookup_key(key_code key, int with_flags) goto out; } - /* Is this a UTF-8 key? */ - if (key > 127 && key < KEYC_BASE) { + /* Is this a Unicode key? */ + if (KEYC_IS_UNICODE(key)) { utf8_to_data(key, &ud); off = strlen(out); memcpy(out + off, ud.data, ud.size); diff --git a/status.c b/status.c index 271e1afa..f4418500 100644 --- a/status.c +++ b/status.c @@ -1300,7 +1300,7 @@ process_key: return (0); append_key: - if (key <= 0x1f || key >= KEYC_BASE) + if (key <= 0x1f || (key >= KEYC_BASE && key < KEYC_BASE_END)) return (0); if (key <= 0x7f) utf8_set(&tmp, key); diff --git a/tmux.h b/tmux.h index 96d829fb..02dcdad0 100644 --- a/tmux.h +++ b/tmux.h @@ -108,11 +108,16 @@ struct winlink; #define VISUAL_ON 1 #define VISUAL_BOTH 2 -/* Special key codes. */ -#define KEYC_NONE 0x00ff000000000ULL -#define KEYC_UNKNOWN 0x00fe000000000ULL -#define KEYC_BASE 0x0001000000000ULL -#define KEYC_USER 0x0002000000000ULL +/* No key or unknown key. */ +#define KEYC_NONE 0x000ff000000000ULL +#define KEYC_UNKNOWN 0x000fe000000000ULL + +/* + * Base for special (that is, not Unicode) keys. An enum must be at most a + * signed int, so these are based in the highest Unicode PUA. + */ +#define KEYC_BASE 0x0000000010e000ULL +#define KEYC_USER 0x0000000010f000ULL /* Key modifier bits. */ #define KEYC_META 0x00100000000000ULL @@ -135,8 +140,15 @@ struct winlink; #define KEYC_NUSER 1000 /* Is this a mouse key? */ -#define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ - ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) +#define KEYC_IS_MOUSE(key) \ + (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ + ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) + +/* Is this a Unicode key? */ +#define KEYC_IS_UNICODE(key) \ + (((key) & KEYC_MASK_KEY) > 0x7f && \ + (((key) & KEYC_MASK_KEY) < KEYC_BASE || \ + ((key) & KEYC_MASK_KEY) >= KEYC_BASE_END)) /* Multiple click timeout. */ #define KEYC_CLICK_TIMEOUT 300 @@ -158,8 +170,8 @@ struct winlink; { #s "Border", KEYC_ ## name ## _BORDER } /* - * A single key. This can be ASCII or Unicode or one of the keys starting at - * KEYC_BASE. + * A single key. This can be ASCII or Unicode or one of the keys between + * KEYC_BASE and KEYC_BASE_END. */ typedef unsigned long long key_code; @@ -252,6 +264,9 @@ enum { KEYC_KP_ENTER, KEYC_KP_ZERO, KEYC_KP_PERIOD, + + /* End of special keys. */ + KEYC_BASE_END }; /* Termcap codes. */ From 02a6b39db73e55f9f30d46106c378ed4c70cb02b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:43:44 +0000 Subject: [PATCH 0808/1006] Improve logging of screen mode changes. --- screen-write.c | 6 ++++++ screen.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++-- server-client.c | 5 ++++- tmux.h | 1 + tty.c | 8 ++++++-- 5 files changed, 67 insertions(+), 5 deletions(-) diff --git a/screen-write.c b/screen-write.c index 8e9ec582..e351a5e5 100644 --- a/screen-write.c +++ b/screen-write.c @@ -769,6 +769,9 @@ screen_write_mode_set(struct screen_write_ctx *ctx, int mode) struct screen *s = ctx->s; s->mode |= mode; + + if (log_get_level() != 0) + log_debug("%s: %s", __func__, screen_mode_to_string(mode)); } /* Clear a mode. */ @@ -778,6 +781,9 @@ screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) struct screen *s = ctx->s; s->mode &= ~mode; + + if (log_get_level() != 0) + log_debug("%s: %s", __func__, screen_mode_to_string(mode)); } /* Cursor up by ny. */ diff --git a/screen.c b/screen.c index 2d770abb..e7ee1df9 100644 --- a/screen.c +++ b/screen.c @@ -156,8 +156,8 @@ screen_reset_tabs(struct screen *s) void screen_set_cursor_style(struct screen *s, u_int style) { - switch (style) - { + log_debug("%s: new %u, was %u", __func__, style, s->cstyle); + switch (style) { case 0: s->cstyle = SCREEN_CURSOR_DEFAULT; break; @@ -653,3 +653,51 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) if (s->cy > screen_size_y(s) - 1) s->cy = screen_size_y(s) - 1; } + +/* Get mode as a string. */ +const char * +screen_mode_to_string(int mode) +{ + static char tmp[1024]; + + if (mode == 0) + return "NONE"; + if (mode == ALL_MODES) + return "ALL"; + + *tmp = '\0'; + if (mode & MODE_CURSOR) + strlcat(tmp, "CURSOR,", sizeof tmp); + if (mode & MODE_INSERT) + strlcat(tmp, "INSERT,", sizeof tmp); + if (mode & MODE_KCURSOR) + strlcat(tmp, "KCURSOR,", sizeof tmp); + if (mode & MODE_KKEYPAD) + strlcat(tmp, "KKEYPAD,", sizeof tmp); + if (mode & MODE_WRAP) + strlcat(tmp, "WRAP,", sizeof tmp); + if (mode & MODE_MOUSE_STANDARD) + strlcat(tmp, "STANDARD,", sizeof tmp); + if (mode & MODE_MOUSE_BUTTON) + strlcat(tmp, "BUTTON,", sizeof tmp); + if (mode & MODE_BLINKING) + strlcat(tmp, "BLINKING,", sizeof tmp); + if (mode & MODE_MOUSE_UTF8) + strlcat(tmp, "UTF8,", sizeof tmp); + if (mode & MODE_MOUSE_SGR) + strlcat(tmp, "SGR,", sizeof tmp); + if (mode & MODE_BRACKETPASTE) + strlcat(tmp, "BRACKETPASTE,", sizeof tmp); + if (mode & MODE_FOCUSON) + strlcat(tmp, "FOCUSON,", sizeof tmp); + if (mode & MODE_MOUSE_ALL) + strlcat(tmp, "ALL,", sizeof tmp); + if (mode & MODE_ORIGIN) + strlcat(tmp, "ORIGIN,", sizeof tmp); + if (mode & MODE_CRLF) + strlcat(tmp, "CRLF,", sizeof tmp); + if (mode & MODE_KEXTENDED) + strlcat(tmp, "KEXTENDED,", sizeof tmp); + tmp[strlen (tmp) - 1] = '\0'; + return (tmp); +} diff --git a/server-client.c b/server-client.c index 9bd2ad19..27b264a0 100644 --- a/server-client.c +++ b/server-client.c @@ -1694,7 +1694,10 @@ server_client_reset_state(struct client *c) s = wp->screen; if (s != NULL) mode = s->mode; - log_debug("%s: client %s mode %x", __func__, c->name, mode); + if (log_get_level() != 0) { + log_debug("%s: client %s mode %s", __func__, c->name, + screen_mode_to_string(mode)); + } /* Reset region and margin. */ tty_region_off(tty); diff --git a/tmux.h b/tmux.h index 02dcdad0..fbe19136 100644 --- a/tmux.h +++ b/tmux.h @@ -2749,6 +2749,7 @@ void screen_select_cell(struct screen *, struct grid_cell *, const struct grid_cell *); void screen_alternate_on(struct screen *, struct grid_cell *, int); void screen_alternate_off(struct screen *, struct grid_cell *, int); +const char *screen_mode_to_string(int); /* window.c */ extern struct windows windows; diff --git a/tty.c b/tty.c index 367f54d5..45868fc1 100644 --- a/tty.c +++ b/tty.c @@ -665,8 +665,12 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; - if (changed != 0) - log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); + if (log_get_level() != 0 && changed != 0) { + log_debug("%s: current mode %s", c->name, + screen_mode_to_string(tty->mode)); + log_debug("%s: setting mode %s", c->name, + screen_mode_to_string(mode)); + } if (s != NULL) { if (strcmp(s->ccolour, tty->ccolour) != 0) From 6c659494f5591ebba2ef91e6cfa6a24761c14915 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:45:43 +0000 Subject: [PATCH 0809/1006] Fix warnings, from Jan Tache in GitHub issue 2692. --- format.c | 2 +- server.c | 3 ++- tty-term.c | 2 +- window-buffer.c | 6 +++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/format.c b/format.c index 3a1385b2..03c01dc4 100644 --- a/format.c +++ b/format.c @@ -4199,7 +4199,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, value = xstrdup("0"); } else { format_log(es, "search '%s' pane %%%u", new, wp->id); - value = format_search(fm, wp, new); + value = format_search(search, wp, new); } free(new); } else if (cmp != NULL) { diff --git a/server.c b/server.c index 6ac08a4d..eb6e9610 100644 --- a/server.c +++ b/server.c @@ -161,7 +161,8 @@ server_tidy_event(__unused int fd, __unused short events, __unused void *data) format_tidy_jobs(); - log_debug("%s: took %llu milliseconds", __func__, get_timer() - t); + log_debug("%s: took %llu milliseconds", __func__, + (unsigned long long)(get_timer() - t)); evtimer_add(&server_ev_tidy, &tv); } diff --git a/tty-term.c b/tty-term.c index 275efe2f..ae103fd7 100644 --- a/tty-term.c +++ b/tty-term.c @@ -688,7 +688,7 @@ tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps, ent = &tty_term_codes[i]; switch (ent->type) { case TTYCODE_NONE: - break; + continue; case TTYCODE_STRING: s = tigetstr((char *)ent->name); if (s == NULL || s == (char *)-1) diff --git a/window-buffer.c b/window-buffer.c index 5e4dd699..30bd5092 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -294,9 +294,9 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line) struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item = itemdata; struct format_tree *ft; - struct session *s; - struct winlink *wl; - struct window_pane *wp; + struct session *s = NULL; + struct winlink *wl = NULL; + struct window_pane *wp = NULL; struct paste_buffer *pb; char *expanded; key_code key; From 866117636e47c9a5d961df2ac40456b7c910a932 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:50:03 +0000 Subject: [PATCH 0810/1006] Add different command historys for different types of prompts ("command", "search" etc). From Anindya Mukherjee. --- Makefile | 1 + cmd-command-prompt.c | 39 ++++---- cmd-confirm-before.c | 2 +- cmd-show-prompt-history.c | 108 ++++++++++++++++++++++ cmd.c | 4 + key-bindings.c | 12 +-- mode-tree.c | 4 +- options-table.c | 9 ++ status.c | 184 +++++++++++++++++++++++++++----------- tmux.1 | 47 ++++++++-- tmux.h | 22 +++-- window-customize.c | 14 +-- window-tree.c | 6 +- 13 files changed, 356 insertions(+), 96 deletions(-) create mode 100644 cmd-show-prompt-history.c diff --git a/Makefile b/Makefile index 21141317..4cbbd34a 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,7 @@ SRCS= alerts.c \ cmd-show-environment.c \ cmd-show-messages.c \ cmd-show-options.c \ + cmd-show-prompt-history.c \ cmd-source-file.c \ cmd-split-window.c \ cmd-swap-pane.c \ diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index c82235c5..c2a2dec7 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,25 +40,26 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1kiI:Np:Tt:W", 0, 1 }, - .usage = "[-1kiNTW] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " - "[template]", + .args = { "1kiI:Np:t:T:", 0, 1 }, + .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE + " [-T type] [template]", .flags = CMD_CLIENT_TFLAG, .exec = cmd_command_prompt_exec }; struct cmd_command_prompt_cdata { - int flags; + int flags; + enum prompt_type prompt_type; - char *inputs; - char *next_input; + char *inputs; + char *next_input; - char *prompts; - char *next_prompt; + char *prompts; + char *next_prompt; - char *template; - int idx; + char *template; + int idx; }; static enum cmd_retval @@ -67,7 +68,7 @@ cmd_command_prompt_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_find_state *target = cmdq_get_target(item); - const char *inputs, *prompts; + const char *inputs, *prompts, *type; struct cmd_command_prompt_cdata *cdata; char *prompt, *ptr, *input = NULL; size_t n; @@ -114,6 +115,16 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) input = strsep(&cdata->next_input, ","); } + /* Get prompt type. */ + if ((type = args_get(args, 'T')) != NULL) { + cdata->prompt_type = status_prompt_type(type); + if (cdata->prompt_type == PROMPT_TYPE_INVALID) { + cmdq_error(item, "unknown type: %s", type); + return (CMD_RETURN_ERROR); + } + } else + cdata->prompt_type = PROMPT_TYPE_COMMAND; + if (args_has(args, '1')) cdata->flags |= PROMPT_SINGLE; else if (args_has(args, 'N')) @@ -122,13 +133,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_INCREMENTAL; else if (args_has(args, 'k')) cdata->flags |= PROMPT_KEY; - else if (args_has(args, 'W')) - cdata->flags |= PROMPT_WINDOW; - else if (args_has(args, 'T')) - cdata->flags |= PROMPT_TARGET; status_prompt_set(tc, target, prompt, input, cmd_command_prompt_callback, cmd_command_prompt_free, cdata, - cdata->flags); + cdata->flags, cdata->prompt_type); free(prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 0c562836..0b490b17 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -74,7 +74,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) status_prompt_set(tc, target, new_prompt, NULL, cmd_confirm_before_callback, cmd_confirm_before_free, cdata, - PROMPT_SINGLE); + PROMPT_SINGLE, PROMPT_TYPE_COMMAND); free(new_prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-show-prompt-history.c b/cmd-show-prompt-history.c new file mode 100644 index 00000000..2091ac9d --- /dev/null +++ b/cmd-show-prompt-history.c @@ -0,0 +1,108 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 Anindya Mukherjee + * + * 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 "tmux.h" + +#include + +/* + * Show or clear prompt history. + */ + +static enum cmd_retval cmd_show_prompt_history_exec(struct cmd *, + struct cmdq_item *); + +const struct cmd_entry cmd_show_prompt_history_entry = { + .name = "show-prompt-history", + .alias = "showphist", + + .args = { "T:", 0, 0 }, + .usage = "[-T type]", + + .flags = CMD_AFTERHOOK, + .exec = cmd_show_prompt_history_exec +}; + +const struct cmd_entry cmd_clear_prompt_history_entry = { + .name = "clear-prompt-history", + .alias = "clearphist", + + .args = { "T:", 0, 0 }, + .usage = "[-T type]", + + .flags = CMD_AFTERHOOK, + .exec = cmd_show_prompt_history_exec +}; + +static enum cmd_retval +cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + const char *typestr = args_get(args, 'T'); + enum prompt_type type; + u_int tidx, hidx; + + if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) { + if (typestr == NULL) { + for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) { + free(status_prompt_hlist[tidx]); + status_prompt_hlist[tidx] = NULL; + status_prompt_hsize[tidx] = 0; + } + } else { + type = status_prompt_type(typestr); + if (type == PROMPT_TYPE_INVALID) { + cmdq_error(item, "invalid type: %s", typestr); + return (CMD_RETURN_ERROR); + } + free(status_prompt_hlist[type]); + status_prompt_hlist[type] = NULL; + status_prompt_hsize[type] = 0; + } + + return (CMD_RETURN_NORMAL); + } + + if (typestr == NULL) { + for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) { + cmdq_print(item, "History for %s:\n", + status_prompt_type_string(tidx)); + for (hidx = 0; hidx < status_prompt_hsize[tidx]; + hidx++) { + cmdq_print(item, "%d: %s", hidx + 1, + status_prompt_hlist[tidx][hidx]); + } + cmdq_print(item, "%s", ""); + } + } else { + type = status_prompt_type(typestr); + if (type == PROMPT_TYPE_INVALID) { + cmdq_error(item, "invalid type: %s", typestr); + return (CMD_RETURN_ERROR); + } + cmdq_print(item, "History for %s:\n", + status_prompt_type_string(type)); + for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++) { + cmdq_print(item, "%d: %s", hidx + 1, + status_prompt_hlist[type][hidx]); + } + cmdq_print(item, "%s", ""); + } + + return (CMD_RETURN_NORMAL); +} diff --git a/cmd.c b/cmd.c index 6b443fa2..90fa221a 100644 --- a/cmd.c +++ b/cmd.c @@ -36,6 +36,7 @@ extern const struct cmd_entry cmd_choose_buffer_entry; extern const struct cmd_entry cmd_choose_client_entry; extern const struct cmd_entry cmd_choose_tree_entry; extern const struct cmd_entry cmd_clear_history_entry; +extern const struct cmd_entry cmd_clear_prompt_history_entry; extern const struct cmd_entry cmd_clock_mode_entry; extern const struct cmd_entry cmd_command_prompt_entry; extern const struct cmd_entry cmd_confirm_before_entry; @@ -105,6 +106,7 @@ extern const struct cmd_entry cmd_show_environment_entry; extern const struct cmd_entry cmd_show_hooks_entry; extern const struct cmd_entry cmd_show_messages_entry; extern const struct cmd_entry cmd_show_options_entry; +extern const struct cmd_entry cmd_show_prompt_history_entry; extern const struct cmd_entry cmd_show_window_options_entry; extern const struct cmd_entry cmd_source_file_entry; extern const struct cmd_entry cmd_split_window_entry; @@ -127,6 +129,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_choose_client_entry, &cmd_choose_tree_entry, &cmd_clear_history_entry, + &cmd_clear_prompt_history_entry, &cmd_clock_mode_entry, &cmd_command_prompt_entry, &cmd_confirm_before_entry, @@ -195,6 +198,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_show_hooks_entry, &cmd_show_messages_entry, &cmd_show_options_entry, + &cmd_show_prompt_history_entry, &cmd_show_window_options_entry, &cmd_source_file_entry, &cmd_split_window_entry, diff --git a/key-bindings.c b/key-bindings.c index 467c7f93..b380f2cd 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -350,12 +350,12 @@ key_bindings_init(void) "bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"", "bind -N 'Split window horizontally' % split-window -h", "bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window", - "bind -N 'Prompt for window index to select' \"'\" command-prompt -Wpindex \"select-window -t ':%%'\"", + "bind -N 'Prompt for window index to select' \"'\" command-prompt -T window-target -pindex \"select-window -t ':%%'\"", "bind -N 'Switch to previous client' ( switch-client -p", "bind -N 'Switch to next client' ) switch-client -n", "bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"", "bind -N 'Delete the most recent paste buffer' - delete-buffer", - "bind -N 'Move the current window' . command-prompt -T \"move-window -t '%%'\"", + "bind -N 'Move the current window' . command-prompt -T target \"move-window -t '%%'\"", "bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'", "bind -N 'Select window 0' 0 select-window -t:=0", "bind -N 'Select window 1' 1 select-window -t:=1", @@ -479,8 +479,8 @@ key_bindings_init(void) "bind -Tcopy-mode C-k send -X copy-end-of-line", "bind -Tcopy-mode C-n send -X cursor-down", "bind -Tcopy-mode C-p send -X cursor-up", - "bind -Tcopy-mode C-r command-prompt -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", - "bind -Tcopy-mode C-s command-prompt -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'", + "bind -Tcopy-mode C-r command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", + "bind -Tcopy-mode C-s command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'", "bind -Tcopy-mode C-v send -X page-down", "bind -Tcopy-mode C-w send -X copy-pipe-and-cancel", "bind -Tcopy-mode Escape send -X cancel", @@ -559,7 +559,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi Space send -X begin-selection", "bind -Tcopy-mode-vi '$' send -X end-of-line", "bind -Tcopy-mode-vi , send -X jump-reverse", - "bind -Tcopy-mode-vi / command-prompt -p'(search down)' 'send -X search-forward \"%%%\"'", + "bind -Tcopy-mode-vi / command-prompt -T search -p'(search down)' 'send -X search-forward \"%%%\"'", "bind -Tcopy-mode-vi 0 send -X start-of-line", "bind -Tcopy-mode-vi 1 command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 2 command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"'", @@ -572,7 +572,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi 9 command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"'", "bind -Tcopy-mode-vi : command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode-vi \\; send -X jump-again", - "bind -Tcopy-mode-vi ? command-prompt -p'(search up)' 'send -X search-backward \"%%%\"'", + "bind -Tcopy-mode-vi ? command-prompt -T search -p'(search up)' 'send -X search-backward \"%%%\"'", "bind -Tcopy-mode-vi A send -X append-selection-and-cancel", "bind -Tcopy-mode-vi B send -X previous-space", "bind -Tcopy-mode-vi D send -X copy-end-of-line", diff --git a/mode-tree.c b/mode-tree.c index ca7f33a4..807c1dcb 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1168,7 +1168,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mtd->references++; status_prompt_set(c, NULL, "(search) ", "", mode_tree_search_callback, mode_tree_search_free, mtd, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH); break; case 'n': mode_tree_search_set(mtd); @@ -1177,7 +1177,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mtd->references++; status_prompt_set(c, NULL, "(filter) ", mtd->filter, mode_tree_filter_callback, mode_tree_filter_free, mtd, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH); break; case 'v': mtd->preview = !mtd->preview; diff --git a/options-table.c b/options-table.c index 4f4d9ff6..73c7fd63 100644 --- a/options-table.c +++ b/options-table.c @@ -302,6 +302,15 @@ const struct options_table_entry options_table[] = { .text = "Maximum number of server messages to keep." }, + { .name = "prompt-history-limit", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 100, + .text = "Maximum number of commands to keep in history." + }, + { .name = "set-clipboard", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SERVER, diff --git a/status.c b/status.c index f4418500..7bcd2f38 100644 --- a/status.c +++ b/status.c @@ -33,9 +33,9 @@ static void status_message_callback(int, short, void *); static void status_timer_callback(int, short, void *); static char *status_prompt_find_history_file(void); -static const char *status_prompt_up_history(u_int *); -static const char *status_prompt_down_history(u_int *); -static void status_prompt_add_history(const char *); +static const char *status_prompt_up_history(u_int *, u_int); +static const char *status_prompt_down_history(u_int *, u_int); +static void status_prompt_add_history(const char *, u_int); static char *status_prompt_complete(struct client *, const char *, u_int); static char *status_prompt_complete_window_menu(struct client *, @@ -49,10 +49,16 @@ struct status_prompt_menu { char flag; }; +static const char *prompt_type_strings[] = { + "command", + "search", + "target", + "window-target" +}; + /* Status prompt history. */ -#define PROMPT_HISTORY 100 -static char **status_prompt_hlist; -static u_int status_prompt_hsize; +char **status_prompt_hlist[PROMPT_NTYPES]; +u_int status_prompt_hsize[PROMPT_NTYPES]; /* Find the history file to load/save from/to. */ static char * @@ -75,6 +81,28 @@ status_prompt_find_history_file(void) return (path); } +/* Add loaded history item to the appropriate list. */ +static void +status_prompt_add_typed_history(char *line) +{ + char *typestr; + enum prompt_type type = PROMPT_TYPE_INVALID; + + typestr = strsep(&line, ":"); + if (line != NULL) + type = status_prompt_type(typestr); + if (type == PROMPT_TYPE_INVALID) { + /* + * Invalid types are not expected, but this provides backward + * compatibility with old history files. + */ + if (line != NULL) + *(--line) = ':'; + status_prompt_add_history(typestr, PROMPT_TYPE_COMMAND); + } else + status_prompt_add_history(line, type); +} + /* Load status prompt history from file. */ void status_prompt_load_history(void) @@ -102,12 +130,12 @@ status_prompt_load_history(void) if (length > 0) { if (line[length - 1] == '\n') { line[length - 1] = '\0'; - status_prompt_add_history(line); + status_prompt_add_typed_history(line); } else { tmp = xmalloc(length + 1); memcpy(tmp, line, length); tmp[length] = '\0'; - status_prompt_add_history(tmp); + status_prompt_add_typed_history(tmp); free(tmp); } } @@ -120,7 +148,7 @@ void status_prompt_save_history(void) { FILE *f; - u_int i; + u_int i, type; char *history_file; if ((history_file = status_prompt_find_history_file()) == NULL) @@ -135,9 +163,13 @@ status_prompt_save_history(void) } free(history_file); - for (i = 0; i < status_prompt_hsize; i++) { - fputs(status_prompt_hlist[i], f); - fputc('\n', f); + for (type = 0; type < PROMPT_NTYPES; type++) { + for (i = 0; i < status_prompt_hsize[type]; i++) { + fputs(prompt_type_strings[type], f); + fputc(':', f); + fputs(status_prompt_hlist[type][i], f); + fputc('\n', f); + } } fclose(f); @@ -545,7 +577,7 @@ status_message_redraw(struct client *c) void status_prompt_set(struct client *c, struct cmd_find_state *fs, const char *msg, const char *input, prompt_input_cb inputcb, - prompt_free_cb freecb, void *data, int flags) + prompt_free_cb freecb, void *data, int flags, enum prompt_type prompt_type) { struct format_tree *ft; char *tmp; @@ -581,9 +613,10 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, c->prompt_freecb = freecb; c->prompt_data = data; - c->prompt_hindex = 0; + memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); c->prompt_flags = flags; + c->prompt_type = prompt_type; c->prompt_mode = PROMPT_ENTRY; if (~flags & PROMPT_INCREMENTAL) @@ -644,7 +677,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input) c->prompt_buffer = utf8_fromcstr(tmp); c->prompt_index = utf8_strlen(c->prompt_buffer); - c->prompt_hindex = 0; + memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); c->flags |= CLIENT_REDRAWSTATUS; @@ -768,7 +801,7 @@ status_prompt_space(const struct utf8_data *ud) } /* - * Translate key from emacs to vi. Return 0 to drop key, 1 to process the key + * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key * as an emacs key; return 2 to append to the buffer. */ static int @@ -1222,7 +1255,8 @@ process_key: goto changed; case KEYC_UP: case '\020': /* C-p */ - histstr = status_prompt_up_history(&c->prompt_hindex); + histstr = status_prompt_up_history(c->prompt_hindex, + c->prompt_type); if (histstr == NULL) break; free(c->prompt_buffer); @@ -1231,7 +1265,8 @@ process_key: goto changed; case KEYC_DOWN: case '\016': /* C-n */ - histstr = status_prompt_down_history(&c->prompt_hindex); + histstr = status_prompt_down_history(c->prompt_hindex, + c->prompt_type); if (histstr == NULL) break; free(c->prompt_buffer); @@ -1259,7 +1294,7 @@ process_key: case '\n': s = utf8_tocstr(c->prompt_buffer); if (*s != '\0') - status_prompt_add_history(s); + status_prompt_add_history(s, c->prompt_type); if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) status_prompt_clear(c); free(s); @@ -1348,54 +1383,78 @@ changed: /* Get previous line from the history. */ static const char * -status_prompt_up_history(u_int *idx) +status_prompt_up_history(u_int *idx, u_int type) { /* * History runs from 0 to size - 1. Index is from 0 to size. Zero is * empty. */ - if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) + if (status_prompt_hsize[type] == 0 || + idx[type] == status_prompt_hsize[type]) return (NULL); - (*idx)++; - return (status_prompt_hlist[status_prompt_hsize - *idx]); + idx[type]++; + return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]); } /* Get next line from the history. */ static const char * -status_prompt_down_history(u_int *idx) +status_prompt_down_history(u_int *idx, u_int type) { - if (status_prompt_hsize == 0 || *idx == 0) + if (status_prompt_hsize[type] == 0 || idx[type] == 0) return (""); - (*idx)--; - if (*idx == 0) + idx[type]--; + if (idx[type] == 0) return (""); - return (status_prompt_hlist[status_prompt_hsize - *idx]); + return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]); } /* Add line to the history. */ static void -status_prompt_add_history(const char *line) +status_prompt_add_history(const char *line, u_int type) { - size_t size; + u_int i, oldsize, newsize, freecount, hlimit, new = 1; + size_t movesize; - if (status_prompt_hsize > 0 && - strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) - return; + oldsize = status_prompt_hsize[type]; + if (oldsize > 0 && + strcmp(status_prompt_hlist[type][oldsize - 1], line) == 0) + new = 0; - if (status_prompt_hsize == PROMPT_HISTORY) { - free(status_prompt_hlist[0]); - - size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; - memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); - - status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); - return; + hlimit = options_get_number(global_options, "prompt-history-limit"); + if (hlimit > oldsize) { + if (new == 0) + return; + newsize = oldsize + new; + } else { + newsize = hlimit; + freecount = oldsize + new - newsize; + if (freecount > oldsize) + freecount = oldsize; + if (freecount == 0) + return; + for (i = 0; i < freecount; i++) + free(status_prompt_hlist[type][i]); + movesize = (oldsize - freecount) * + sizeof *status_prompt_hlist[type]; + if (movesize > 0) { + memmove(&status_prompt_hlist[type][0], + &status_prompt_hlist[type][freecount], movesize); + } } - status_prompt_hlist = xreallocarray(status_prompt_hlist, - status_prompt_hsize + 1, sizeof *status_prompt_hlist); - status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); + if (newsize == 0) { + free(status_prompt_hlist[type]); + status_prompt_hlist[type] = NULL; + } else if (newsize != oldsize) { + status_prompt_hlist[type] = + xreallocarray(status_prompt_hlist[type], newsize, + sizeof *status_prompt_hlist[type]); + } + + if (new == 1 && newsize > 0) + status_prompt_hlist[type][newsize - 1] = xstrdup(line); + status_prompt_hsize[type] = newsize; } /* Build completion list. */ @@ -1501,7 +1560,7 @@ status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key, s = xstrdup(spm->list[idx]); else xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); - if (c->prompt_flags & PROMPT_WINDOW) { + if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { free(c->prompt_buffer); c->prompt_buffer = utf8_fromcstr(s); c->prompt_index = utf8_strlen(c->prompt_buffer); @@ -1609,7 +1668,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, } list = xreallocarray(list, size + 1, sizeof *list); - if (c->prompt_flags & PROMPT_WINDOW) { + if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); xasprintf(&list[size++], "%d", wl->idx); } else { @@ -1717,10 +1776,12 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) u_int size = 0, i; if (*word == '\0' && - ((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0)) + c->prompt_type != PROMPT_TYPE_TARGET && + c->prompt_type != PROMPT_TYPE_WINDOW_TARGET) return (NULL); - if (((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0) && + if (c->prompt_type != PROMPT_TYPE_TARGET && + c->prompt_type != PROMPT_TYPE_WINDOW_TARGET && strncmp(word, "-t", 2) != 0 && strncmp(word, "-s", 2) != 0) { list = status_prompt_complete_list(&size, word, offset == 0); @@ -1733,7 +1794,8 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) goto found; } - if (c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) { + if (c->prompt_type == PROMPT_TYPE_TARGET || + c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { s = word; flag = '\0'; } else { @@ -1743,7 +1805,7 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) } /* If this is a window completion, open the window menu. */ - if (c->prompt_flags & PROMPT_WINDOW) { + if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { out = status_prompt_complete_window_menu(c, c->session, s, offset, '\0'); goto found; @@ -1793,3 +1855,25 @@ found: } return (out); } + +/* Return the type of the prompt as an enum. */ +enum prompt_type +status_prompt_type(const char *type) +{ + u_int i; + + for (i = 0; i < PROMPT_NTYPES; i++) { + if (strcmp(type, status_prompt_type_string(i)) == 0) + return (i); + } + return (PROMPT_TYPE_INVALID); +} + +/* Accessor for prompt_type_strings. */ +const char * +status_prompt_type_string(u_int type) +{ + if (type >= PROMPT_NTYPES) + return ("invalid"); + return (prompt_type_strings[type]); +} diff --git a/tmux.1 b/tmux.1 index 47cf4f2c..50b3b6b7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3453,7 +3453,9 @@ will write command prompt history on exit and load it from on start. .It Ic message-limit Ar number Set the number of error or information messages to save in the message log for each client. -The default is 100. +.It Ic prompt-history-limit Ar number +Set the number of history items to save in the history file for each type of +command prompt. .It Xo Ic set-clipboard .Op Ic on | external | off .Xc @@ -5376,11 +5378,25 @@ session option. .Pp Commands related to the status line are as follows: .Bl -tag -width Ds +.It Xo Ic clear-prompt-history +.Op Fl T Ar prompt-type +.Xc +.D1 (alias: Ic clrphist) +Clear status prompt history for prompt type +.Ar prompt-type . +If +.Fl T +is omitted, then clear history for all types. +See +.Ic command-prompt +for possible values for +.Ar prompt-type . .It Xo Ic command-prompt -.Op Fl 1ikNTW +.Op Fl 1ikN .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client +.Op Fl T Ar prompt-type .Op Ar template .Xc Open the command prompt in a client. @@ -5436,14 +5452,20 @@ makes the prompt only accept numeric key presses. .Fl i executes the command every time the prompt input changes instead of when the user exits the command prompt. +.Pp .Fl T tells .Nm -that the prompt is for a target which affects what completions are offered when +the prompt type. +This affects what completions are offered when .Em Tab -is pressed; -.Fl W -is similar but indicates the prompt is for a window. +is pressed. +Available types are: +.Ql command , +.Ql search , +.Ql target +and +.Ql window-target . .Pp The following keys have a special meaning in the command prompt, depending on the value of the @@ -5665,6 +5687,19 @@ If omitted, half of the terminal size is used. The .Fl C flag closes any popup on the client. +.It Xo Ic show-prompt-history +.Op Fl T Ar prompt-type +.Xc +.D1 (alias: Ic showphist) +Display status prompt history for prompt type +.Ar prompt-type . +If +.Fl T +is omitted, then show history for all types. +See +.Ic command-prompt +for possible values for +.Ar prompt-type . .El .Sh BUFFERS .Nm diff --git a/tmux.h b/tmux.h index fbe19136..5fefcef5 100644 --- a/tmux.h +++ b/tmux.h @@ -1571,6 +1571,16 @@ struct status_line { struct status_line_entry entries[STATUS_LINES_LIMIT]; }; +/* Prompt type. */ +#define PROMPT_NTYPES 4 +enum prompt_type { + PROMPT_TYPE_COMMAND, + PROMPT_TYPE_SEARCH, + PROMPT_TYPE_TARGET, + PROMPT_TYPE_WINDOW_TARGET, + PROMPT_TYPE_INVALID = 0xff +}; + /* File in client. */ typedef void (*client_file_cb) (struct client *, const char *, int, int, struct evbuffer *, void *); @@ -1734,18 +1744,16 @@ struct client { prompt_input_cb prompt_inputcb; prompt_free_cb prompt_freecb; void *prompt_data; - u_int prompt_hindex; + u_int prompt_hindex[PROMPT_NTYPES]; enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; struct utf8_data *prompt_saved; - #define PROMPT_SINGLE 0x1 #define PROMPT_NUMERIC 0x2 #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 #define PROMPT_KEY 0x10 -#define PROMPT_WINDOW 0x20 -#define PROMPT_TARGET 0x40 int prompt_flags; + enum prompt_type prompt_type; struct session *session; struct session *last_session; @@ -2517,6 +2525,8 @@ void server_check_unattached(void); void server_unzoom_window(struct window *); /* status.c */ +extern char **status_prompt_hlist[]; +extern u_int status_prompt_hsize[]; void status_timer_start(struct client *); void status_timer_start_all(void); void status_update_cache(struct session *); @@ -2531,13 +2541,15 @@ void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, struct cmd_find_state *, const char *, const char *, prompt_input_cb, prompt_free_cb, - void *, int); + void *, int, enum prompt_type); void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); int status_prompt_key(struct client *, key_code); void status_prompt_update(struct client *, const char *, const char *); void status_prompt_load_history(void); void status_prompt_save_history(void); +const char *status_prompt_type_string(u_int); +enum prompt_type status_prompt_type(const char *type); /* resize.c */ void resize_window(struct window *, u_int, u_int, int, int); diff --git a/window-customize.c b/window-customize.c index 34a13f73..782542f7 100644 --- a/window-customize.c +++ b/window-customize.c @@ -1123,7 +1123,7 @@ window_customize_set_option(struct client *c, status_prompt_set(c, NULL, prompt, value, window_customize_set_option_callback, window_customize_free_item_callback, new_item, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); free(value); @@ -1264,7 +1264,7 @@ window_customize_set_key(struct client *c, status_prompt_set(c, NULL, prompt, value, window_customize_set_command_callback, window_customize_free_item_callback, new_item, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); free(value); } else if (strcmp(s, "Note") == 0) { @@ -1281,7 +1281,7 @@ window_customize_set_key(struct client *c, (bd->note == NULL ? "" : bd->note), window_customize_set_note_callback, window_customize_free_item_callback, new_item, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); } } @@ -1458,7 +1458,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_current_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'D': @@ -1471,7 +1471,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_tagged_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'u': @@ -1487,7 +1487,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_current_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'U': @@ -1500,7 +1500,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_tagged_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'H': diff --git a/window-tree.c b/window-tree.c index 881896e7..f8e38399 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1296,7 +1296,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, data->references++; status_prompt_set(c, NULL, prompt, "", window_tree_kill_current_callback, window_tree_command_free, - data, PROMPT_SINGLE|PROMPT_NOFORMAT); + data, PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'X': @@ -1307,7 +1307,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, data->references++; status_prompt_set(c, NULL, prompt, "", window_tree_kill_tagged_callback, window_tree_command_free, - data, PROMPT_SINGLE|PROMPT_NOFORMAT); + data, PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case ':': @@ -1319,7 +1319,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, data->references++; status_prompt_set(c, NULL, prompt, "", window_tree_command_callback, window_tree_command_free, - data, PROMPT_NOFORMAT); + data, PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case '\r': From cfc7c9cf2403f9975421c634d09ed2dd1e04661d Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:51:43 +0000 Subject: [PATCH 0811/1006] Fire check callback after cleaning up event so it does not get stuck, from Jeongho Jang in GitHub issue 2695. --- file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/file.c b/file.c index 8f497b38..e527f121 100644 --- a/file.c +++ b/file.c @@ -506,14 +506,14 @@ file_write_error_callback(__unused struct bufferevent *bev, __unused short what, log_debug("write error file %d", cf->stream); - if (cf->cb != NULL) - cf->cb(NULL, NULL, 0, -1, NULL, cf->data); - bufferevent_free(cf->event); cf->event = NULL; close(cf->fd); cf->fd = -1; + + if (cf->cb != NULL) + cf->cb(NULL, NULL, 0, -1, NULL, cf->data); } /* Client file write callback. */ From f706a7e2366ba19bb06265a331fa22a3c7546d90 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:52:07 +0000 Subject: [PATCH 0812/1006] Remove old shift function keys which interfere with xterm keys now. GitHub issue 2696. --- input-keys.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/input-keys.c b/input-keys.c index b975c1ed..d6f48e6c 100644 --- a/input-keys.c +++ b/input-keys.c @@ -95,30 +95,6 @@ static struct input_key_entry input_key_defaults[] = { { .key = KEYC_F12, .data = "\033[24~" }, - { .key = KEYC_F1|KEYC_SHIFT, - .data = "\033[25~" - }, - { .key = KEYC_F2|KEYC_SHIFT, - .data = "\033[26~" - }, - { .key = KEYC_F3|KEYC_SHIFT, - .data = "\033[28~" - }, - { .key = KEYC_F4|KEYC_SHIFT, - .data = "\033[29~" - }, - { .key = KEYC_F5|KEYC_SHIFT, - .data = "\033[31~" - }, - { .key = KEYC_F6|KEYC_SHIFT, - .data = "\033[32~" - }, - { .key = KEYC_F7|KEYC_SHIFT, - .data = "\033[33~" - }, - { .key = KEYC_F8|KEYC_SHIFT, - .data = "\033[34~" - }, { .key = KEYC_IC, .data = "\033[2~" }, From 73bf358f6da9ce8b6c2dd51849ab88a22bb883ad Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:52:29 +0000 Subject: [PATCH 0813/1006] Do not expand the file given with -f so it can contain :s. --- tmux.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tmux.c b/tmux.c index 71cbccba..16877407 100644 --- a/tmux.c +++ b/tmux.c @@ -338,7 +338,7 @@ main(int argc, char **argv) char *path = NULL, *label = NULL; char *cause, **var; const char *s, *cwd; - int opt, keys, feat = 0; + int opt, keys, feat = 0, fflag = 0; uint64_t flags = 0; const struct options_table_entry *oe; u_int i; @@ -383,10 +383,15 @@ main(int argc, char **argv) flags |= CLIENT_CONTROL; break; case 'f': - for (i = 0; i < cfg_nfiles; i++) - free(cfg_files[i]); - free(cfg_files); - expand_paths(optarg, &cfg_files, &cfg_nfiles, 0); + if (!fflag) { + fflag = 1; + for (i = 0; i < cfg_nfiles; i++) + free(cfg_files[i]); + cfg_nfiles = 0; + } + cfg_files = xreallocarray(cfg_files, cfg_nfiles + 1, + sizeof *cfg_files); + cfg_files[cfg_nfiles++] = xstrdup(optarg); cfg_quiet = 0; break; case 'V': From 1bbdd2aba27057363134fa8c20c10e56ce095dea Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:52:56 +0000 Subject: [PATCH 0814/1006] Add -F for command-prompt and use it to fix "Rename" on the window menu, GitHub issue 2699. --- cmd-command-prompt.c | 10 ++++++---- key-bindings.c | 2 +- tmux.1 | 7 ++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index c2a2dec7..a955ac32 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,8 +40,8 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1kiI:Np:t:T:", 0, 1 }, - .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE + .args = { "1FkiI:Np:t:T:", 0, 1 }, + .usage = "[-1FkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [-T type] [template]", .flags = CMD_CLIENT_TFLAG, @@ -59,7 +59,7 @@ struct cmd_command_prompt_cdata { char *next_prompt; char *template; - int idx; + int idx; }; static enum cmd_retval @@ -87,7 +87,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->template = NULL; cdata->idx = 1; - if (args->argc != 0) + if (args->argc != 0 && args_has(args, 'F')) + cdata->template = format_single_from_target(item, args->argv[0]); + else if (args->argc != 0) cdata->template = xstrdup(args->argv[0]); else cdata->template = xstrdup("%1"); diff --git a/key-bindings.c b/key-bindings.c index b380f2cd..de5c20ee 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -41,7 +41,7 @@ " 'Kill' 'X' {kill-window}" \ " 'Respawn' 'R' {respawn-window -k}" \ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ - " 'Rename' 'n' {command-prompt -I \"#W\" \"rename-window -- '%%'\"}" \ + " 'Rename' 'n' {command-prompt -FI \"#W\" \"rename-window -t#{window_id} -- '%%'\"}" \ " ''" \ " 'New After' 'w' {new-window -a}" \ " 'New At End' 'W' {new-window}" diff --git a/tmux.1 b/tmux.1 index 50b3b6b7..d44dd591 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5392,7 +5392,7 @@ See for possible values for .Ar prompt-type . .It Xo Ic command-prompt -.Op Fl 1ikN +.Op Fl 1FikN .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client @@ -5407,6 +5407,11 @@ to execute commands interactively. If .Ar template is specified, it is used as the command. +With +.Fl F , +.Ar template +is expanded as a format. +.Pp If present, .Fl I is a comma-separated list of the initial text for each prompt. From 77bd6b9ec32bfbc7fbf4de09cae1ce7ea7f3ac35 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:53:19 +0000 Subject: [PATCH 0815/1006] Do not use NULL client when source-file finishes, GitHub issue 2707. --- cmd-source-file.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd-source-file.c b/cmd-source-file.c index b7a7abee..5509259f 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -68,7 +68,9 @@ cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata) struct cmdq_item *new_item; if (cfg_finished) { - if (cdata->retval == CMD_RETURN_ERROR && c->session == NULL) + if (cdata->retval == CMD_RETURN_ERROR && + c != NULL && + c->session == NULL) c->retval = 1; new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL); cmdq_insert_after(cdata->after, new_item); From 77b1290698e3bc3ea8edeadb96fd483af26efdfe Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:56:47 +0000 Subject: [PATCH 0816/1006] More accurate vi(1) word navigation in copy mode and on the status line. This changes the meaning of the word-separators option - setting it to the empty string is equivalent to the previous behavior. From Will Noble in GitHub issue 2693. --- grid-reader.c | 161 ++++++++++++++++++++++--------------- options-table.c | 6 +- status.c | 207 ++++++++++++++++++++++++++++++++++++------------ tmux.1 | 16 ++-- tmux.h | 6 +- utf8.c | 2 +- window-copy.c | 102 +++++++++++++----------- 7 files changed, 325 insertions(+), 175 deletions(-) diff --git a/grid-reader.c b/grid-reader.c index df0dd450..c14e3d33 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -153,6 +153,29 @@ grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all) gr->cx = grid_reader_line_length(gr); } +/* Handle line wrapping while moving the cursor. */ +static int +grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy) +{ + /* + * Make sure the cursor lies within the grid reader's bounding area, + * wrapping to the next line as necessary. Return zero if the cursor + * would wrap past the bottom of the grid. + */ + while (gr->cx > *xx) { + if (gr->cy == *yy) + return (0); + grid_reader_cursor_start_of_line(gr, 0); + grid_reader_cursor_down(gr); + + if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) + *xx = gr->gd->sx - 1; + else + *xx = grid_reader_line_length(gr); + } + return (1); +} + /* Check if character under cursor is in set. */ int grid_reader_in_set(struct grid_reader *gr, const char *set) @@ -170,7 +193,6 @@ void grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) { u_int xx, yy; - int expected = 0; /* Do not break up wrapped words. */ if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) @@ -180,33 +202,35 @@ grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) yy = gr->gd->hsize + gr->gd->sy - 1; /* - * If we started inside a word, skip over word characters. Then skip - * over separators till the next word. + * When navigating via spaces (for example with next-space) separators + * should be empty. * - * expected is initially set to 0 for the former and then 1 for the - * latter. It is finally set to 0 when the beginning of the next word is - * found. + * If we started on a separator that is not whitespace, skip over + * subsequent separators that are not whitespace. Otherwise, if we + * started on a non-whitespace character, skip over subsequent + * characters that are neither whitespace nor separators. Then, skip + * over whitespace (if any) until the next non-whitespace character. */ - do { - while (gr->cx > xx || - grid_reader_in_set(gr, separators) == expected) { - /* Move down if we are past the end of the line. */ - if (gr->cx > xx) { - if (gr->cy == yy) - return; - grid_reader_cursor_start_of_line(gr, 0); - grid_reader_cursor_down(gr); - - if (grid_get_line(gr->gd, gr->cy)->flags & - GRID_LINE_WRAPPED) - xx = gr->gd->sx - 1; - else - xx = grid_reader_line_length(gr); - } else + if (!grid_reader_handle_wrap(gr, &xx, &yy)) + return; + if (!grid_reader_in_set(gr, WHITESPACE)) { + if (grid_reader_in_set(gr, separators)) { + do gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + grid_reader_in_set(gr, separators) && + !grid_reader_in_set(gr, WHITESPACE)); + } else { + do + gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + !(grid_reader_in_set(gr, separators) || + grid_reader_in_set(gr, WHITESPACE))); } - expected = !expected; - } while (expected == 1); + } + while (grid_reader_handle_wrap(gr, &xx, &yy) && + grid_reader_in_set(gr, WHITESPACE)) + gr->cx++; } /* Move cursor to the end of the next word. */ @@ -214,7 +238,6 @@ void grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) { u_int xx, yy; - int expected = 1; /* Do not break up wrapped words. */ if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) @@ -224,49 +247,54 @@ grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) yy = gr->gd->hsize + gr->gd->sy - 1; /* - * If we started on a separator, skip over separators. Then skip over - * word characters till the next separator. + * When navigating via spaces (for example with next-space), separators + * should be empty in both modes. * - * expected is initially set to 1 for the former and then 1 for the - * latter. It is finally set to 1 when the end of the next word is - * found. + * If we started on a whitespace, move until reaching the first + * non-whitespace character. If that character is a separator, treat + * subsequent separators as a word, and continue moving until the first + * non-separator. Otherwise, continue moving until the first separator + * or whitespace. */ - do { - while (gr->cx > xx || - grid_reader_in_set(gr, separators) == expected) { - /* Move down if we are past the end of the line. */ - if (gr->cx > xx) { - if (gr->cy == yy) - return; - grid_reader_cursor_start_of_line(gr, 0); - grid_reader_cursor_down(gr); - if (grid_get_line(gr->gd, gr->cy)->flags & - GRID_LINE_WRAPPED) - xx = gr->gd->sx - 1; - else - xx = grid_reader_line_length(gr); - } else + while (grid_reader_handle_wrap(gr, &xx, &yy)) { + if (grid_reader_in_set(gr, WHITESPACE)) + gr->cx++; + else if (grid_reader_in_set(gr, separators)) { + do gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + grid_reader_in_set(gr, separators) && + !grid_reader_in_set(gr, WHITESPACE)); + return; + } else { + do + gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + !(grid_reader_in_set(gr, WHITESPACE) || + grid_reader_in_set(gr, separators))); + return; } - expected = !expected; - } while (expected == 0); + } } /* Move to the previous place where a word begins. */ void grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, - int already) + int already, int stop_at_eol) { - int oldx, oldy, r; + int oldx, oldy, at_eol, word_is_letters; /* Move back to the previous word character. */ - if (already || grid_reader_in_set(gr, separators)) { + if (already || grid_reader_in_set(gr, WHITESPACE)) { for (;;) { if (gr->cx > 0) { gr->cx--; - if (!grid_reader_in_set(gr, separators)) + if (!grid_reader_in_set(gr, WHITESPACE)) { + word_is_letters = + !grid_reader_in_set(gr, separators); break; + } } else { if (gr->cy == 0) return; @@ -274,17 +302,21 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, grid_reader_cursor_end_of_line(gr, 0, 0); /* Stop if separator at EOL. */ - if (gr->cx > 0) { + if (stop_at_eol && gr->cx > 0) { oldx = gr->cx; gr->cx--; - r = grid_reader_in_set(gr, separators); + at_eol = grid_reader_in_set(gr, + WHITESPACE); gr->cx = oldx; - if (r) + if (at_eol) { + word_is_letters = 0; break; + } } } } - } + } else + word_is_letters = !grid_reader_in_set(gr, separators); /* Move back to the beginning of this word. */ do { @@ -292,15 +324,16 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, oldy = gr->cy; if (gr->cx == 0) { if (gr->cy == 0 || - ~grid_get_line(gr->gd, gr->cy - 1)->flags & - GRID_LINE_WRAPPED) + (~grid_get_line(gr->gd, gr->cy - 1)->flags & + GRID_LINE_WRAPPED)) break; grid_reader_cursor_up(gr); grid_reader_cursor_end_of_line(gr, 0, 1); } if (gr->cx > 0) gr->cx--; - } while (!grid_reader_in_set(gr, separators)); + } while (!grid_reader_in_set(gr, WHITESPACE) && + word_is_letters != grid_reader_in_set(gr, separators)); gr->cx = oldx; gr->cy = oldy; } @@ -324,17 +357,17 @@ grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc) memcmp(gc.data.data, jc->data, gc.data.size) == 0) { gr->cx = px; gr->cy = py; - return 1; + return (1); } px++; } if (py == yy || !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)) - return 0; + return (0); px = 0; } - return 0; + return (0); } /* Jump back to character. */ @@ -354,16 +387,16 @@ grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc) memcmp(gc.data.data, jc->data, gc.data.size) == 0) { gr->cx = px - 1; gr->cy = py - 1; - return 1; + return (1); } } if (py == 1 || !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED)) - return 0; + return (0); xx = grid_line_length(gr->gd, py - 2); } - return 0; + return (0); } /* Jump back to the first non-blank character of the line. */ diff --git a/options-table.c b/options-table.c index 73c7fd63..01880d62 100644 --- a/options-table.c +++ b/options-table.c @@ -755,7 +755,11 @@ const struct options_table_entry options_table[] = { { .name = "word-separators", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = " ", + /* + * The set of non-alphanumeric printable ASCII characters minus the + * underscore. + */ + .default_str = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", .text = "Characters considered to separate words." }, diff --git a/status.c b/status.c index 7bcd2f38..7435b31a 100644 --- a/status.c +++ b/status.c @@ -876,17 +876,25 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) *new_key = KEYC_BSPACE; return (1); case 'b': - case 'B': *new_key = 'b'|KEYC_META; return (1); + case 'B': + *new_key = 'B'|KEYC_VI; + return (1); case 'd': *new_key = '\025'; return (1); case 'e': + *new_key = 'e'|KEYC_VI; + return (1); case 'E': + *new_key = 'E'|KEYC_VI; + return (1); case 'w': + *new_key = 'w'|KEYC_VI; + return (1); case 'W': - *new_key = 'f'|KEYC_META; + *new_key = 'W'|KEYC_VI; return (1); case 'p': *new_key = '\031'; /* C-y */ @@ -1061,16 +1069,125 @@ status_prompt_replace_complete(struct client *c, const char *s) return (1); } +/* Prompt forward to the next beginning of a word. */ +static void +status_prompt_forward_word(struct client *c, size_t size, int vi, + const char *separators) +{ + size_t idx = c->prompt_index; + int word_is_separators; + + /* In emacs mode, skip until the first non-whitespace character. */ + if (!vi) + while (idx != size && + status_prompt_space(&c->prompt_buffer[idx])) + idx++; + + /* Can't move forward if we're already at the end. */ + if (idx == size) { + c->prompt_index = idx; + return; + } + + /* Determine the current character class (separators or not). */ + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]) && + !status_prompt_space(&c->prompt_buffer[idx]); + + /* Skip ahead until the first space or opposite character class. */ + do { + idx++; + if (status_prompt_space(&c->prompt_buffer[idx])) { + /* In vi mode, go to the start of the next word. */ + if (vi) + while (idx != size && + status_prompt_space(&c->prompt_buffer[idx])) + idx++; + break; + } + } while (idx != size && word_is_separators == status_prompt_in_list( + separators, &c->prompt_buffer[idx])); + + c->prompt_index = idx; +} + +/* Prompt forward to the next end of a word. */ +static void +status_prompt_end_word(struct client *c, size_t size, const char *separators) +{ + size_t idx = c->prompt_index; + int word_is_separators; + + /* Can't move forward if we're already at the end. */ + if (idx == size) + return; + + /* Find the next word. */ + do { + idx++; + if (idx == size) { + c->prompt_index = idx; + return; + } + } while (status_prompt_space(&c->prompt_buffer[idx])); + + /* Determine the character class (separators or not). */ + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]); + + /* Skip ahead until the next space or opposite character class. */ + do { + idx++; + if (idx == size) + break; + } while (!status_prompt_space(&c->prompt_buffer[idx]) && + word_is_separators == status_prompt_in_list(separators, + &c->prompt_buffer[idx])); + + /* Back up to the previous character to stop at the end of the word. */ + c->prompt_index = idx - 1; +} + +/* Prompt backward to the previous beginning of a word. */ +static void +status_prompt_backward_word(struct client *c, const char *separators) +{ + size_t idx = c->prompt_index; + int word_is_separators; + + /* Find non-whitespace. */ + while (idx != 0) { + --idx; + if (!status_prompt_space(&c->prompt_buffer[idx])) + break; + } + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]); + + /* Find the character before the beginning of the word. */ + while (idx != 0) { + --idx; + if (status_prompt_space(&c->prompt_buffer[idx]) || + word_is_separators != status_prompt_in_list(separators, + &c->prompt_buffer[idx])) { + /* Go back to the word. */ + idx++; + break; + } + } + c->prompt_index = idx; +} + /* Handle keys in prompt. */ int status_prompt_key(struct client *c, key_code key) { struct options *oo = c->session->options; char *s, *cp, prefix = '='; - const char *histstr, *ws = NULL, *keystring; + const char *histstr, *separators = NULL, *keystring; size_t size, idx; struct utf8_data tmp; - int keys; + int keys, word_is_separators; if (c->prompt_flags & PROMPT_KEY) { keystring = key_string_lookup_key(key, 0); @@ -1173,20 +1290,24 @@ process_key: } break; case '\027': /* C-w */ - ws = options_get_string(oo, "word-separators"); + separators = options_get_string(oo, "word-separators"); idx = c->prompt_index; - /* Find a non-separator. */ + /* Find non-whitespace. */ while (idx != 0) { idx--; - if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) + if (!status_prompt_space(&c->prompt_buffer[idx])) break; } + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]); - /* Find the separator at the beginning of the word. */ + /* Find the character before the beginning of the word. */ while (idx != 0) { idx--; - if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) { + if (status_prompt_space(&c->prompt_buffer[idx]) || + word_is_separators != status_prompt_in_list( + separators, &c->prompt_buffer[idx])) { /* Go back to the word. */ idx++; break; @@ -1208,50 +1329,32 @@ process_key: c->prompt_index = idx; goto changed; - case 'f'|KEYC_META: case KEYC_RIGHT|KEYC_CTRL: - ws = options_get_string(oo, "word-separators"); - - /* Find a word. */ - while (c->prompt_index != size) { - idx = ++c->prompt_index; - if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) - break; - } - - /* Find the separator at the end of the word. */ - while (c->prompt_index != size) { - idx = ++c->prompt_index; - if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) - break; - } - - /* Back up to the end-of-word like vi. */ - if (options_get_number(oo, "status-keys") == MODEKEY_VI && - c->prompt_index != 0) - c->prompt_index--; - + case 'f'|KEYC_META: + separators = options_get_string(oo, "word-separators"); + status_prompt_forward_word(c, size, 0, separators); + goto changed; + case 'E'|KEYC_VI: + status_prompt_end_word(c, size, ""); + goto changed; + case 'e'|KEYC_VI: + separators = options_get_string(oo, "word-separators"); + status_prompt_end_word(c, size, separators); + goto changed; + case 'W'|KEYC_VI: + status_prompt_forward_word(c, size, 1, ""); + goto changed; + case 'w'|KEYC_VI: + separators = options_get_string(oo, "word-separators"); + status_prompt_forward_word(c, size, 1, separators); + goto changed; + case 'B'|KEYC_VI: + status_prompt_backward_word(c, ""); goto changed; - case 'b'|KEYC_META: case KEYC_LEFT|KEYC_CTRL: - ws = options_get_string(oo, "word-separators"); - - /* Find a non-separator. */ - while (c->prompt_index != 0) { - idx = --c->prompt_index; - if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) - break; - } - - /* Find the separator at the beginning of the word. */ - while (c->prompt_index != 0) { - idx = --c->prompt_index; - if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) { - /* Go back to the word. */ - c->prompt_index++; - break; - } - } + case 'b'|KEYC_META: + separators = options_get_string(oo, "word-separators"); + status_prompt_backward_word(c, separators); goto changed; case KEYC_UP: case '\020': /* C-p */ @@ -1339,8 +1442,10 @@ append_key: return (0); if (key <= 0x7f) utf8_set(&tmp, key); - else + else if (KEYC_IS_UNICODE(key)) utf8_to_data(key, &tmp); + else + return (0); c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2, sizeof *c->prompt_buffer); diff --git a/tmux.1 b/tmux.1 index d44dd591..2a77ba21 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1782,19 +1782,18 @@ commands) or when the cursor reaches the bottom (for scrolling commands). .Ql -no-clear variants do not clear the selection. .Pp -The next and previous word keys use space and the -.Ql - , -.Ql _ -and -.Ql @ -characters as word delimiters by default, but this can be adjusted by -setting the +The next and previous word keys skip over whitespace and treat consecutive +runs of either word separators or other letters as words. +Word separators can be customized with the .Em word-separators session option. Next word moves to the start of the next word, next word end to the end of the next word and previous word to the start of the previous word. The three next and previous space keys work similarly but use a space alone as the word separator. +Setting +.Em word-separators +to the empty string makes next/previous word equivalent to next/previous space. .Pp The jump commands enable quick movement within a line. For instance, typing @@ -3974,9 +3973,6 @@ If set to both, a bell and a message are produced. Sets the session's conception of what characters are considered word separators, for the purposes of the next and previous word commands in copy mode. -The default is -.Ql \ -_@ . -.El .Pp Available window options are: .Pp diff --git a/tmux.h b/tmux.h index 5fefcef5..917f4528 100644 --- a/tmux.h +++ b/tmux.h @@ -130,6 +130,7 @@ struct winlink; #define KEYC_CURSOR 0x04000000000000ULL #define KEYC_IMPLIED_META 0x08000000000000ULL #define KEYC_BUILD_MODIFIERS 0x10000000000000ULL +#define KEYC_VI 0x20000000000000ULL /* Masks for key bits. */ #define KEYC_MASK_MODIFIERS 0x00f00000000000ULL @@ -589,6 +590,9 @@ struct msg_write_close { int stream; }; +/* Character classes. */ +#define WHITESPACE " " + /* Mode keys. */ #define MODEKEY_EMACS 0 #define MODEKEY_VI 1 @@ -2639,7 +2643,7 @@ void grid_reader_cursor_end_of_line(struct grid_reader *, int, int); void grid_reader_cursor_next_word(struct grid_reader *, const char *); void grid_reader_cursor_next_word_end(struct grid_reader *, const char *); void grid_reader_cursor_previous_word(struct grid_reader *, const char *, - int); + int, int); int grid_reader_cursor_jump(struct grid_reader *, const struct utf8_data *); int grid_reader_cursor_jump_back(struct grid_reader *, diff --git a/utf8.c b/utf8.c index 88dbdb7a..55a68110 100644 --- a/utf8.c +++ b/utf8.c @@ -65,7 +65,7 @@ static struct utf8_index_tree utf8_index_tree = RB_INITIALIZER(utf8_index_tree); static u_int utf8_next_index; #define UTF8_GET_SIZE(uc) (((uc) >> 24) & 0x1f) -#define UTF8_GET_WIDTH(flags) (((uc) >> 29) - 1) +#define UTF8_GET_WIDTH(uc) (((uc) >> 29) - 1) #define UTF8_SET_SIZE(size) (((utf8_char)(size)) << 24) #define UTF8_SET_WIDTH(width) ((((utf8_char)(width)) + 1) << 29) diff --git a/window-copy.c b/window-copy.c index 423cce8f..2f33a23a 100644 --- a/window-copy.c +++ b/window-copy.c @@ -128,7 +128,7 @@ static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *, static void window_copy_cursor_next_word_end(struct window_mode_entry *, const char *, int); static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, - const char *, int, u_int *, u_int *); + const char *, u_int *, u_int *); static void window_copy_cursor_previous_word(struct window_mode_entry *, const char *, int); static void window_copy_scroll_up(struct window_mode_entry *, u_int); @@ -255,7 +255,7 @@ struct window_copy_mode_data { SEL_LINE, /* select one line at a time */ } selflag; - const char *ws; /* word separators */ + const char *separators; /* word separators */ u_int dx; /* drag start position */ u_int dy; @@ -1321,7 +1321,7 @@ window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_previous_word(wme, "}]) ", 1); + window_copy_cursor_previous_word(wme, close, 1); } continue; } @@ -1433,8 +1433,7 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_next_word_end(wme, "{[( ", - 0); + window_copy_cursor_next_word_end(wme, open, 0); continue; } /* For vi, continue searching for bracket until EOL. */ @@ -1506,7 +1505,7 @@ window_copy_cmd_next_space(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_next_word(wme, " "); + window_copy_cursor_next_word(wme, ""); return (WINDOW_COPY_CMD_NOTHING); } @@ -1517,7 +1516,7 @@ window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, " ", 0); + window_copy_cursor_next_word_end(wme, "", 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1525,13 +1524,13 @@ static enum window_copy_cmd_action window_copy_cmd_next_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; u_int np = wme->prefix; - const char *ws; + const char *separators; + + separators = options_get_string(cs->s->options, "word-separators"); - ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_next_word(wme, ws); + window_copy_cursor_next_word(wme, separators); return (WINDOW_COPY_CMD_NOTHING); } @@ -1539,13 +1538,13 @@ static enum window_copy_cmd_action window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; u_int np = wme->prefix; - const char *ws; + const char *separators; + + separators = options_get_string(cs->s->options, "word-separators"); - ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, ws, 0); + window_copy_cursor_next_word_end(wme, separators, 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1618,7 +1617,7 @@ window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_previous_word(wme, " ", 1); + window_copy_cursor_previous_word(wme, "", 1); return (WINDOW_COPY_CMD_NOTHING); } @@ -1626,13 +1625,13 @@ static enum window_copy_cmd_action window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; u_int np = wme->prefix; - const char *ws; + const char *separators; + + separators = options_get_string(cs->s->options, "word-separators"); - ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_previous_word(wme, ws, 1); + window_copy_cursor_previous_word(wme, separators, 1); return (WINDOW_COPY_CMD_NOTHING); } @@ -1778,18 +1777,20 @@ static enum window_copy_cmd_action window_copy_cmd_select_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; + struct options *session_options = cs->s->options; struct window_copy_mode_data *data = wme->data; u_int px, py, nextx, nexty; + data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; data->selflag = SEL_WORD; data->dx = data->cx; data->dy = screen_hsize(data->backing) + data->cy - data->oy; - data->ws = options_get_string(s->options, "word-separators"); - window_copy_cursor_previous_word(wme, data->ws, 0); + data->separators = options_get_string(session_options, + "word-separators"); + window_copy_cursor_previous_word(wme, data->separators, 0); px = data->cx; py = screen_hsize(data->backing) + data->cy - data->oy; data->selrx = px; @@ -1805,8 +1806,8 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) nexty++; } if (px >= window_copy_find_length(wme, py) || - !window_copy_in_set(wme, nextx, nexty, data->ws)) - window_copy_cursor_next_word_end(wme, data->ws, 1); + !window_copy_in_set(wme, nextx, nexty, WHITESPACE)) + window_copy_cursor_next_word_end(wme, data->separators, 1); else { window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1, 1)) @@ -3730,8 +3731,8 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, begin = 0; if (data->dy > yy || (data->dy == yy && data->dx > xx)) { /* Right to left selection. */ - window_copy_cursor_previous_word_pos(wme, data->ws, 0, - &xx, &yy); + window_copy_cursor_previous_word_pos(wme, + data->separators, &xx, &yy); begin = 1; /* Reset the end. */ @@ -3740,9 +3741,10 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, } else { /* Left to right selection. */ if (xx >= window_copy_find_length(wme, yy) || - !window_copy_in_set(wme, xx + 1, yy, data->ws)) + !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) { window_copy_cursor_next_word_end_pos(wme, - data->ws, &xx, &yy); + data->separators, &xx, &yy); + } /* Reset the start. */ data->selx = data->selrx; @@ -4666,19 +4668,19 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, hsize; - int keys; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; grid_reader_start(&gr, back_s->grid, px, py); - keys = options_get_number(oo, "mode-keys"); - if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators)) - grid_reader_cursor_right(&gr, 0, 0); - grid_reader_cursor_next_word_end(&gr, separators); - if (keys == MODEKEY_VI) + if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { + if (!grid_reader_in_set(&gr, WHITESPACE)) + grid_reader_cursor_right(&gr, 0, 0); + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_cursor_left(&gr, 1); + } else + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_get_cursor(&gr, &px, &py); *ppx = px; *ppy = py; @@ -4695,7 +4697,6 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, oldy, hsize; - int keys; px = data->cx; hsize = screen_hsize(back_s); @@ -4703,12 +4704,13 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); - keys = options_get_number(oo, "mode-keys"); - if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators)) - grid_reader_cursor_right(&gr, 0, 0); - grid_reader_cursor_next_word_end(&gr, separators); - if (keys == MODEKEY_VI) + if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { + if (!grid_reader_in_set(&gr, WHITESPACE)) + grid_reader_cursor_right(&gr, 0, 0); + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_cursor_left(&gr, 1); + } else + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), data->oy, oldy, px, py, no_reset); @@ -4717,7 +4719,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, /* Compute the previous place where a word begins. */ static void window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, - const char *separators, int already, u_int *ppx, u_int *ppy) + const char *separators, u_int *ppx, u_int *ppy) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; @@ -4729,7 +4731,8 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, py = hsize + data->cy - data->oy; grid_reader_start(&gr, back_s->grid, px, py); - grid_reader_cursor_previous_word(&gr, separators, already); + grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0, + /* stop_at_eol= */ 1); grid_reader_get_cursor(&gr, &px, &py); *ppx = px; *ppy = py; @@ -4744,6 +4747,11 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, oldy, hsize; + int stop_at_eol; + + stop_at_eol = + options_get_number(wme->wp->window->options, "mode-keys") + == MODEKEY_EMACS; px = data->cx; hsize = screen_hsize(back_s); @@ -4751,7 +4759,7 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); - grid_reader_cursor_previous_word(&gr, separators, already); + grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } @@ -4893,10 +4901,10 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) data->selflag = SEL_CHAR; switch (data->selflag) { case SEL_WORD: - if (data->ws != NULL) { + if (data->separators != NULL) { window_copy_update_cursor(wme, x, y); - window_copy_cursor_previous_word_pos(wme, data->ws, 0, - &x, &y); + window_copy_cursor_previous_word_pos(wme, + data->separators, &x, &y); y -= screen_hsize(data->backing) - data->oy; } window_copy_update_cursor(wme, x, y); From 3a5b576399e5f4dff4a975cadb9ef741de8e2011 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:57:06 +0000 Subject: [PATCH 0817/1006] Fix <= operator. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index 03c01dc4..17ce998d 100644 --- a/format.c +++ b/format.c @@ -3991,7 +3991,7 @@ format_replace_expression(struct format_modifier *mexp, result = (mleft < mright); break; case LESS_THAN_EQUAL: - result = (mleft > mright); + result = (mleft >= mright); break; } if (use_fp) From 8d75542986a54fdedda9d8ad1fc37dd83f7f05c9 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:58:08 +0000 Subject: [PATCH 0818/1006] Bump FORMAT_LOOOP_LIMIT and add a log message when hit, GitHub issue 2715. --- format.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/format.c b/format.c index 17ce998d..a10af458 100644 --- a/format.c +++ b/format.c @@ -103,7 +103,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_CHARACTER 0x10000 /* Limit on recursion. */ -#define FORMAT_LOOP_LIMIT 10 +#define FORMAT_LOOP_LIMIT 100 /* Format expand flags. */ #define FORMAT_EXPAND_TIME 0x1 @@ -3991,7 +3991,7 @@ format_replace_expression(struct format_modifier *mexp, result = (mleft < mright); break; case LESS_THAN_EQUAL: - result = (mleft >= mright); + result = (mleft <= mright); break; } if (use_fp) @@ -4441,8 +4441,10 @@ format_expand1(struct format_expand_state *es, const char *fmt) if (fmt == NULL || *fmt == '\0') return (xstrdup("")); - if (es->loop == FORMAT_LOOP_LIMIT) + if (es->loop == FORMAT_LOOP_LIMIT) { + format_log(es, "reached loop limit (%u)", FORMAT_LOOP_LIMIT); return (xstrdup("")); + } es->loop++; format_log(es, "expanding format: %s", fmt); From 43514f4af612405c0dc07abfdee54bae0889989e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:58:42 +0000 Subject: [PATCH 0819/1006] Fix rectangle selection, from Anindya Mukherjee, GitHub issue 2709. --- window-copy.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/window-copy.c b/window-copy.c index 2f33a23a..31875739 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1102,10 +1102,13 @@ static enum window_copy_cmd_action window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - for (; np != 0; np--) - window_copy_cursor_right(wme, 0); + for (; np != 0; np--) { + window_copy_cursor_right(wme, data->screen.sel != NULL && + data->rectflag); + } return (WINDOW_COPY_CMD_NOTHING); } @@ -4427,10 +4430,12 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int ox, oy, px, py; + int norectsel; + norectsel = data->screen.sel == NULL || !data->rectflag; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wme, oy); - if (data->cx != ox) { + if (norectsel && data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } @@ -4439,7 +4444,8 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) window_copy_other_end(wme); if (scroll_only || data->cy == 0) { - data->cx = data->lastcx; + if (norectsel) + data->cx = data->lastcx; window_copy_scroll_down(wme, 1); if (scroll_only) { if (data->cy == screen_size_y(s) - 1) @@ -4448,7 +4454,11 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) window_copy_redraw_lines(wme, data->cy, 2); } } else { - window_copy_update_cursor(wme, data->lastcx, data->cy - 1); + if (norectsel) { + window_copy_update_cursor(wme, data->lastcx, + data->cy - 1); + } else + window_copy_update_cursor(wme, data->cx, data->cy - 1); if (window_copy_update_selection(wme, 1, 0)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wme, data->cy, 1); @@ -4457,7 +4467,7 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) } } - if (data->screen.sel == NULL || !data->rectflag) { + if (norectsel) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || @@ -4494,10 +4504,12 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int ox, oy, px, py; + int norectsel; + norectsel = data->screen.sel == NULL || !data->rectflag; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wme, oy); - if (data->cx != ox) { + if (norectsel && data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } @@ -4506,17 +4518,22 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) window_copy_other_end(wme); if (scroll_only || data->cy == screen_size_y(s) - 1) { - data->cx = data->lastcx; + if (norectsel) + data->cx = data->lastcx; window_copy_scroll_up(wme, 1); if (scroll_only && data->cy > 0) window_copy_redraw_lines(wme, data->cy - 1, 2); } else { - window_copy_update_cursor(wme, data->lastcx, data->cy + 1); + if (norectsel) { + window_copy_update_cursor(wme, data->lastcx, + data->cy + 1); + } else + window_copy_update_cursor(wme, data->cx, data->cy + 1); if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy - 1, 2); } - if (data->screen.sel == NULL || !data->rectflag) { + if (norectsel) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || From 1e879ef458a21ea4dd266761757537fbbf567eee Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:59:08 +0000 Subject: [PATCH 0820/1006] Feature for the mouse since FreeBSD termcap does not have kmous. --- tmux.1 | 4 ++++ tty-features.c | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index 2a77ba21..d6a7e35f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3540,6 +3540,10 @@ Supports extended keys. Supports focus reporting. .It margins Supports DECSLRM margins. +.It mouse +Supports +.Xr xterm 1 +mouse sequences. .It overline Supports the overline SGR attribute. .It rectfill diff --git a/tty-features.c b/tty-features.c index b42cf74a..48ac51be 100644 --- a/tty-features.c +++ b/tty-features.c @@ -25,7 +25,6 @@ /* * Still hardcoded: - * - mouse (under kmous capability); * - default colours (under AX or op capabilities); * - AIX colours (under colors >= 16); * - alternate escape (if terminal is VT100-like). @@ -54,6 +53,17 @@ static const struct tty_feature tty_feature_title = { 0 }; +/* Terminal has mouse support. */ +static const char *tty_feature_mouse_capabilities[] = { + "kmous=\\E[M", + NULL +}; +static const struct tty_feature tty_feature_mouse = { + "mouse", + tty_feature_mouse_capabilities, + 0 +}; + /* Terminal can set the clipboard with OSC 52. */ static const char *tty_feature_clipboard_capabilities[] = { "Ms=\\E]52;%p1%s;%p2%s\\a", @@ -238,6 +248,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_extkeys, &tty_feature_focus, &tty_feature_margins, + &tty_feature_mouse, &tty_feature_overline, &tty_feature_rectfill, &tty_feature_rgb, @@ -338,7 +349,7 @@ tty_default_features(int *feat, const char *name, u_int version) const char *features; } table[] = { #define TTY_FEATURES_BASE_MODERN_XTERM \ - "256,RGB,bpaste,clipboard,strikethrough,title" + "256,RGB,bpaste,clipboard,mouse,strikethrough,title" { .name = "mintty", .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,margins,overline,usstyle" @@ -348,7 +359,7 @@ tty_default_features(int *feat, const char *name, u_int version) ",ccolour,cstyle,focus,overline,usstyle" }, { .name = "rxvt-unicode", - .features = "256,bpaste,ccolour,cstyle,title" + .features = "256,bpaste,ccolour,cstyle,mouse,title" }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM From a35c897f0f16a9dc578c9ebfe4c4c4b0d73002aa Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:59:31 +0000 Subject: [PATCH 0821/1006] Do not clear region based on current cursor position, this is not necessary anymore and causes problems, GitHub issue 2735. --- tty.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tty.c b/tty.c index 45868fc1..4b1b6777 100644 --- a/tty.c +++ b/tty.c @@ -1005,13 +1005,8 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) return; } - if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { - for (i = ctx->ocy; i < ctx->sy; i++) - tty_draw_pane(tty, ctx, i); - } else { - for (i = ctx->orupper; i <= ctx->orlower; i++) - tty_draw_pane(tty, ctx, i); - } + for (i = ctx->orupper; i <= ctx->orlower; i++) + tty_draw_pane(tty, ctx, i); } /* Is this position visible in the pane? */ From d25738b61e732dc8a35ca7c7866329b325e80335 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Jun 2021 09:16:14 +0100 Subject: [PATCH 0822/1006] Update CHANGES. --- CHANGES | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGES b/CHANGES index 8b23a0fb..3d9304c5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,9 +1,29 @@ CHANGES FROM 3.2 TO 3.3 +* Change cursor style handling so tmux understands which sequences contain + blinking and sets the flag appropriately, means that it works whether cnorm + disables blinking or not. This now matches xterm's behaviour. + * More accurate vi(1) word navigation in copy mode and on the status line. This changes the meaning of the word-separators option - setting it to the empty string is equivalent to the previous behavior. +* Add an "always" value for the "extended-keys" option; if set then tmux will + forward extended keys to applications even if they do not request them. + +* Add a "mouse" terminal feature so tmux can enable the mouse on terminals + where it is known to be supported even if terminfo(5) says otherwise. + +* Add -F for command-prompt and use it to fix "Rename" on the window menu. + +* Do not expand the filename given to -f so it can contain colons. + +* Add different command historys for different types of prompts ("command", + "search" etc). From Anindya Mukherjee. + +* Fixes for problems with extended keys and modifiers, scroll region, + source-file, crosscompiling, format modifiers and other minor issues. + CHANGES FROM 3.1c TO 3.2 * Add a flag to disable keys to close a message. From 2ab53d30d0bcfd920f9417fce0ff5896d768b77e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Jun 2021 09:17:46 +0100 Subject: [PATCH 0823/1006] 3.2a version. --- CHANGES | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 209d7d31..613fe40c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +CHANGES FROM 3.2 TO 3.2a + +XXX + CHANGES FROM 3.1c TO 3.2 * Add a flag to disable keys to close a message. diff --git a/configure.ac b/configure.ac index c1915f50..a8d3787c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.2) +AC_INIT([tmux], 3.2a) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From bab7a9a085293aa50e6994376e1beed57ba44b4b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Apr 2021 12:26:34 +0000 Subject: [PATCH 0824/1006] Change how extended ctrl keys are processed to fix C-S-Tab and C-;. --- tty-keys.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index c0aceb32..040e7005 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -955,23 +955,19 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, */ if (nkey & KEYC_CTRL) { onlykey = (nkey & KEYC_MASK_KEY); - if (onlykey < 32) { - if (onlykey != 9) - onlykey = (nkey & ~KEYC_CTRL); - else - onlykey = (9|KEYC_CTRL); - } else { - if (onlykey >= 97 && onlykey <= 122) - onlykey -= 96; - else if (onlykey >= 64 && onlykey <= 95) - onlykey -= 64; - else if (onlykey == 32) - onlykey = 0; - else if (onlykey == 63) - onlykey = 127; - onlykey |= ((nkey & KEYC_MASK_MODIFIERS) & ~KEYC_CTRL); - } - nkey = onlykey; + if (onlykey < 32 && onlykey != 9) + /* nothing */; + else if (onlykey >= 97 && onlykey <= 122) + onlykey -= 96; + else if (onlykey >= 64 && onlykey <= 95) + onlykey -= 64; + else if (onlykey == 32) + onlykey = 0; + else if (onlykey == 63) + onlykey = 127; + else + onlykey |= KEYC_CTRL; + nkey = onlykey|((nkey & KEYC_MASK_MODIFIERS) & ~KEYC_CTRL); } if (log_get_level() != 0) { From 57d5f675527d860595911b8ff480f500b93eb24a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Apr 2021 16:00:47 +0000 Subject: [PATCH 0825/1006] Include modifiers when looking up an individual key. --- cmd-list-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index ca4bf752..91715f93 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -165,7 +165,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "invalid key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } - only &= KEYC_MASK_KEY; + only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS); } tablename = args_get(args, 'T'); From d8639784647045593353270ae1b04ffb288533ff Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 15 Apr 2021 05:38:11 +0000 Subject: [PATCH 0826/1006] %begin now has three arguments, not two. GitHubs issue 2646. --- tmux.1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index 2343ab89..84cddc55 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6116,12 +6116,13 @@ and matching .Em %end or .Em %error -have two arguments: an integer time (as seconds from epoch) and command number. +have three arguments: an integer time (as seconds from epoch), command number and +flags (currently not used). For example: .Bd -literal -offset indent -%begin 1363006971 2 +%begin 1363006971 2 1 0: ksh* (1 panes) [80x24] [layout b25f,80x24,0,0,2] @2 (active) -%end 1363006971 2 +%end 1363006971 2 1 .Ed .Pp The From 0431d4d6396e01fb333bc2bd0f1716c81b450dcf Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 15 Apr 2021 06:45:19 +0100 Subject: [PATCH 0827/1006] Add crosscompiling fallbacks, from Hasso Tepper. --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index a8d3787c..bfbec1d4 100644 --- a/configure.ac +++ b/configure.ac @@ -163,6 +163,7 @@ AC_RUN_IFELSE([AC_LANG_PROGRAM( [return (reallocarray(NULL, 1, 1) == NULL);] )], AC_MSG_RESULT(yes), + [AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])], [AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])] ) AC_MSG_CHECKING([for working recallocarray]) @@ -171,6 +172,7 @@ AC_RUN_IFELSE([AC_LANG_PROGRAM( [return (recallocarray(NULL, 1, 1, 1) == NULL);] )], AC_MSG_RESULT(yes), + [AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])], [AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])] ) From a11aa870b397f1711a36666582d41492933d75ff Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 15 Apr 2021 08:22:36 +0100 Subject: [PATCH 0828/1006] Handle modifier 9 as Meta, GitHub issue 2647. --- tty-keys.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index 040e7005..5bf4e4a5 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -252,7 +252,8 @@ static const key_code tty_default_xterm_modifiers[] = { KEYC_CTRL, KEYC_SHIFT|KEYC_CTRL, KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, - KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, + KEYC_META|KEYC_IMPLIED_META }; /* @@ -944,6 +945,9 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, case 8: nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); break; + case 9: + nkey |= (KEYC_META|KEYC_IMPLIED_META); + break; default: *key = KEYC_NONE; break; From a25af7d0f3347dc7664d7c1c13795eb05178895d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 16 Apr 2021 11:59:08 +0100 Subject: [PATCH 0829/1006] Adjust latest client when a client detaches, GitHub issue 2657. --- server-client.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index d3ffd682..0f8ad687 100644 --- a/server-client.c +++ b/server-client.c @@ -43,6 +43,7 @@ static void server_client_check_modes(struct client *); static void server_client_set_title(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); +static void server_client_update_latest(struct client *); static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch_command(struct client *, struct imsg *); @@ -271,6 +272,40 @@ server_client_open(struct client *c, char **cause) return (0); } +/* Lost an attached client. */ +static void +server_client_attached_lost(struct client *c) +{ + struct session *s = c->session; + struct window *w; + struct client *loop; + struct client *found; + + log_debug("lost attached client %p", c); + + /* + * By this point the session in the client has been cleared so walk all + * windows to find any with this client as the latest. + */ + RB_FOREACH(w, windows, &windows) { + if (w->latest != c) + continue; + + found = NULL; + TAILQ_FOREACH(loop, &clients, entry) { + s = loop->session; + if (loop == c || s == NULL || s->curw->window != w) + continue; + if (found == NULL || + timercmp(&loop->activity_time, &found->activity_time, + >)) + found = loop; + } + if (found != NULL) + server_client_update_latest(found); + } +} + /* Lost a client. */ void server_client_lost(struct client *c) @@ -296,8 +331,10 @@ server_client_lost(struct client *c) TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); - if (c->flags & CLIENT_ATTACHED) + if (c->flags & CLIENT_ATTACHED) { + server_client_attached_lost(c); notify_client("client-detached", c); + } if (c->flags & CLIENT_CONTROL) control_stop(c); From 16b497e12b971443913e271f4d3e320b8693d411 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 16 Apr 2021 12:07:54 +0100 Subject: [PATCH 0830/1006] Apple have broken strtonum so check it works, from Teubel Gyorgy. --- configure.ac | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index bfbec1d4..158e228c 100644 --- a/configure.ac +++ b/configure.ac @@ -150,10 +150,19 @@ AC_REPLACE_FUNCS([ \ strlcpy \ strndup \ strsep \ - strtonum \ ]) AC_FUNC_STRNLEN +# Check if strtonum works. +AC_MSG_CHECKING([for working strtonum]) +AC_RUN_IFELSE([AC_LANG_PROGRAM( + [#include ], + [return (strtonum("0", 0, 1, NULL) == 0 ? 0 : 1);] + )], + [AC_DEFINE(HAVE_STRTONUM) AC_MSG_RESULT(yes)], + [AC_LIBOBJ(strtonum) AC_MSG_RESULT(no)] +) + # Clang sanitizers wrap reallocarray even if it isn't available on the target # system. When compiled it always returns NULL and crashes the program. To # detect this we need a more complicated test. From 9865ad27a53e69aa651608e356c5335e76bc54cf Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 16 Apr 2021 12:12:50 +0100 Subject: [PATCH 0831/1006] Fix display-menu -xR, from Alexis Hildebrandt. --- cmd-display-menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 5a5aabcd..de423e68 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -205,7 +205,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, if (xp == NULL || strcmp(xp, "C") == 0) xp = "#{popup_centre_x}"; else if (strcmp(xp, "R") == 0) - xp = "#{popup_right}"; + xp = "#{popup_pane_right}"; else if (strcmp(xp, "P") == 0) xp = "#{popup_pane_left}"; else if (strcmp(xp, "M") == 0) From 33d4f854c04b993c57195030ca33429256919949 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 18 Apr 2021 08:47:11 +0100 Subject: [PATCH 0832/1006] back-to-indentation fixes, from Anindya Mukherjee. --- grid-reader.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/grid-reader.c b/grid-reader.c index 89fe90fb..df0dd450 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -371,19 +371,26 @@ void grid_reader_cursor_back_to_indentation(struct grid_reader *gr) { struct grid_cell gc; - u_int px, py, xx, yy; + u_int px, py, xx, yy, oldx, oldy; yy = gr->gd->hsize + gr->gd->sy - 1; + oldx = gr->cx; + oldy = gr->cy; grid_reader_cursor_start_of_line(gr, 1); for (py = gr->cy; py <= yy; py++) { xx = grid_line_length(gr->gd, py); for (px = 0; px < xx; px++) { grid_get_cell(gr->gd, px, py, &gc); - if (gc.data.size != 1 || *gc.data.data != ' ') - break; + if (gc.data.size != 1 || *gc.data.data != ' ') { + gr->cx = px; + gr->cy = py; + return; + } } if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED) break; } + gr->cx = oldx; + gr->cy = oldy; } From d8c006925464ce50d92aee913aa452acf2033173 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 18 Apr 2021 08:48:03 +0100 Subject: [PATCH 0833/1006] Use = not ==, from Leonardo Taccari. --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 158e228c..9bbfd49e 100644 --- a/configure.ac +++ b/configure.ac @@ -34,10 +34,10 @@ AC_ARG_VAR( # Set up convenient fuzzing defaults before initializing compiler. if test "x$enable_fuzzing" = xyes; then AC_DEFINE(NEED_FUZZING) - test "x$CC" == x && CC=clang - test "x$FUZZING_LIBS" == x && \ + test "x$CC" = x && CC=clang + test "x$FUZZING_LIBS" = x && \ FUZZING_LIBS="-fsanitize=fuzzer" - test "x$SAVED_CFLAGS" == x && \ + test "x$SAVED_CFLAGS" = x && \ AM_CFLAGS="-g -fsanitize=fuzzer-no-link,address" fi From 825feac9f8f8aa10ec69932ab6ea52e58586e058 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 20 Apr 2021 06:35:54 +0100 Subject: [PATCH 0834/1006] Add another couple of keys needed for extended keys, GitHub issue 2658. --- key-bindings.c | 6 ++++++ key-string.c | 10 +++++++--- tty-keys.c | 5 ++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index b47f6ff7..467c7f93 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -215,6 +215,9 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, if (repeat) bd->flags |= KEY_BINDING_REPEAT; bd->cmdlist = cmdlist; + + log_debug("%s: %#llx %s = %s", __func__, bd->key, + key_string_lookup_key(bd->key, 1), cmd_list_print(bd->cmdlist, 0)); } void @@ -231,6 +234,9 @@ key_bindings_remove(const char *name, key_code key) if (bd == NULL) return; + log_debug("%s: %#llx %s", __func__, bd->key, + key_string_lookup_key(bd->key, 1)); + RB_REMOVE(key_bindings, &table->key_bindings, bd); key_bindings_free(bd); diff --git a/key-string.c b/key-string.c index 8d60f132..c24a33fc 100644 --- a/key-string.c +++ b/key-string.c @@ -164,7 +164,7 @@ key_string_get_modifiers(const char **string) key_code key_string_lookup_string(const char *string) { - static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177"; + static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177`/"; key_code key, modifiers; u_int u, i; struct utf8_data ud, *udp; @@ -238,8 +238,12 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && - strchr(other, key) == NULL) { + if (key < KEYC_BASE && + (modifiers & KEYC_CTRL) && + strchr(other, key) == NULL && + key != 9 && + key != 13 && + key != 27) { if (key >= 97 && key <= 122) key -= 96; else if (key >= 64 && key <= 95) diff --git a/tty-keys.c b/tty-keys.c index 5bf4e4a5..3012be3d 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -959,7 +959,10 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, */ if (nkey & KEYC_CTRL) { onlykey = (nkey & KEYC_MASK_KEY); - if (onlykey < 32 && onlykey != 9) + if (onlykey < 32 && + onlykey != 9 && + onlykey != 13 && + onlykey != 27) /* nothing */; else if (onlykey >= 97 && onlykey <= 122) onlykey -= 96; From 736a276cc9bdb97bf35caa1995a54d328a839102 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 20 Apr 2021 06:37:01 +0100 Subject: [PATCH 0835/1006] Minor CHANGES and tmux.1 fixed, from Daniel Hahler, GitHub issue 2664. --- CHANGES | 10 +++++----- tmux.1 | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 613fe40c..e50c061b 100644 --- a/CHANGES +++ b/CHANGES @@ -33,7 +33,7 @@ CHANGES FROM 3.1c TO 3.2 * Add a -S flag to new-window to make it select the existing window if one with the given name already exists rather than failing with an error. -* Addd a format modifier to check if a window or session name exists (N/w or +* Add a format modifier to check if a window or session name exists (N/w or N/s). * Add compat clock_gettime for older macOS. @@ -65,7 +65,7 @@ CHANGES FROM 3.1c TO 3.2 an option on all panes. * Make replacement of ##s consistent when drawing formats, whether followed by - [ or not. Add a flag (e) to the q: format modifier to double up #s + [ or not. Add a flag (e) to the q: format modifier to double up #s. * Add -N flag to display-panes to ignore keys. @@ -269,7 +269,7 @@ CHANGES FROM 3.1c TO 3.2 * Wait until the initial command sequence is done before sending a device attributes request and other bits that prompt a reply from the terminal. This - means that stray relies are not left on the terminal if the command has + means that stray replies are not left on the terminal if the command has attached and then immediately detached and tmux will not be around to receive them. @@ -284,7 +284,7 @@ CHANGES FROM 3.1c TO 3.2 window-renamed window-unlinked - And these now pane options: + And these are now pane options: pane-died pane-exited @@ -359,7 +359,7 @@ CHANGES FROM 3.1c TO 3.2 * Add a default binding for button 2 to paste. * Add -d flag to run-shell to delay before running the command and allow it to - run without a command so it just delays. + be used without a command so it just delays. * Add C-g to cancel command prompt with vi keys as well as emacs, and q in command mode. diff --git a/tmux.1 b/tmux.1 index 84cddc55..c4fc06ba 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3501,8 +3501,8 @@ capabilities to be set instead, is intended for classes of functionality supported in a standard way but not reported by .Xr terminfo 5 . -Care must be taken only to configure this with features the terminal actually -support. +Care must be taken to configure this only with features the terminal actually +supports. .Pp This is an array option where each entry is a colon-separated string made up of a terminal type pattern (matched using From 5107e848977640398843e720e26d45a66954e7f9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Apr 2021 09:32:48 +0100 Subject: [PATCH 0836/1006] Add an "always" value to the extended-keys option to always forward these keys to applications inside tmux. --- input.c | 2 ++ options-table.c | 6 +++++- screen.c | 4 +++- tmux.1 | 21 +++++++++++++++++---- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/input.c b/input.c index f6aeb027..b599f27d 100644 --- a/input.c +++ b/input.c @@ -1390,6 +1390,8 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_MODSET: n = input_get(ictx, 0, 0, 0); m = input_get(ictx, 1, 0, 0); + if (options_get_number(global_options, "extended-keys") == 2) + break; if (n == 0 || (n == 4 && m == 0)) screen_write_mode_clear(sctx, MODE_KEXTENDED); else if (n == 4 && (m == 1 || m == 2)) diff --git a/options-table.c b/options-table.c index b185969c..8264d1b7 100644 --- a/options-table.c +++ b/options-table.c @@ -74,6 +74,9 @@ static const char *options_table_remain_on_exit_list[] = { static const char *options_table_detach_on_destroy_list[] = { "off", "on", "no-detached", NULL }; +static const char *options_table_extended_keys_list[] = { + "off", "on", "always", NULL +}; /* Status line format. */ #define OPTIONS_TABLE_STATUS_FORMAT1 \ @@ -266,8 +269,9 @@ const struct options_table_entry options_table[] = { }, { .name = "extended-keys", - .type = OPTIONS_TABLE_FLAG, + .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SERVER, + .choices = options_table_extended_keys_list, .default_num = 0, .text = "Whether to request extended key sequences from terminals " "that support it." diff --git a/screen.c b/screen.c index 2b9d70de..2b83c285 100644 --- a/screen.c +++ b/screen.c @@ -100,7 +100,9 @@ screen_reinit(struct screen *s) s->rupper = 0; s->rlower = screen_size_y(s) - 1; - s->mode = MODE_CURSOR | MODE_WRAP; + s->mode = MODE_CURSOR|MODE_WRAP; + if (options_get_number(global_options, "extended-keys") == 2) + s->mode |= MODE_KEXTENDED; if (s->saved_grid != NULL) screen_alternate_off(s, NULL, 0); diff --git a/tmux.1 b/tmux.1 index c4fc06ba..6967c769 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3418,11 +3418,24 @@ sessions. .Xc If enabled, the server will exit when there are no attached clients. .It Xo Ic extended-keys -.Op Ic on | off +.Op Ic on | off | always .Xc -When enabled, extended keys are requested from the terminal and if supported -are recognised by -.Nm . +When +.Ic on +or +.Ic always , +the escape sequence to enable extended keys is sent to the terminal, if +.Nm +knows that it is supported. +.Nm +always recognises extended keys itself. +If this option is +.Ic on , +.Nm +will only forward extended keys to applications when they request them; if +.Ic always , +.Nm +will always forward the keys. .It Xo Ic focus-events .Op Ic on | off .Xc From 4cf595a4023acfd9fce2e589f0d01de3e7b5eff7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 21 Apr 2021 09:47:03 +0100 Subject: [PATCH 0837/1006] Include current client in size calcultion for new sessions, GitHub issue 2662. --- cmd-new-session.c | 1 + resize.c | 47 +++++++++++++++++++++++++++++++++-------------- status.c | 2 ++ window.c | 2 ++ 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index cc3494de..c47d66cd 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -269,6 +269,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; + sc.tc = c; sc.name = args_get(args, 'n'); sc.argc = args->argc; diff --git a/resize.c b/resize.c index 172cbcb5..66cb9430 100644 --- a/resize.c +++ b/resize.c @@ -108,17 +108,19 @@ clients_with_window(struct window *w) } 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) +clients_calculate_size(int type, int current, struct client *c, + 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) + if (type == WINDOW_SIZE_MANUAL) { + log_debug("%s: type is manual", __func__); return (0); + } /* * Start comparing with 0 for largest and UINT_MAX for smallest or @@ -139,18 +141,24 @@ clients_calculate_size(int type, int current, struct session *s, /* Loop over the clients and work out the size. */ TAILQ_FOREACH(loop, &clients, entry) { - if (ignore_client_size(loop)) + if (loop != c && ignore_client_size(loop)) { + log_debug("%s: ignoring %s", __func__, loop->name); continue; - if (skip_client(loop, type, current, s, w)) + } + if (loop != c && skip_client(loop, type, current, s, w)) { + log_debug("%s: skipping %s", __func__, loop->name); 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) + if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest) { + log_debug("%s: %s is not latest", __func__, loop->name); continue; + } /* Work out this client's size. */ cx = loop->tty.sx; @@ -175,16 +183,24 @@ clients_calculate_size(int type, int current, struct session *s, *xpixel = loop->tty.xpixel; *ypixel = loop->tty.ypixel; } + log_debug("%s: after %s (%ux%u), size is %ux%u", __func__, + loop->name, cx, cy, *sx, *sy); } /* Return whether a suitable size was found. */ - if (type == WINDOW_SIZE_LARGEST) + if (type == WINDOW_SIZE_LARGEST) { + log_debug("%s: type is largest", __func__); return (*sx != 0 && *sy != 0); + } + if (type == WINDOW_SIZE_LATEST) + log_debug("%s: type is latest", __func__); + else + log_debug("%s: type is smallest", __func__); return (*sx != UINT_MAX && *sy != UINT_MAX); } static int -default_window_size_skip_client (struct client *loop, int type, +default_window_size_skip_client(struct client *loop, int type, __unused int current, struct session *s, struct window *w) { /* @@ -221,23 +237,25 @@ default_window_size(struct client *c, struct session *s, struct window *w, *sy = c->tty.sy - status_line_size(c); *xpixel = c->tty.xpixel; *ypixel = c->tty.ypixel; + log_debug("%s: using %ux%u from %s", __func__, *sx, *sy, + c->name); goto done; } - if (w == NULL) - type = WINDOW_SIZE_MANUAL; } /* * 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, + if (!clients_calculate_size(type, 0, c, 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; } + log_debug("%s: using %ux%u from default-size", __func__, *sx, + *sy); } done: @@ -250,6 +268,7 @@ done: *sy = WINDOW_MINIMUM; if (*sy > WINDOW_MAXIMUM) *sy = WINDOW_MAXIMUM; + log_debug("%s: resulting size is %ux%u", __func__, *sx, *sy); } static int @@ -289,7 +308,7 @@ recalculate_size(struct window *w, int now) current = options_get_number(w->options, "aggressive-resize"); /* Look for a suitable client and get the new size. */ - changed = clients_calculate_size(type, current, NULL, w, + changed = clients_calculate_size(type, current, NULL, NULL, w, recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel); /* diff --git a/status.c b/status.c index f9786f4b..271e1afa 100644 --- a/status.c +++ b/status.c @@ -226,6 +226,8 @@ status_line_size(struct client *c) if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) return (0); + if (s == NULL) + return (options_get_number(global_s_options, "status")); return (s->statuslines); } diff --git a/window.c b/window.c index 38c1913c..d8cff590 100644 --- a/window.c +++ b/window.c @@ -329,6 +329,8 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) window_update_activity(w); + log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy, + w->xpixel, w->ypixel); return (w); } From ddc67152a5e8761c3dd5b8807c7fa1b997292e96 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 22 Apr 2021 09:01:22 +0100 Subject: [PATCH 0838/1006] Three changes to fix problems with xterm in VT340 mode, reported by Thomas Sattler. 1) Do not include the DECSLRM or DECFRA features for xterm; they will be added instead if secondary DA responds as VT420 (this happens already). 2) Set or reset the individual flags after terminal-overrides is applied, so the user can properly disable them. 3) Add a capability for DECFRA ("Rect"). --- tmux.1 | 4 +++ tmux.h | 1 + tty-features.c | 13 +++++-- tty-term.c | 98 ++++++++++++++++++++++++++++++++------------------ 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/tmux.1 b/tmux.1 index 6967c769..522de231 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6049,6 +6049,10 @@ Disable and enable focus reporting. These are set automatically if the .Em XT capability is present. +.It Em \&Rect +Tell +.Nm +that the terminal supports rectangle operations. .It Em \&Smol Enable the overline attribute. .It Em \&Smulx diff --git a/tmux.h b/tmux.h index 6be5b302..a67b30b9 100644 --- a/tmux.h +++ b/tmux.h @@ -452,6 +452,7 @@ enum tty_code_code { TTYC_MS, TTYC_OL, TTYC_OP, + TTYC_RECT, TTYC_REV, TTYC_RGB, TTYC_RI, diff --git a/tty-features.c b/tty-features.c index f167a2d3..b42cf74a 100644 --- a/tty-features.c +++ b/tty-features.c @@ -218,9 +218,13 @@ static const struct tty_feature tty_feature_margins = { }; /* Terminal supports DECFRA rectangle fill. */ +static const char *tty_feature_rectfill_capabilities[] = { + "Rect", + NULL +}; static const struct tty_feature tty_feature_rectfill = { "rectfill", - NULL, + tty_feature_rectfill_capabilities, TERM_DECFRA }; @@ -351,8 +355,13 @@ tty_default_features(int *feat, const char *name, u_int version) ",cstyle,extkeys,margins,sync" }, { .name = "XTerm", + /* + * xterm also supports DECSLRM and DECFRA, but they can be + * disabled so not set it here - they will be added if + * secondary DA shows VT420. + */ .features = TTY_FEATURES_BASE_MODERN_XTERM - ",ccolour,cstyle,extkeys,focus,margins,rectfill" + ",ccolour,cstyle,extkeys,focus" } }; u_int i; diff --git a/tty-term.c b/tty-term.c index 1d9b36da..4a7e7415 100644 --- a/tty-term.c +++ b/tty-term.c @@ -251,6 +251,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_MS] = { TTYCODE_STRING, "Ms" }, [TTYC_OL] = { TTYCODE_STRING, "ol" }, [TTYC_OP] = { TTYCODE_STRING, "op" }, + [TTYC_RECT] = { TTYCODE_STRING, "Rect" }, [TTYC_REV] = { TTYCODE_STRING, "rev" }, [TTYC_RGB] = { TTYCODE_FLAG, "RGB" }, [TTYC_RIN] = { TTYCODE_STRING, "rin" }, @@ -434,10 +435,11 @@ tty_term_apply_overrides(struct tty_term *term) struct options_entry *o; struct options_array_item *a; union options_value *ov; - const char *s; + const char *s, *acs; size_t offset; char *first; + /* Update capabilities from the option. */ o = options_get_only(global_options, "terminal-overrides"); a = options_array_first(o); while (a != NULL) { @@ -450,6 +452,64 @@ tty_term_apply_overrides(struct tty_term *term) tty_term_apply(term, s + offset, 0); a = options_array_next(a); } + + /* Update the RGB flag if the terminal has RGB colours. */ + if (tty_term_has(term, TTYC_SETRGBF) && + tty_term_has(term, TTYC_SETRGBB)) + term->flags |= TERM_RGBCOLOURS; + else + term->flags &= ~TERM_RGBCOLOURS; + log_debug("RGBCOLOURS flag is %d", !!(term->flags & TERM_RGBCOLOURS)); + + /* + * Set or clear the DECSLRM flag if the terminal has the margin + * capabilities. + */ + if (tty_term_has(term, TTYC_CMG) && tty_term_has(term, TTYC_CLMG)) + term->flags |= TERM_DECSLRM; + else + term->flags &= ~TERM_DECSLRM; + log_debug("DECSLRM flag is %d", !!(term->flags & TERM_DECSLRM)); + + /* + * Set or clear the DECFRA flag if the terminal has the rectangle + * capability. + */ + if (tty_term_has(term, TTYC_RECT)) + term->flags |= TERM_DECFRA; + else + term->flags &= ~TERM_DECFRA; + log_debug("DECFRA flag is %d", !!(term->flags & TERM_DECFRA)); + + /* + * Terminals without am (auto right margin) wrap at at $COLUMNS - 1 + * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). + * + * Terminals without xenl (eat newline glitch) ignore a newline beyond + * the right edge of the terminal, but tmux doesn't care about this - + * it always uses absolute only moves the cursor with a newline when + * also sending a linefeed. + * + * This is irritating, most notably because it is painful to write to + * the very bottom-right of the screen without scrolling. + * + * Flag the terminal here and apply some workarounds in other places to + * do the best possible. + */ + if (!tty_term_flag(term, TTYC_AM)) + term->flags |= TERM_NOAM; + else + term->flags &= ~TERM_NOAM; + log_debug("NOAM flag is %d", !!(term->flags & TERM_NOAM)); + + /* Generate ACS table. If none is present, use nearest ASCII. */ + memset(term->acs, 0, sizeof term->acs); + if (tty_term_has(term, TTYC_ACSC)) + acs = tty_term_string(term, TTYC_ACSC); + else + acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y~."; + for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) + term->acs[(u_char) acs[0]][0] = acs[1]; } struct tty_term * @@ -463,7 +523,7 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps, struct options_array_item *a; union options_value *ov; u_int i, j; - const char *s, *acs, *value; + const char *s, *value; size_t offset, namelen; char *first; @@ -566,40 +626,10 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps, (!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); - tty_term_apply_overrides(term); - - /* - * Terminals without am (auto right margin) wrap at at $COLUMNS - 1 - * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). - * - * Terminals without xenl (eat newline glitch) ignore a newline beyond - * the right edge of the terminal, but tmux doesn't care about this - - * it always uses absolute only moves the cursor with a newline when - * also sending a linefeed. - * - * This is irritating, most notably because it is painful to write to - * the very bottom-right of the screen without scrolling. - * - * Flag the terminal here and apply some workarounds in other places to - * do the best possible. - */ - if (!tty_term_flag(term, TTYC_AM)) - term->flags |= TERM_NOAM; - - /* Generate ACS table. If none is present, use nearest ASCII. */ - memset(term->acs, 0, sizeof term->acs); - if (tty_term_has(term, TTYC_ACSC)) - acs = tty_term_string(term, TTYC_ACSC); - else - acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y~."; - for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) - term->acs[(u_char) acs[0]][0] = acs[1]; + if (tty_apply_features(term, *feat)) + tty_term_apply_overrides(term); /* Log the capabilities. */ for (i = 0; i < tty_term_ncodes(); i++) From fb52921a86f15cd781bc2b2e65a9c6dd2a437f06 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 23 Apr 2021 13:41:49 +0100 Subject: [PATCH 0839/1006] Do not count client if no window. --- resize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resize.c b/resize.c index 66cb9430..38cda23d 100644 --- a/resize.c +++ b/resize.c @@ -136,7 +136,7 @@ clients_calculate_size(int type, int current, struct client *c, * For latest, count the number of clients with this window. We only * care if there is more than one. */ - if (type == WINDOW_SIZE_LATEST) + if (type == WINDOW_SIZE_LATEST && w != NULL) n = clients_with_window(w); /* Loop over the clients and work out the size. */ From 7c28597e0f0a000e46aa7f42c00c9750fe6cff61 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 27 Apr 2021 08:29:54 +0100 Subject: [PATCH 0840/1006] Mention S- for Shift, GitHub issue 2683. --- tmux.1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tmux.1 b/tmux.1 index 522de231..22982b11 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2953,6 +2953,8 @@ Ctrl keys may be prefixed with .Ql C- or .Ql ^ , +Shift keys with +.Ql S- and Alt (meta) with .Ql M- . In addition, the following special key names are accepted: From cb2943faab2053719b13306f95ed58a2dae64e81 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 28 Apr 2021 09:16:30 +0100 Subject: [PATCH 0841/1006] Change resize timers and flags into one timer and a queue to fix problems with vim when resized multiple times. GitHub issue 2677. --- server-client.c | 117 +++++++++++++++++++++++------------------------- tmux.h | 25 ++++++++--- window.c | 51 +++++++++------------ 3 files changed, 94 insertions(+), 99 deletions(-) diff --git a/server-client.c b/server-client.c index 0f8ad687..e74d893d 100644 --- a/server-client.c +++ b/server-client.c @@ -1447,84 +1447,79 @@ server_client_resize_timer(__unused int fd, __unused short events, void *data) evtimer_del(&wp->resize_timer); } -/* Start the resize timer. */ -static void -server_client_start_resize_timer(struct window_pane *wp) -{ - struct timeval tv = { .tv_usec = 250000 }; - - log_debug("%s: %%%u resize timer started", __func__, wp->id); - evtimer_add(&wp->resize_timer, &tv); -} - -/* Force timer event. */ -static void -server_client_force_timer(__unused int fd, __unused short events, void *data) -{ - struct window_pane *wp = data; - - log_debug("%s: %%%u force timer expired", __func__, wp->id); - evtimer_del(&wp->force_timer); - wp->flags |= PANE_RESIZENOW; -} - -/* Start the force timer. */ -static void -server_client_start_force_timer(struct window_pane *wp) -{ - struct timeval tv = { .tv_usec = 10000 }; - - log_debug("%s: %%%u force timer started", __func__, wp->id); - evtimer_add(&wp->force_timer, &tv); -} - /* Check if pane should be resized. */ static void server_client_check_pane_resize(struct window_pane *wp) { + struct window_pane_resize *r; + struct window_pane_resize *r1; + struct window_pane_resize *first; + struct window_pane_resize *last; + struct timeval tv = { .tv_usec = 250000 }; + + if (TAILQ_EMPTY(&wp->resize_queue)) + return; + if (!event_initialized(&wp->resize_timer)) evtimer_set(&wp->resize_timer, server_client_resize_timer, wp); - if (!event_initialized(&wp->force_timer)) - evtimer_set(&wp->force_timer, server_client_force_timer, wp); - - if (~wp->flags & PANE_RESIZE) + if (evtimer_pending(&wp->resize_timer, NULL)) return; + log_debug("%s: %%%u needs to be resized", __func__, wp->id); - - if (evtimer_pending(&wp->resize_timer, NULL)) { - log_debug("%s: %%%u resize timer is running", __func__, wp->id); - return; + TAILQ_FOREACH(r, &wp->resize_queue, entry) { + log_debug("queued resize: %ux%u -> %ux%u", r->osx, r->osy, + r->sx, r->sy); } - server_client_start_resize_timer(wp); - if (~wp->flags & PANE_RESIZEFORCE) { - /* - * The timer is not running and we don't need to force a - * resize, so just resize immediately. - */ - log_debug("%s: resizing %%%u now", __func__, wp->id); - window_pane_send_resize(wp, 0); - wp->flags &= ~PANE_RESIZE; + /* + * There are three cases that matter: + * + * - Only one resize. It can just be applied. + * + * - Multiple resizes and the ending size is different from the + * starting size. We can discard all resizes except the most recent. + * + * - Multiple resizes and the ending size is the same as the starting + * size. We must resize at least twice to force the application to + * redraw. So apply the first and leave the last on the queue for + * next time. + */ + first = TAILQ_FIRST(&wp->resize_queue); + last = TAILQ_LAST(&wp->resize_queue, window_pane_resizes); + if (first == last) { + /* Only one resize. */ + window_pane_send_resize(wp, first->sx, first->sy); + TAILQ_REMOVE(&wp->resize_queue, first, entry); + free(first); + } else if (last->sx != first->osx || last->sy != first->osy) { + /* Multiple resizes ending up with a different size. */ + window_pane_send_resize(wp, last->sx, last->sy); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); + } } else { /* - * The timer is not running, but we need to force a resize. If - * the force timer has expired, resize to the real size now. - * Otherwise resize to the force size and start the timer. + * Multiple resizes ending up with the same size. There will + * not be more than one to the same size in succession so we + * can just use the last-but-one on the list and leave the last + * for later. We reduce the time until the next check to avoid + * a long delay between the resizes. */ - if (wp->flags & PANE_RESIZENOW) { - log_debug("%s: resizing %%%u after forced resize", - __func__, wp->id); - window_pane_send_resize(wp, 0); - wp->flags &= ~(PANE_RESIZE|PANE_RESIZEFORCE|PANE_RESIZENOW); - } else if (!evtimer_pending(&wp->force_timer, NULL)) { - log_debug("%s: forcing resize of %%%u", __func__, - wp->id); - window_pane_send_resize(wp, 1); - server_client_start_force_timer(wp); + r = TAILQ_PREV(last, window_pane_resizes, entry); + window_pane_send_resize(wp, r->sx, r->sy); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + if (r == last) + break; + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); } + tv.tv_usec = 10000; } + evtimer_add(&wp->resize_timer, &tv); } + /* Check pane buffer size. */ static void server_client_check_pane_buffer(struct window_pane *wp) diff --git a/tmux.h b/tmux.h index a67b30b9..1e496e6f 100644 --- a/tmux.h +++ b/tmux.h @@ -919,7 +919,7 @@ struct window_mode_entry { struct screen *screen; u_int prefix; - TAILQ_ENTRY (window_mode_entry) entry; + TAILQ_ENTRY(window_mode_entry) entry; }; /* Offsets into pane buffer. */ @@ -927,6 +927,18 @@ struct window_pane_offset { size_t used; }; +/* Queued pane resize. */ +struct window_pane_resize { + u_int sx; + u_int sy; + + u_int osx; + u_int osy; + + TAILQ_ENTRY(window_pane_resize) entry; +}; +TAILQ_HEAD(window_pane_resizes, window_pane_resize); + /* Child window structure. */ struct window_pane { u_int id; @@ -951,8 +963,8 @@ struct window_pane { #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 -#define PANE_RESIZE 0x8 -#define PANE_RESIZEFORCE 0x10 +/* 0x8 unused */ +/* 0x10 unused */ #define PANE_FOCUSPUSH 0x20 #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 @@ -961,7 +973,6 @@ struct window_pane { #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 #define PANE_STYLECHANGED 0x1000 -#define PANE_RESIZENOW 0x2000 int argc; char **argv; @@ -978,8 +989,8 @@ struct window_pane { struct window_pane_offset offset; size_t base_offset; + struct window_pane_resizes resize_queue; struct event resize_timer; - struct event force_timer; struct input_ctx *ictx; @@ -997,7 +1008,7 @@ struct window_pane { struct screen status_screen; size_t status_size; - TAILQ_HEAD (, window_mode_entry) modes; + TAILQ_HEAD(, window_mode_entry) modes; char *searchstr; int searchregex; @@ -2756,7 +2767,7 @@ void window_redraw_active_switch(struct window *, struct window_pane *window_add_pane(struct window *, struct window_pane *, u_int, int); void window_resize(struct window *, u_int, u_int, int, int); -void window_pane_send_resize(struct window_pane *, int); +void window_pane_send_resize(struct window_pane *, u_int, u_int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); int window_push_zoom(struct window *, int, int); diff --git a/window.c b/window.c index d8cff590..f21a4d5f 100644 --- a/window.c +++ b/window.c @@ -425,25 +425,18 @@ window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) } void -window_pane_send_resize(struct window_pane *wp, int force) +window_pane_send_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window *w = wp->window; struct winsize ws; - u_int sy; if (wp->fd == -1) return; - if (!force) - sy = wp->sy; - else if (wp->sy <= 1) - sy = wp->sy + 1; - else - sy = wp->sy - 1; - log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, sy); + log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, sx, sy); memset(&ws, 0, sizeof ws); - ws.ws_col = wp->sx; + ws.ws_col = sx; ws.ws_row = sy; ws.ws_xpixel = w->xpixel * ws.ws_col; ws.ws_ypixel = w->ypixel * ws.ws_row; @@ -867,29 +860,19 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); - wp->argc = 0; - wp->argv = NULL; - wp->shell = NULL; - wp->cwd = NULL; - wp->fd = -1; - wp->event = NULL; wp->fg = 8; wp->bg = 8; TAILQ_INIT(&wp->modes); - wp->layout_cell = NULL; - - wp->xoff = 0; - wp->yoff = 0; + TAILQ_INIT (&wp->resize_queue); wp->sx = sx; wp->sy = sy; wp->pipe_fd = -1; - wp->pipe_event = NULL; screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; @@ -905,6 +888,9 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) static void window_pane_destroy(struct window_pane *wp) { + struct window_pane_resize *r; + struct window_pane_resize *r1; + window_pane_reset_mode_all(wp); free(wp->searchstr); @@ -929,8 +915,10 @@ window_pane_destroy(struct window_pane *wp) if (event_initialized(&wp->resize_timer)) event_del(&wp->resize_timer); - if (event_initialized(&wp->force_timer)) - event_del(&wp->force_timer); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); + } RB_REMOVE(window_pane_tree, &all_window_panes, wp); @@ -999,9 +987,18 @@ void window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window_mode_entry *wme; + struct window_pane_resize *r; if (sx == wp->sx && sy == wp->sy) return; + + r = xmalloc (sizeof *r); + r->sx = sx; + r->sy = sy; + r->osx = wp->sx; + r->osy = wp->sy; + TAILQ_INSERT_TAIL (&wp->resize_queue, r, entry); + wp->sx = sx; wp->sy = sy; @@ -1011,14 +1008,6 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); - - /* - * If the pane has already been resized, set the force flag and make - * the application resize twice to force it to redraw. - */ - if (wp->flags & PANE_RESIZE) - wp->flags |= PANE_RESIZEFORCE; - wp->flags |= PANE_RESIZE; } void From 059580e0f72ebeb0538d0ef54808dae277388bf9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 28 Apr 2021 09:18:04 +0100 Subject: [PATCH 0842/1006] Move "special" keys into the Unicode PUA rather than making them high a top bit set, some compilers cannot handle enums that are larger than int. GitHub issue 2673. --- input-keys.c | 2 +- key-string.c | 6 +++--- status.c | 2 +- tmux.h | 33 ++++++++++++++++++++++++--------- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/input-keys.c b/input-keys.c index a3252855..ffd2201c 100644 --- a/input-keys.c +++ b/input-keys.c @@ -476,7 +476,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) input_key_write(__func__, bev, &ud.data[0], 1); return (0); } - if (justkey > 0x7f && justkey < KEYC_BASE) { + if (KEYC_IS_UNICODE(justkey)) { if (key & KEYC_META) input_key_write(__func__, bev, "\033", 1); utf8_to_data(justkey, &ud); diff --git a/key-string.c b/key-string.c index c24a33fc..b41eaa73 100644 --- a/key-string.c +++ b/key-string.c @@ -238,7 +238,7 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (key < KEYC_BASE && + if (KEYC_IS_UNICODE(key) && (modifiers & KEYC_CTRL) && strchr(other, key) == NULL && key != 9 && @@ -368,8 +368,8 @@ key_string_lookup_key(key_code key, int with_flags) goto out; } - /* Is this a UTF-8 key? */ - if (key > 127 && key < KEYC_BASE) { + /* Is this a Unicode key? */ + if (KEYC_IS_UNICODE(key)) { utf8_to_data(key, &ud); off = strlen(out); memcpy(out + off, ud.data, ud.size); diff --git a/status.c b/status.c index 271e1afa..f4418500 100644 --- a/status.c +++ b/status.c @@ -1300,7 +1300,7 @@ process_key: return (0); append_key: - if (key <= 0x1f || key >= KEYC_BASE) + if (key <= 0x1f || (key >= KEYC_BASE && key < KEYC_BASE_END)) return (0); if (key <= 0x7f) utf8_set(&tmp, key); diff --git a/tmux.h b/tmux.h index 1e496e6f..735f4e68 100644 --- a/tmux.h +++ b/tmux.h @@ -109,11 +109,16 @@ struct winlink; #define VISUAL_ON 1 #define VISUAL_BOTH 2 -/* Special key codes. */ -#define KEYC_NONE 0x00ff000000000ULL -#define KEYC_UNKNOWN 0x00fe000000000ULL -#define KEYC_BASE 0x0001000000000ULL -#define KEYC_USER 0x0002000000000ULL +/* No key or unknown key. */ +#define KEYC_NONE 0x000ff000000000ULL +#define KEYC_UNKNOWN 0x000fe000000000ULL + +/* + * Base for special (that is, not Unicode) keys. An enum must be at most a + * signed int, so these are based in the highest Unicode PUA. + */ +#define KEYC_BASE 0x0000000010e000ULL +#define KEYC_USER 0x0000000010f000ULL /* Key modifier bits. */ #define KEYC_META 0x00100000000000ULL @@ -136,8 +141,15 @@ struct winlink; #define KEYC_NUSER 1000 /* Is this a mouse key? */ -#define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ - ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) +#define KEYC_IS_MOUSE(key) \ + (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ + ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) + +/* Is this a Unicode key? */ +#define KEYC_IS_UNICODE(key) \ + (((key) & KEYC_MASK_KEY) > 0x7f && \ + (((key) & KEYC_MASK_KEY) < KEYC_BASE || \ + ((key) & KEYC_MASK_KEY) >= KEYC_BASE_END)) /* Multiple click timeout. */ #define KEYC_CLICK_TIMEOUT 300 @@ -159,8 +171,8 @@ struct winlink; { #s "Border", KEYC_ ## name ## _BORDER } /* - * A single key. This can be ASCII or Unicode or one of the keys starting at - * KEYC_BASE. + * A single key. This can be ASCII or Unicode or one of the keys between + * KEYC_BASE and KEYC_BASE_END. */ typedef unsigned long long key_code; @@ -253,6 +265,9 @@ enum { KEYC_KP_ENTER, KEYC_KP_ZERO, KEYC_KP_PERIOD, + + /* End of special keys. */ + KEYC_BASE_END }; /* Termcap codes. */ From ad2f7642f2795fc1073c02cfa6348e48dfdfc45d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 28 Apr 2021 20:20:53 +0100 Subject: [PATCH 0843/1006] Ctrl keys are < 0x7f, not Unicode. --- key-string.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-string.c b/key-string.c index b41eaa73..406d26dd 100644 --- a/key-string.c +++ b/key-string.c @@ -238,7 +238,7 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (KEYC_IS_UNICODE(key) && + if (key <= 127 && (modifiers & KEYC_CTRL) && strchr(other, key) == NULL && key != 9 && From bacb4d1b4df77c0f4ad940edcd75bbafef8efa9f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 3 May 2021 06:39:17 +0100 Subject: [PATCH 0844/1006] Fix warnings, from Jan Tache in GitHub issue 2692. --- format.c | 2 +- server.c | 3 ++- tty-term.c | 2 +- window-buffer.c | 6 +++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/format.c b/format.c index 5a12502e..c7960819 100644 --- a/format.c +++ b/format.c @@ -4199,7 +4199,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, value = xstrdup("0"); } else { format_log(es, "search '%s' pane %%%u", new, wp->id); - value = format_search(fm, wp, new); + value = format_search(search, wp, new); } free(new); } else if (cmp != NULL) { diff --git a/server.c b/server.c index 0b878d9e..29bcf88a 100644 --- a/server.c +++ b/server.c @@ -163,7 +163,8 @@ server_tidy_event(__unused int fd, __unused short events, __unused void *data) malloc_trim(0); #endif - log_debug("%s: took %llu milliseconds", __func__, get_timer() - t); + log_debug("%s: took %llu milliseconds", __func__, + (unsigned long long)(get_timer() - t)); evtimer_add(&server_ev_tidy, &tv); } diff --git a/tty-term.c b/tty-term.c index 4a7e7415..add71d89 100644 --- a/tty-term.c +++ b/tty-term.c @@ -697,7 +697,7 @@ tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps, ent = &tty_term_codes[i]; switch (ent->type) { case TTYCODE_NONE: - break; + continue; case TTYCODE_STRING: s = tigetstr((char *)ent->name); if (s == NULL || s == (char *)-1) diff --git a/window-buffer.c b/window-buffer.c index 8d93558a..c1ddcc74 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -294,9 +294,9 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line) struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item = itemdata; struct format_tree *ft; - struct session *s; - struct winlink *wl; - struct window_pane *wp; + struct session *s = NULL; + struct winlink *wl = NULL; + struct window_pane *wp = NULL; struct paste_buffer *pb; char *expanded; key_code key; From aaf87abfb4a93067c3fcb78d604636886252791a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 3 May 2021 10:49:51 +0100 Subject: [PATCH 0845/1006] Fire check callback after cleaning up event so it does not get stuck, from Jeongho Jang in GitHub issue 2695. --- file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/file.c b/file.c index baa2bd58..974c8a37 100644 --- a/file.c +++ b/file.c @@ -503,14 +503,14 @@ file_write_error_callback(__unused struct bufferevent *bev, __unused short what, log_debug("write error file %d", cf->stream); - if (cf->cb != NULL) - cf->cb(NULL, NULL, 0, -1, NULL, cf->data); - bufferevent_free(cf->event); cf->event = NULL; close(cf->fd); cf->fd = -1; + + if (cf->cb != NULL) + cf->cb(NULL, NULL, 0, -1, NULL, cf->data); } /* Client file write callback. */ From 47af583a50db35e1fbf881845d0aa342e8fa57c8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 5 May 2021 07:23:23 +0100 Subject: [PATCH 0846/1006] Remove old shift function keys which interfere with xterm keys now. GitHub issue 2696. --- input-keys.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/input-keys.c b/input-keys.c index ffd2201c..be83600e 100644 --- a/input-keys.c +++ b/input-keys.c @@ -94,30 +94,6 @@ static struct input_key_entry input_key_defaults[] = { { .key = KEYC_F12, .data = "\033[24~" }, - { .key = KEYC_F1|KEYC_SHIFT, - .data = "\033[25~" - }, - { .key = KEYC_F2|KEYC_SHIFT, - .data = "\033[26~" - }, - { .key = KEYC_F3|KEYC_SHIFT, - .data = "\033[28~" - }, - { .key = KEYC_F4|KEYC_SHIFT, - .data = "\033[29~" - }, - { .key = KEYC_F5|KEYC_SHIFT, - .data = "\033[31~" - }, - { .key = KEYC_F6|KEYC_SHIFT, - .data = "\033[32~" - }, - { .key = KEYC_F7|KEYC_SHIFT, - .data = "\033[33~" - }, - { .key = KEYC_F8|KEYC_SHIFT, - .data = "\033[34~" - }, { .key = KEYC_IC, .data = "\033[2~" }, From 434ac8734af2a15d5661139acc06a9659f4feed9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 10 May 2021 07:42:35 +0100 Subject: [PATCH 0847/1006] Looks like evports on SunOS are broken also, disable them. GitHub issue 2702. --- osdep-sunos.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osdep-sunos.c b/osdep-sunos.c index 138e6bad..c3563ca4 100644 --- a/osdep-sunos.c +++ b/osdep-sunos.c @@ -96,5 +96,17 @@ osdep_get_cwd(int fd) struct event_base * osdep_event_init(void) { - return (event_init()); + struct event_base *base; + + /* + * On Illumos, evports don't seem to work properly. It is not clear if + * this a problem in libevent, with the way tmux uses file descriptors, + * or with some types of file descriptor. But using poll instead is + * fine. + */ + setenv("EVENT_NOEVPORT", "1", 1); + + base = event_init(); + unsetenv("EVENT_NOEVPORT"); + return (base); } From 5ea6ccbb7f75b6a977fca0751ef0b81b18c375d1 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 10 May 2021 07:51:30 +0100 Subject: [PATCH 0848/1006] Do not expand the file given with -f so it can contain :s. --- tmux.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tmux.c b/tmux.c index 3a49c803..29921781 100644 --- a/tmux.c +++ b/tmux.c @@ -328,7 +328,7 @@ main(int argc, char **argv) char *path = NULL, *label = NULL; char *cause, **var; const char *s, *cwd; - int opt, keys, feat = 0; + int opt, keys, feat = 0, fflag = 0; uint64_t flags = 0; const struct options_table_entry *oe; u_int i; @@ -373,10 +373,15 @@ main(int argc, char **argv) flags |= CLIENT_CONTROL; break; case 'f': - for (i = 0; i < cfg_nfiles; i++) - free(cfg_files[i]); - free(cfg_files); - expand_paths(optarg, &cfg_files, &cfg_nfiles, 0); + if (!fflag) { + fflag = 1; + for (i = 0; i < cfg_nfiles; i++) + free(cfg_files[i]); + cfg_nfiles = 0; + } + cfg_files = xreallocarray(cfg_files, cfg_nfiles + 1, + sizeof *cfg_files); + cfg_files[cfg_nfiles++] = xstrdup(optarg); cfg_quiet = 0; break; case 'V': From 8aa34f616fa6723f4a98c435ddcab0ecaaa5b4e7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 12 May 2021 07:08:58 +0100 Subject: [PATCH 0849/1006] Do not use NULL client when source-file finishes, GitHub issue 2707. --- cmd-source-file.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd-source-file.c b/cmd-source-file.c index 1da59193..c1eeba58 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -67,7 +67,9 @@ cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata) struct cmdq_item *new_item; if (cfg_finished) { - if (cdata->retval == CMD_RETURN_ERROR && c->session == NULL) + if (cdata->retval == CMD_RETURN_ERROR && + c != NULL && + c->session == NULL) c->retval = 1; new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL); cmdq_insert_after(cdata->after, new_item); From 3b9b823df50202e466470d3cb557ef16a31b6af7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 17 May 2021 06:58:45 +0100 Subject: [PATCH 0850/1006] Fix <= operator. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index c7960819..3e640a6f 100644 --- a/format.c +++ b/format.c @@ -3991,7 +3991,7 @@ format_replace_expression(struct format_modifier *mexp, result = (mleft < mright); break; case LESS_THAN_EQUAL: - result = (mleft > mright); + result = (mleft >= mright); break; } if (use_fp) From 9b4c05b6b9aec916c0940aa704c5f2ec9e1a8a83 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 17 May 2021 06:59:29 +0100 Subject: [PATCH 0851/1006] Er, fix it properly. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index 3e640a6f..6ac086cb 100644 --- a/format.c +++ b/format.c @@ -3991,7 +3991,7 @@ format_replace_expression(struct format_modifier *mexp, result = (mleft < mright); break; case LESS_THAN_EQUAL: - result = (mleft >= mright); + result = (mleft <= mright); break; } if (use_fp) From f06ee2b87b88d0889e8dfeb2ae85a2b7d4ce0d49 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 19 May 2021 09:04:45 +0100 Subject: [PATCH 0852/1006] Bump FORMAT_LOOOP_LIMIT and add a log message when hit, GitHub issue 2715. --- format.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 6ac086cb..512df009 100644 --- a/format.c +++ b/format.c @@ -103,7 +103,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_CHARACTER 0x10000 /* Limit on recursion. */ -#define FORMAT_LOOP_LIMIT 10 +#define FORMAT_LOOP_LIMIT 100 /* Format expand flags. */ #define FORMAT_EXPAND_TIME 0x1 @@ -4441,8 +4441,10 @@ format_expand1(struct format_expand_state *es, const char *fmt) if (fmt == NULL || *fmt == '\0') return (xstrdup("")); - if (es->loop == FORMAT_LOOP_LIMIT) + if (es->loop == FORMAT_LOOP_LIMIT) { + format_log(es, "reached loop limit (%u)", FORMAT_LOOP_LIMIT); return (xstrdup("")); + } es->loop++; format_log(es, "expanding format: %s", fmt); From f48c46a76adb78527627894c07ca45a393d7fa2c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 19 May 2021 09:05:53 +0100 Subject: [PATCH 0853/1006] Fix rectangle selection, from Anindya Mukherjee, GitHub issue 2709. --- window-copy.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/window-copy.c b/window-copy.c index 423cce8f..f92e02ba 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1102,10 +1102,13 @@ static enum window_copy_cmd_action window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - for (; np != 0; np--) - window_copy_cursor_right(wme, 0); + for (; np != 0; np--) { + window_copy_cursor_right(wme, data->screen.sel != NULL && + data->rectflag); + } return (WINDOW_COPY_CMD_NOTHING); } @@ -4425,10 +4428,12 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int ox, oy, px, py; + int norectsel; + norectsel = data->screen.sel == NULL || !data->rectflag; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wme, oy); - if (data->cx != ox) { + if (norectsel && data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } @@ -4437,7 +4442,8 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) window_copy_other_end(wme); if (scroll_only || data->cy == 0) { - data->cx = data->lastcx; + if (norectsel) + data->cx = data->lastcx; window_copy_scroll_down(wme, 1); if (scroll_only) { if (data->cy == screen_size_y(s) - 1) @@ -4446,7 +4452,11 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) window_copy_redraw_lines(wme, data->cy, 2); } } else { - window_copy_update_cursor(wme, data->lastcx, data->cy - 1); + if (norectsel) { + window_copy_update_cursor(wme, data->lastcx, + data->cy - 1); + } else + window_copy_update_cursor(wme, data->cx, data->cy - 1); if (window_copy_update_selection(wme, 1, 0)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wme, data->cy, 1); @@ -4455,7 +4465,7 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) } } - if (data->screen.sel == NULL || !data->rectflag) { + if (norectsel) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || @@ -4492,10 +4502,12 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int ox, oy, px, py; + int norectsel; + norectsel = data->screen.sel == NULL || !data->rectflag; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wme, oy); - if (data->cx != ox) { + if (norectsel && data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } @@ -4504,17 +4516,22 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) window_copy_other_end(wme); if (scroll_only || data->cy == screen_size_y(s) - 1) { - data->cx = data->lastcx; + if (norectsel) + data->cx = data->lastcx; window_copy_scroll_up(wme, 1); if (scroll_only && data->cy > 0) window_copy_redraw_lines(wme, data->cy - 1, 2); } else { - window_copy_update_cursor(wme, data->lastcx, data->cy + 1); + if (norectsel) { + window_copy_update_cursor(wme, data->lastcx, + data->cy + 1); + } else + window_copy_update_cursor(wme, data->cx, data->cy + 1); if (window_copy_update_selection(wme, 1, 0)) window_copy_redraw_lines(wme, data->cy - 1, 2); } - if (data->screen.sel == NULL || !data->rectflag) { + if (norectsel) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || From d8feffd2bfd0c53a3323a4bda830acaafed259ed Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Jun 2021 10:49:40 +0100 Subject: [PATCH 0854/1006] Feature for the mouse since FreeBSD termcap does not have kmous. --- tmux.1 | 4 ++++ tty-features.c | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index 22982b11..365b93f5 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3539,6 +3539,10 @@ Supports extended keys. Supports focus reporting. .It margins Supports DECSLRM margins. +.It mouse +Supports +.Xr xterm 1 +mouse sequences. .It overline Supports the overline SGR attribute. .It rectfill diff --git a/tty-features.c b/tty-features.c index b42cf74a..48ac51be 100644 --- a/tty-features.c +++ b/tty-features.c @@ -25,7 +25,6 @@ /* * Still hardcoded: - * - mouse (under kmous capability); * - default colours (under AX or op capabilities); * - AIX colours (under colors >= 16); * - alternate escape (if terminal is VT100-like). @@ -54,6 +53,17 @@ static const struct tty_feature tty_feature_title = { 0 }; +/* Terminal has mouse support. */ +static const char *tty_feature_mouse_capabilities[] = { + "kmous=\\E[M", + NULL +}; +static const struct tty_feature tty_feature_mouse = { + "mouse", + tty_feature_mouse_capabilities, + 0 +}; + /* Terminal can set the clipboard with OSC 52. */ static const char *tty_feature_clipboard_capabilities[] = { "Ms=\\E]52;%p1%s;%p2%s\\a", @@ -238,6 +248,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_extkeys, &tty_feature_focus, &tty_feature_margins, + &tty_feature_mouse, &tty_feature_overline, &tty_feature_rectfill, &tty_feature_rgb, @@ -338,7 +349,7 @@ tty_default_features(int *feat, const char *name, u_int version) const char *features; } table[] = { #define TTY_FEATURES_BASE_MODERN_XTERM \ - "256,RGB,bpaste,clipboard,strikethrough,title" + "256,RGB,bpaste,clipboard,mouse,strikethrough,title" { .name = "mintty", .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,margins,overline,usstyle" @@ -348,7 +359,7 @@ tty_default_features(int *feat, const char *name, u_int version) ",ccolour,cstyle,focus,overline,usstyle" }, { .name = "rxvt-unicode", - .features = "256,bpaste,ccolour,cstyle,title" + .features = "256,bpaste,ccolour,cstyle,mouse,title" }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM From c827f5092db2142af097e32d02fb73d70acbfa6d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 9 Jun 2021 14:46:24 +0100 Subject: [PATCH 0855/1006] Do not clear region based on current cursor position, this is not necessary anymore and causes problems, GitHub issue 2735. --- tty.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tty.c b/tty.c index e8a8cbaa..9e7dfde8 100644 --- a/tty.c +++ b/tty.c @@ -958,13 +958,8 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) return; } - if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { - for (i = ctx->ocy; i < ctx->sy; i++) - tty_draw_pane(tty, ctx, i); - } else { - for (i = ctx->orupper; i <= ctx->orlower; i++) - tty_draw_pane(tty, ctx, i); - } + for (i = ctx->orupper; i <= ctx->orlower; i++) + tty_draw_pane(tty, ctx, i); } /* Is this position visible in the pane? */ From 3b929f332aafa7f1080eacc31feb11ffbb1d1841 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Jun 2021 09:24:57 +0100 Subject: [PATCH 0856/1006] Update CHANGES. --- CHANGES | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index e50c061b..f9c9d007 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,15 @@ CHANGES FROM 3.2 TO 3.2a -XXX +* Add an "always" value for the "extended-keys" option; if set then tmux will + forward extended keys to applications even if they do not request them. + +* Add a "mouse" terminal feature so tmux can enable the mouse on terminals + where it is known to be supported even if terminfo(5) says otherwise. + +* Do not expand the filename given to -f so it can contain colons. + +* Fixes for problems with extended keys and modifiers, scroll region, + source-file, crosscompiling, format modifiers and other minor issues. CHANGES FROM 3.1c TO 3.2 From 57aaad2ddbc5504c5874290edf838d111152adc5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Jun 2021 09:25:50 +0100 Subject: [PATCH 0857/1006] Update CHANGES. --- CHANGES | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/CHANGES b/CHANGES index 3d9304c5..fbf02422 100644 --- a/CHANGES +++ b/CHANGES @@ -8,22 +8,11 @@ CHANGES FROM 3.2 TO 3.3 changes the meaning of the word-separators option - setting it to the empty string is equivalent to the previous behavior. -* Add an "always" value for the "extended-keys" option; if set then tmux will - forward extended keys to applications even if they do not request them. - -* Add a "mouse" terminal feature so tmux can enable the mouse on terminals - where it is known to be supported even if terminfo(5) says otherwise. - * Add -F for command-prompt and use it to fix "Rename" on the window menu. -* Do not expand the filename given to -f so it can contain colons. - -* Add different command historys for different types of prompts ("command", +* Add different command histories for different types of prompts ("command", "search" etc). From Anindya Mukherjee. -* Fixes for problems with extended keys and modifiers, scroll region, - source-file, crosscompiling, format modifiers and other minor issues. - CHANGES FROM 3.1c TO 3.2 * Add a flag to disable keys to close a message. From 0490707671bd2655b676878cb4aed6544a96561d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Jun 2021 09:56:35 +0100 Subject: [PATCH 0858/1006] Move lock.yml. --- .github/lock.yml | 10 ---------- .github/workflows/lock.yml | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 10 deletions(-) delete mode 100644 .github/lock.yml create mode 100644 .github/workflows/lock.yml diff --git a/.github/lock.yml b/.github/lock.yml deleted file mode 100644 index 08cf2fc0..00000000 --- a/.github/lock.yml +++ /dev/null @@ -1,10 +0,0 @@ -daysUntilLock: 30 -skipCreatedBefore: false -exemptLabels: [] -lockLabel: false -lockComment: > - This thread has been automatically locked since there has not been - any recent activity after it was closed. Please open a new issue for - related bugs. -setLockReason: false -#only: issues diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 00000000..3206fdf1 --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,23 @@ +name: 'Lock Threads' + +on: + schedule: + - cron: '0 * * * *' + +jobs: + lock: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v2 + with: + github-token: ${{ github.token }} + issue-lock-inactive-days: '30' + pr-lock-inactive-days: '60' + issue-lock-comment: > + This issue has been automatically locked since there + has not been any recent activity after it was closed. + Please open a new issue for related bugs. + pr-lock-comment: > + This pull request has been automatically locked since there + has not been any recent activity after it was closed. + Please open a new issue for related bugs. From 96ad8280b28283eb01e7789f5bafd2e1f6cde139 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Jun 2021 13:05:22 +0100 Subject: [PATCH 0859/1006] Tweak. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 65a96717..ed3875ce 100644 --- a/CHANGES +++ b/CHANGES @@ -5,7 +5,7 @@ CHANGES FROM 3.2a TO 3.3 disables blinking or not. This now matches xterm's behaviour. * More accurate vi(1) word navigation in copy mode and on the status line. This - changes the meaning of the word-separators option - setting it to the empty + changes the meaning of the word-separators option: setting it to the empty string is equivalent to the previous behavior. * Add -F for command-prompt and use it to fix "Rename" on the window menu. From 0d0683c28aa467d30c50ac7cc99b95ae829f61a5 Mon Sep 17 00:00:00 2001 From: jmc Date: Thu, 10 Jun 2021 13:12:31 +0000 Subject: [PATCH 0860/1006] fix some formatting errors; --- tmux.1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index d6a7e35f..c06b6456 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4331,6 +4331,7 @@ see the .Sx STYLES section. .El +.El .Sh HOOKS .Nm allows commands to run on various triggers, called @@ -5381,7 +5382,7 @@ Commands related to the status line are as follows: .It Xo Ic clear-prompt-history .Op Fl T Ar prompt-type .Xc -.D1 (alias: Ic clrphist) +.D1 (alias: Ic clrphist ) Clear status prompt history for prompt type .Ar prompt-type . If @@ -5695,7 +5696,7 @@ flag closes any popup on the client. .It Xo Ic show-prompt-history .Op Fl T Ar prompt-type .Xc -.D1 (alias: Ic showphist) +.D1 (alias: Ic showphist ) Display status prompt history for prompt type .Ar prompt-type . If From 6a8d848a3e123ec5a0bb9747522a343c843f1c61 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 11 Jun 2021 08:30:51 +0100 Subject: [PATCH 0861/1006] Once a day. --- .github/workflows/lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 3206fdf1..c6691e46 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -2,7 +2,7 @@ name: 'Lock Threads' on: schedule: - - cron: '0 * * * *' + - cron: '0 0 * * *' jobs: lock: From 87521214d3b6f47979964b5d0523269dd2e127cd Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 15 Jun 2021 09:44:56 +0100 Subject: [PATCH 0862/1006] Fix incorrect option name, from Gregory Pakosz. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index ed3875ce..bf2cff05 100644 --- a/CHANGES +++ b/CHANGES @@ -257,7 +257,7 @@ CHANGES FROM 3.1c TO 3.2 only show windows and C-b . only targets. * Change all the style options to string options so they can support formats. - Change pane-border-active-style to use this to change the border colour when + Change pane-active-border-style to use this to change the border colour when in a mode or with synchronize-panes on. This also implies a few minor changes to existing behaviour: From 9f3874e5c74f5d2c990c44ab0e1369d3876eb57b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Jun 2021 08:37:58 +0000 Subject: [PATCH 0863/1006] Pass Ctrl keys through as is when given as hex, GitHub issue 2724. --- key-string.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/key-string.c b/key-string.c index 406d26dd..4f7be858 100644 --- a/key-string.c +++ b/key-string.c @@ -183,6 +183,8 @@ key_string_lookup_string(const char *string) if (string[0] == '0' && string[1] == 'x') { if (sscanf(string + 2, "%x", &u) != 1) return (KEYC_UNKNOWN); + if (u < 32) + return (u); mlen = wctomb(m, u); if (mlen <= 0 || mlen > MB_LEN_MAX) return (KEYC_UNKNOWN); From a83fb8127a6995a92b1f4954e397be9e5ee3c9a0 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Jun 2021 11:57:04 +0000 Subject: [PATCH 0864/1006] Minor fixes to option descriptions. --- options-table.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/options-table.c b/options-table.c index 01880d62..da1d8776 100644 --- a/options-table.c +++ b/options-table.c @@ -368,7 +368,7 @@ const struct options_table_entry options_table[] = { .maximum = INT_MAX, .default_num = 1, .unit = "milliseconds", - .text = "Maximum time between input to assume it pasting rather " + .text = "Maximum time between input to assume it is pasting rather " "than typing." }, @@ -615,7 +615,7 @@ const struct options_table_entry options_table[] = { .text = "Formats for the status lines. " "Each array member is the format for one status line. " "The default status line is made up of several components " - "which may be configured individually with other option such " + "which may be configured individually with other options such " "as 'status-left'." }, @@ -952,7 +952,8 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_pane_lines_list, .default_num = PANE_LINES_SINGLE, - .text = "Type of the pane type lines." + .text = "Type of characters used to draw pane border lines. Some of " + "these are only supported on terminals with UTF-8 support." }, { .name = "pane-border-status", From 1d4296f17fde47186e2d09752a912559af034e37 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 18 Jun 2021 07:46:54 +0000 Subject: [PATCH 0865/1006] Mention %1 under choose-tree also. --- tmux.1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index c06b6456..dd25edfc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2102,9 +2102,11 @@ The following keys may be used in tree mode: .It Li "q" Ta "Exit mode" .El .Pp -After a session, window or pane is chosen, +After a session, window or pane is chosen, the first instance of .Ql %% -is replaced by the target in +and all instances of +.Ql %1 +are replaced by the target in .Ar template and the result executed as a command. If From b1a8c0fe022e99cffb0fb4f321740464f35bc6b9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 22 Jun 2021 12:16:48 +0100 Subject: [PATCH 0866/1006] Fix crosscompiling, Marco A L Barbosa. --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index f6bdd3c7..30b4d370 100644 --- a/configure.ac +++ b/configure.ac @@ -160,6 +160,7 @@ AC_RUN_IFELSE([AC_LANG_PROGRAM( [return (strtonum("0", 0, 1, NULL) == 0 ? 0 : 1);] )], [AC_DEFINE(HAVE_STRTONUM) AC_MSG_RESULT(yes)], + [AC_LIBOBJ(strtonum) AC_MSG_RESULT(no)], [AC_LIBOBJ(strtonum) AC_MSG_RESULT(no)] ) From 35c2958ae4caf70a3074f59fbceeb24cfe82f902 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 6 Jul 2021 08:18:38 +0000 Subject: [PATCH 0867/1006] Forbid empty session names, GitHub issue 2758. --- cmd-new-session.c | 13 ++++++++++++- cmd-rename-session.c | 5 +++++ session.c | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index c47d66cd..666aeaac 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -102,6 +102,11 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (tmp != NULL) { name = format_single(item, tmp, c, NULL, NULL, NULL); newname = session_check_name(name); + if (newname == NULL) { + cmdq_error(item, "invalid session: %s", name); + free(name); + return (CMD_RETURN_ERROR); + } free(name); } if (args_has(args, 'A')) { @@ -134,8 +139,14 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) prefix = xstrdup(sg->name); else if (groupwith != NULL) prefix = xstrdup(groupwith->name); - else + else { prefix = session_check_name(group); + if (prefix == NULL) { + cmdq_error(item, "invalid session group: %s", + group); + goto fail; + } + } } /* Set -d if no client. */ diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 51b8ffc8..49fafe33 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -53,6 +53,11 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) tmp = format_single_from_target(item, args->argv[0]); newname = session_check_name(tmp); + if (newname == NULL) { + cmdq_error(item, "invalid session: %s", tmp); + free(tmp); + return (CMD_RETURN_ERROR); + } free(tmp); if (strcmp(newname, s->name) == 0) { free(newname); diff --git a/session.c b/session.c index 93d50b47..cb0093a2 100644 --- a/session.c +++ b/session.c @@ -237,6 +237,8 @@ session_check_name(const char *name) { char *copy, *cp, *new_name; + if (*name == '\0') + return (NULL); copy = xstrdup(name); for (cp = copy; *cp != '\0'; cp++) { if (*cp == ':' || *cp == '.') From 32f2d9d089ced7d693aa412821f1d66134877cf0 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 6 Jul 2021 08:26:00 +0000 Subject: [PATCH 0868/1006] Improve error reporting when the tmux /tmp directory cannot be created or used, GitHub issue 2765 from Uwe Kleine-Koenig. --- tmux.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tmux.c b/tmux.c index 16877407..55ab9751 100644 --- a/tmux.c +++ b/tmux.c @@ -211,16 +211,22 @@ make_label(const char *label, char **cause) free(paths); xasprintf(&base, "%s/tmux-%ld", path, (long)uid); - if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) + if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) { + xasprintf(cause, "couldn't create directory %s (%s)", base, + strerror(errno)); goto fail; - if (lstat(base, &sb) != 0) + } + if (lstat(base, &sb) != 0) { + xasprintf(cause, "couldn't read directory %s (%s)", base, + strerror(errno)); goto fail; + } if (!S_ISDIR(sb.st_mode)) { - errno = ENOTDIR; + xasprintf(cause, "%s is not a directory", base); goto fail; } if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { - errno = EACCES; + xasprintf(cause, "directory %s has unsafe permissions", base); goto fail; } xasprintf(&path, "%s/%s", base, label); @@ -228,7 +234,6 @@ make_label(const char *label, char **cause) return (path); fail: - xasprintf(cause, "error creating %s (%s)", base, strerror(errno)); free(base); return (NULL); } From 51915b9b0ac0ac9a98e12c1a5781f34f492a4679 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 8 Jul 2021 11:14:53 +0000 Subject: [PATCH 0869/1006] Fix mouse_word format now word-separators has no space and position of menu if too close to the bottom. --- cmd-display-menu.c | 2 +- format.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index f2100bdb..a68b9fc4 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -176,7 +176,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, format_add(ft, "popup_mouse_centre_y", "%ld", n); n = (long)event->m.y + h; if (n + h >= tty->sy) - format_add(ft, "popup_mouse_top", "%u", tty->sy - h); + format_add(ft, "popup_mouse_top", "%u", tty->sy - 1); else format_add(ft, "popup_mouse_top", "%ld", n); n = event->m.y - h; diff --git a/format.c b/format.c index a10af458..28962701 100644 --- a/format.c +++ b/format.c @@ -4809,7 +4809,8 @@ format_grid_word(struct grid *gd, u_int x, u_int y) grid_get_cell(gd, x, y, &gc); if (gc.flags & GRID_FLAG_PADDING) break; - if (utf8_cstrhas(ws, &gc.data)) { + if (utf8_cstrhas(ws, &gc.data) || + (gc.data.size == 1 && *gc.data.data == ' ')) { found = 1; break; } @@ -4846,7 +4847,8 @@ format_grid_word(struct grid *gd, u_int x, u_int y) grid_get_cell(gd, x, y, &gc); if (gc.flags & GRID_FLAG_PADDING) break; - if (utf8_cstrhas(ws, &gc.data)) + if (utf8_cstrhas(ws, &gc.data) || + (gc.data.size == 1 && *gc.data.data == ' ')) break; ud = xreallocarray(ud, size + 2, sizeof *ud); From df3fe2aa72da0555106c6187e750418f0e59d901 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Jul 2021 10:38:57 +0000 Subject: [PATCH 0870/1006] Only use client for sizing when not detached, GitHub issue 2772. --- cmd-new-session.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 666aeaac..033c707f 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -280,7 +280,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; - sc.tc = c; + if (!detached) + sc.tc = c; sc.name = args_get(args, 'n'); sc.argc = args->argc; From 38c5788232e0e3abdd08ade55a9d4fbcda637df1 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Jul 2021 22:09:29 +0000 Subject: [PATCH 0871/1006] Give #() commands a one second grace period where the output is empty before telling the user they aren't doing anything. GitHub issue 2774. --- format.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index 28962701..479b97c4 100644 --- a/format.c +++ b/format.c @@ -371,9 +371,6 @@ format_job_get(struct format_expand_state *es, const char *cmd) fj->client = ft->client; fj->tag = ft->tag; fj->cmd = xstrdup(cmd); - fj->expanded = NULL; - - xasprintf(&fj->out, "<'%s' not ready>", fj->cmd); RB_INSERT(format_job_tree, jobs, fj); } @@ -402,11 +399,14 @@ format_job_get(struct format_expand_state *es, const char *cmd) } fj->last = t; fj->updated = 0; - } + } else if (fj->job != NULL && (t - fj->last) > 1 && fj->out == NULL) + xasprintf(&fj->out, "<'%s' not ready>", fj->cmd); free(expanded); if (ft->flags & FORMAT_STATUS) fj->status = 1; + if (fj->out == NULL) + return (xstrdup("")); return (format_expand1(&next, fj->out)); } From 732c72c98e9b3aa6b1d33f1eab9fa16874d92e7d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Jul 2021 08:56:00 +0000 Subject: [PATCH 0872/1006] Move default value for TERM into tmux.h. --- options-table.c | 2 +- tmux.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index da1d8776..9f122071 100644 --- a/options-table.c +++ b/options-table.c @@ -234,7 +234,7 @@ const struct options_table_entry options_table[] = { { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "screen", + .default_str = TMUX_TERM, .text = "Default for the 'TERM' environment variable." }, diff --git a/tmux.h b/tmux.h index 917f4528..deb66292 100644 --- a/tmux.h +++ b/tmux.h @@ -74,6 +74,9 @@ struct winlink; #ifndef TMUX_SOCK #define TMUX_SOCK "$TMUX_TMPDIR:" _PATH_TMP #endif +#ifndef TMUX_TERM +#define TMUX_TERM "screen" +#endif /* Minimum layout cell size, NOT including border lines. */ #define PANE_MINIMUM 1 From d723466df2adaec4bb655094a942a427633aacff Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 14 Jul 2021 11:03:19 +0100 Subject: [PATCH 0873/1006] Pick default-terminal from the first of tmux-256color, tmux, screen-256color, screen that is available on the build system. --- Makefile.am | 3 +- configure.ac | 94 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 90 insertions(+), 7 deletions(-) diff --git a/Makefile.am b/Makefile.am index 9bb5b89e..dadf13a0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,7 +13,8 @@ dist_EXTRA_tmux_SOURCES = compat/*.[ch] # Preprocessor flags. AM_CPPFLAGS += @XOPEN_DEFINES@ \ -DTMUX_VERSION='"@VERSION@"' \ - -DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' + -DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \ + -DTMUX_TERM='"@DEFAULT_TERM@"' # Additional object files. LDADD = $(LIBOBJS) diff --git a/configure.ac b/configure.ac index 30b4d370..2a9907ed 100644 --- a/configure.ac +++ b/configure.ac @@ -74,6 +74,21 @@ if test "x$enable_static" = xyes; then LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS" fi +# Allow default TERM to be set. +AC_ARG_WITH( + TERM, + AC_HELP_STRING(--with-TERM, set default TERM), + [DEFAULT_TERM=$withval], + [DEFAULT_TERM=] +) +case "x$DEFAULT_TERM" in + xscreen*|xtmux*|x) + ;; + *) + AC_MSG_ERROR("unsuitable TERM (must be screen* or tmux*)") + ;; +esac + # Do we need fuzzers? AM_CONDITIONAL(NEED_FUZZING, test "x$enable_fuzzing" = xyes) @@ -156,12 +171,12 @@ AC_FUNC_STRNLEN # Check if strtonum works. AC_MSG_CHECKING([for working strtonum]) AC_RUN_IFELSE([AC_LANG_PROGRAM( - [#include ], - [return (strtonum("0", 0, 1, NULL) == 0 ? 0 : 1);] - )], - [AC_DEFINE(HAVE_STRTONUM) AC_MSG_RESULT(yes)], - [AC_LIBOBJ(strtonum) AC_MSG_RESULT(no)], - [AC_LIBOBJ(strtonum) AC_MSG_RESULT(no)] + [#include ], + [return (strtonum("0", 0, 1, NULL) == 0 ? 0 : 1);] + )], + [AC_DEFINE(HAVE_STRTONUM) AC_MSG_RESULT(yes)], + [AC_LIBOBJ(strtonum) AC_MSG_RESULT(no)], + [AC_LIBOBJ(strtonum) AC_MSG_RESULT(no)] ) # Clang sanitizers wrap reallocarray even if it isn't available on the target @@ -299,6 +314,7 @@ if test "x$found_ncurses" = xno; then fi fi if test "x$found_ncurses" = xyes; then + CPPFLAGS="$CPPFLAGS -DHAVE_NCURSES_H" AC_DEFINE(HAVE_NCURSES_H) else AC_CHECK_LIB( @@ -314,6 +330,7 @@ else ) if test "x$found_curses" = xyes; then LIBS="$LIBS -lcurses" + CPPFLAGS="$CPPFLAGS -DHAVE_CURSES_H" AC_DEFINE(HAVE_CURSES_H) else AC_MSG_ERROR("curses not found") @@ -673,6 +690,71 @@ else AC_MSG_RESULT(no) fi +# Try to figure out what the best value for TERM might be. +if test "x$DEFAULT_TERM" = x; then + DEFAULT_TERM=screen + AC_MSG_CHECKING(TERM) + AC_RUN_IFELSE([AC_LANG_SOURCE( + [ + #include + #include + #if defined(HAVE_CURSES_H) + #include + #elif defined(HAVE_NCURSES_H) + #include + #endif + #include + int main(void) { + if (setupterm("screen-256color", -1, NULL) != OK) + exit(1); + exit(0); + } + ])], + [DEFAULT_TERM=screen-256color], + , + ) + AC_RUN_IFELSE([AC_LANG_SOURCE( + [ + #include + #include + #if defined(HAVE_CURSES_H) + #include + #elif defined(HAVE_NCURSES_H) + #include + #endif + #include + int main(void) { + if (setupterm("tmux", -1, NULL) != OK) + exit(1); + exit(0); + } + ])], + [DEFAULT_TERM=tmux], + , + ) + AC_RUN_IFELSE([AC_LANG_SOURCE( + [ + #include + #include + #if defined(HAVE_CURSES_H) + #include + #elif defined(HAVE_NCURSES_H) + #include + #endif + #include + int main(void) { + if (setupterm("tmux-256color", -1, NULL) != OK) + exit(1); + exit(0); + } + ])], + [DEFAULT_TERM=tmux-256color], + , + ) + AC_MSG_RESULT($DEFAULT_TERM) +fi +AC_SUBST(DEFAULT_TERM) + # Man page defaults to mdoc. MANFORMAT=mdoc AC_SUBST(MANFORMAT) From 0ea6cdca9051461658ba390bbc7298c02bbb1aad Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 14 Jul 2021 11:09:28 +0100 Subject: [PATCH 0874/1006] Need all of the TAILQ bits. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 2a9907ed..dfbc24df 100644 --- a/configure.ac +++ b/configure.ac @@ -617,7 +617,7 @@ AC_CHECK_DECL( ) AC_CHECK_DECL( TAILQ_PREV, - found_queue_h=yes, + , found_queue_h=no, [#include ] ) From f0e02387b2e13ba2a924b4a6c47da38b22bd8d3e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 21 Jul 2021 08:06:36 +0000 Subject: [PATCH 0875/1006] Do not close popups on resize, instead adjust them to fit, from Anindya Mukherjee. --- cmd-display-panes.c | 4 +-- menu.c | 2 +- popup.c | 64 ++++++++++++++++++++++++++++++++++++++++++--- server-client.c | 8 ++++-- tmux.h | 4 ++- 5 files changed, 72 insertions(+), 10 deletions(-) diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 64efb89a..f03b80ed 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -286,12 +286,12 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'N')) { server_client_set_overlay(tc, delay, NULL, NULL, - cmd_display_panes_draw, NULL, cmd_display_panes_free, + cmd_display_panes_draw, NULL, cmd_display_panes_free, NULL, cdata); } else { server_client_set_overlay(tc, delay, NULL, NULL, cmd_display_panes_draw, cmd_display_panes_key, - cmd_display_panes_free, cdata); + cmd_display_panes_free, NULL, cdata); } if (args_has(args, 'b')) diff --git a/menu.c b/menu.c index 48c9ed2f..c8de02a8 100644 --- a/menu.c +++ b/menu.c @@ -390,6 +390,6 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, md->data = data; server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, - menu_key_cb, menu_free_cb, md); + menu_key_cb, menu_free_cb, NULL, md); return (0); } diff --git a/popup.c b/popup.c index d63ee996..dfad082b 100644 --- a/popup.c +++ b/popup.c @@ -39,11 +39,18 @@ struct popup_data { popup_close_cb cb; void *arg; + /* Current position and size. */ u_int px; u_int py; u_int sx; u_int sy; + /* Preferred position and size. */ + u_int ppx; + u_int ppy; + u_int psx; + u_int psy; + enum { OFF, MOVE, SIZE } dragging; u_int dx; u_int dy; @@ -133,9 +140,14 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) screen_init(&s, pd->sx, pd->sy, 0); screen_write_start(&ctx, &s); screen_write_clearscreen(&ctx, 8); - screen_write_box(&ctx, pd->sx, pd->sy); - screen_write_cursormove(&ctx, 1, 1, 0); - screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, pd->sy - 2); + + /* Skip drawing popup if the terminal is too small. */ + if (pd->sx > 2 && pd->sy > 2) { + screen_write_box(&ctx, pd->sx, pd->sy); + screen_write_cursormove(&ctx, 1, 1, 0); + screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, + pd->sy - 2); + } screen_write_stop(&ctx); c->overlay_check = NULL; @@ -171,6 +183,41 @@ popup_free_cb(struct client *c) free(pd); } +static void +popup_resize_cb(struct client *c) +{ + struct popup_data *pd = c->overlay_data; + struct tty *tty = &c->tty; + + if (pd == NULL) + return; + + /* Adjust position and size. */ + if (pd->psy > tty->sy) + pd->sy = tty->sy; + else + pd->sy = pd->psy; + if (pd->psx > tty->sx) + pd->sx = tty->sx; + else + pd->sx = pd->psx; + if (pd->ppy + pd->sy > tty->sy) + pd->py = tty->sy - pd->sy; + else + pd->py = pd->ppy; + if (pd->ppx + pd->sx > tty->sx) + pd->px = tty->sx - pd->sx; + else + pd->px = pd->ppx; + + /* Avoid zero size screens. */ + if (pd->sx > 2 && pd->sy > 2) { + screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); + if (pd->job != NULL) + job_resize(pd->job, pd->sx - 2, pd->sy - 2); + } +} + static void popup_handle_drag(struct client *c, struct popup_data *pd, struct mouse_event *m) @@ -196,6 +243,8 @@ popup_handle_drag(struct client *c, struct popup_data *pd, pd->py = py; pd->dx = m->x - pd->px; pd->dy = m->y - pd->py; + pd->ppx = px; + pd->ppy = py; server_redraw_client(c); } else if (pd->dragging == SIZE) { if (m->x < pd->px + 3) @@ -204,6 +253,8 @@ popup_handle_drag(struct client *c, struct popup_data *pd, return; pd->sx = m->x - pd->px; pd->sy = m->y - pd->py; + pd->psx = pd->sx; + pd->psy = pd->sy; screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); if (pd->job != NULL) @@ -348,13 +399,18 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->sx = sx; pd->sy = sy; + pd->ppx = px; + pd->ppy = py; + pd->psx = sx; + pd->psy = sy; + pd->job = job_run(shellcmd, argc, argv, s, cwd, popup_job_update_cb, popup_job_complete_cb, NULL, pd, JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, pd->sx - 2, pd->sy - 2); pd->ictx = input_init(NULL, job_get_event(pd->job)); server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, - popup_draw_cb, popup_key_cb, popup_free_cb, pd); + popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd); return (0); } diff --git a/server-client.c b/server-client.c index 27b264a0..1be3575e 100644 --- a/server-client.c +++ b/server-client.c @@ -93,7 +93,7 @@ void server_client_set_overlay(struct client *c, u_int delay, overlay_check_cb checkcb, overlay_mode_cb modecb, overlay_draw_cb drawcb, overlay_key_cb keycb, overlay_free_cb freecb, - void *data) + overlay_resize_cb resizecb, void *data) { struct timeval tv; @@ -114,6 +114,7 @@ server_client_set_overlay(struct client *c, u_int delay, c->overlay_draw = drawcb; c->overlay_key = keycb; c->overlay_free = freecb; + c->overlay_resize = resizecb; c->overlay_data = data; c->tty.flags |= TTY_FREEZE; @@ -2061,9 +2062,12 @@ server_client_dispatch(struct imsg *imsg, void *arg) if (c->flags & CLIENT_CONTROL) break; server_client_update_latest(c); - server_client_clear_overlay(c); tty_resize(&c->tty); recalculate_sizes(); + if (c->overlay_resize == NULL) + server_client_clear_overlay(c); + else + c->overlay_resize(c); server_redraw_client(c); if (c->session != NULL) notify_client("client-resized", c); diff --git a/tmux.h b/tmux.h index deb66292..03c9e702 100644 --- a/tmux.h +++ b/tmux.h @@ -1629,6 +1629,7 @@ typedef struct screen *(*overlay_mode_cb)(struct client *, u_int *, u_int *); typedef void (*overlay_draw_cb)(struct client *, struct screen_redraw_ctx *); typedef int (*overlay_key_cb)(struct client *, struct key_event *); typedef void (*overlay_free_cb)(struct client *); +typedef void (*overlay_resize_cb)(struct client *); struct client { const char *name; struct tmuxpeer *peer; @@ -1776,6 +1777,7 @@ struct client { overlay_draw_cb overlay_draw; overlay_key_cb overlay_key; overlay_free_cb overlay_free; + overlay_resize_cb overlay_resize; void *overlay_data; struct event overlay_timer; @@ -2483,7 +2485,7 @@ RB_PROTOTYPE(client_windows, client_window, entry, server_client_window_cmp); u_int server_client_how_many(void); void server_client_set_overlay(struct client *, u_int, overlay_check_cb, overlay_mode_cb, overlay_draw_cb, overlay_key_cb, - overlay_free_cb, void *); + overlay_free_cb, overlay_resize_cb, void *); void server_client_clear_overlay(struct client *); void server_client_set_key_table(struct client *, const char *); const char *server_client_get_key_table(struct client *); From 78ec0579163f6c532a1f64fd59b09e0d4f931ea0 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 21 Jul 2021 08:09:43 +0000 Subject: [PATCH 0876/1006] Do not add height twice when calculating popup_mouse_top, from M Kelly. --- cmd-display-menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index a68b9fc4..d021e477 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -175,7 +175,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, } else format_add(ft, "popup_mouse_centre_y", "%ld", n); n = (long)event->m.y + h; - if (n + h >= tty->sy) + if (n >= tty->sy) format_add(ft, "popup_mouse_top", "%u", tty->sy - 1); else format_add(ft, "popup_mouse_top", "%ld", n); From e37aa45681acf15a98de1940901d7e368dbbf33b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 28 Jul 2021 07:06:54 +0000 Subject: [PATCH 0877/1006] Make window-linked and window-unlinked window options, GitHub issue 2790. --- options-table.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/options-table.c b/options-table.c index 9f122071..bca529cf 100644 --- a/options-table.c +++ b/options-table.c @@ -1161,10 +1161,10 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("session-renamed", ""), OPTIONS_TABLE_HOOK("session-window-changed", ""), OPTIONS_TABLE_WINDOW_HOOK("window-layout-changed", ""), - OPTIONS_TABLE_WINDOW_HOOK("window-linked", ""), + OPTIONS_TABLE_HOOK("window-linked", ""), OPTIONS_TABLE_WINDOW_HOOK("window-pane-changed", ""), OPTIONS_TABLE_WINDOW_HOOK("window-renamed", ""), - OPTIONS_TABLE_WINDOW_HOOK("window-unlinked", ""), + OPTIONS_TABLE_HOOK("window-unlinked", ""), { .name = NULL } }; From 42490f4750bb6f39c08908e8e7a3b85022a077af Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 4 Aug 2021 08:07:19 +0000 Subject: [PATCH 0878/1006] Add a client-active hook, from ncfavier in GitHub issue 2803. --- options-table.c | 1 + server-client.c | 2 ++ tmux.1 | 2 ++ 3 files changed, 5 insertions(+) diff --git a/options-table.c b/options-table.c index bca529cf..a4b85fd2 100644 --- a/options-table.c +++ b/options-table.c @@ -1145,6 +1145,7 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("alert-activity", ""), OPTIONS_TABLE_HOOK("alert-bell", ""), OPTIONS_TABLE_HOOK("alert-silence", ""), + OPTIONS_TABLE_HOOK("client-active", ""), OPTIONS_TABLE_HOOK("client-attached", ""), OPTIONS_TABLE_HOOK("client-detached", ""), OPTIONS_TABLE_HOOK("client-resized", ""), diff --git a/server-client.c b/server-client.c index 1be3575e..4c7def8f 100644 --- a/server-client.c +++ b/server-client.c @@ -1127,6 +1127,8 @@ server_client_update_latest(struct client *c) if (options_get_number(w->options, "window-size") == WINDOW_SIZE_LATEST) recalculate_size(w, 0); + + notify_client("client-active", c); } /* diff --git a/tmux.1 b/tmux.1 index dd25edfc..0abb4c76 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4396,6 +4396,8 @@ See Run when a window has been silent. See .Ic monitor-silence . +.It client-active +Run when a client becomes the latest active client of its session. .It client-attached Run when a client is attached. .It client-detached From c063831df5db1d3789edd8ade4e8c370390c93de Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 4 Aug 2021 09:48:42 +0100 Subject: [PATCH 0879/1006] Do not configure on macOS without the user making a choice about utf8proc. --- configure.ac | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index dfbc24df..6df455b3 100644 --- a/configure.ac +++ b/configure.ac @@ -770,19 +770,32 @@ 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. + # macOS 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. + # macOS 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. # AC_DEFINE(BROKEN_CMSG_FIRSTHDR) AC_LIBOBJ(daemon) AC_LIBOBJ(daemon-darwin) + # + # macOS wcwidth(3) is bad, so complain and suggest using utf8proc + # instead. + # + if test "x$enable_utf8proc" = x; then + AC_MSG_NOTICE([]) + AC_MSG_NOTICE([ macOS library support for Unicode is very poor,]) + AC_MSG_NOTICE([ particularly for complex codepoints like emojis;]) + AC_MSG_NOTICE([ to use these correctly, configuring with]) + AC_MSG_NOTICE([ --enable-utf8proc is recommended. To build]) + AC_MSG_NOTICE([ without anyway, use --disable-utf8proc]) + AC_MSG_NOTICE([]) + AC_MSG_ERROR([must give --enable-utf8proc or --disable-utf8proc]) + fi ;; *dragonfly*) AC_MSG_RESULT(dragonfly) From 93cc8df6929e0a7c63ce2f0403276a064c290adb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 5 Aug 2021 09:43:51 +0000 Subject: [PATCH 0880/1006] Do not freeze output in panes when a popup is open, let them continue to redraw. From Anindya Mukherjee . --- popup.c | 4 -- screen-redraw.c | 2 +- server-client.c | 3 +- tty.c | 105 ++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 91 insertions(+), 23 deletions(-) diff --git a/popup.c b/popup.c index dfad082b..dbddc66e 100644 --- a/popup.c +++ b/popup.c @@ -339,11 +339,7 @@ popup_job_update_cb(struct job *job) return; c->overlay_check = NULL; - c->tty.flags &= ~TTY_FREEZE; - input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size); - - c->tty.flags |= TTY_FREEZE; c->overlay_check = popup_check_cb; evbuffer_drain(evb, size); diff --git a/screen-redraw.c b/screen-redraw.c index cf3e29f6..729bfb7e 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -636,7 +636,7 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) { struct screen_redraw_ctx ctx; - if (c->overlay_draw != NULL || !window_pane_visible(wp)) + if (!window_pane_visible(wp)) return; screen_redraw_set_context(c, &ctx); diff --git a/server-client.c b/server-client.c index 4c7def8f..3b102beb 100644 --- a/server-client.c +++ b/server-client.c @@ -117,7 +117,8 @@ server_client_set_overlay(struct client *c, u_int delay, c->overlay_resize = resizecb; c->overlay_data = data; - c->tty.flags |= TTY_FREEZE; + if (c->overlay_check == NULL) + c->tty.flags |= TTY_FREEZE; if (c->overlay_mode == NULL) c->tty.flags |= TTY_NOCURSOR; server_redraw_client(c); diff --git a/tty.c b/tty.c index 4b1b6777..425d3420 100644 --- a/tty.c +++ b/tty.c @@ -67,6 +67,7 @@ static void tty_repeat_space(struct tty *, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); static void tty_default_attributes(struct tty *, const struct grid_cell *, int *, u_int); +static int tty_check_overlay(struct tty *, u_int, u_int); #define tty_use_margin(tty) \ (tty->term->flags & TERM_DECSLRM) @@ -1069,6 +1070,7 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, u_int px, u_int nx, u_int bg) { struct client *c = tty->client; + u_int i; log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); @@ -1077,7 +1079,7 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, return; /* If genuine BCE is available, can try escape sequences. */ - if (!tty_fake_bce(tty, defaults, bg)) { + if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) { /* Off the end of the line, use EL if available. */ if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) { tty_cursor(tty, px, py); @@ -1100,9 +1102,22 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, } } - /* Couldn't use an escape sequence, use spaces. */ + /* + * Couldn't use an escape sequence, use spaces. Clear only the visible + * bit if there is an overlay. + */ + for (i = 0; i < nx; i++) { + if (!tty_check_overlay(tty, px + i, py)) + break; + } tty_cursor(tty, px, py); - tty_repeat_space(tty, nx); + tty_repeat_space(tty, i); + for (; i < nx; i++) { + if (tty_check_overlay(tty, px + i, py)) + break; + } + tty_cursor(tty, px + i, py); + tty_repeat_space(tty, nx - i); } /* Clear a line, adjusting to visible part of pane. */ @@ -1197,7 +1212,7 @@ tty_clear_area(struct tty *tty, const struct grid_cell *defaults, u_int py, return; /* If genuine BCE is available, can try escape sequences. */ - if (!tty_fake_bce(tty, defaults, bg)) { + if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) { /* Use ED if clearing off the bottom of the terminal. */ if (px == 0 && px + nx >= tty->sx && @@ -1332,6 +1347,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, struct grid_cell gc, last; const struct grid_cell *gcp; struct grid_line *gl; + struct client *c = tty->client; u_int i, j, ux, sx, width; int flags, cleared = 0, wrapped = 0; char buf[512]; @@ -1383,7 +1399,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, atx == 0 && px + sx != nx && tty_term_has(tty->term, TTYC_EL1) && - !tty_fake_bce(tty, defaults, 8)) { + !tty_fake_bce(tty, defaults, 8) && + c->overlay_check == NULL) { tty_default_attributes(tty, defaults, palette, 8); tty_cursor(tty, nx - 1, aty); tty_putcode(tty, TTYC_EL1); @@ -1547,11 +1564,14 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), void tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) { + struct client *c = tty->client; + if (ctx->bigger || !tty_full_width(tty, ctx) || tty_fake_bce(tty, &ctx->defaults, ctx->bg) || (!tty_term_has(tty->term, TTYC_ICH) && - !tty_term_has(tty->term, TTYC_ICH1))) { + !tty_term_has(tty->term, TTYC_ICH1)) || + c->overlay_check != NULL) { tty_draw_pane(tty, ctx, ctx->ocy); return; } @@ -1566,11 +1586,14 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) { + struct client *c = tty->client; + if (ctx->bigger || !tty_full_width(tty, ctx) || tty_fake_bce(tty, &ctx->defaults, ctx->bg) || (!tty_term_has(tty->term, TTYC_DCH) && - !tty_term_has(tty->term, TTYC_DCH1))) { + !tty_term_has(tty->term, TTYC_DCH1)) || + c->overlay_check != NULL) { tty_draw_pane(tty, ctx, ctx->ocy); return; } @@ -1593,13 +1616,16 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { + struct client *c = tty->client; + if (ctx->bigger || !tty_full_width(tty, ctx) || tty_fake_bce(tty, &ctx->defaults, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1) || ctx->sx == 1 || - ctx->sy == 1) { + ctx->sy == 1 || + c->overlay_check != NULL) { tty_redraw_region(tty, ctx); return; } @@ -1617,13 +1643,16 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { + struct client *c = tty->client; + if (ctx->bigger || !tty_full_width(tty, ctx) || tty_fake_bce(tty, &ctx->defaults, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1) || ctx->sx == 1 || - ctx->sy == 1) { + ctx->sy == 1 || + c->overlay_check != NULL) { tty_redraw_region(tty, ctx); return; } @@ -1667,6 +1696,8 @@ tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) { + struct client *c = tty->client; + if (ctx->ocy != ctx->orupper) return; @@ -1677,7 +1708,8 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) (!tty_term_has(tty->term, TTYC_RI) && !tty_term_has(tty->term, TTYC_RIN)) || ctx->sx == 1 || - ctx->sy == 1) { + ctx->sy == 1 || + c->overlay_check != NULL) { tty_redraw_region(tty, ctx); return; } @@ -1697,6 +1729,8 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) { + struct client *c = tty->client; + if (ctx->ocy != ctx->orlower) return; @@ -1705,7 +1739,8 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) tty_fake_bce(tty, &ctx->defaults, 8) || !tty_term_has(tty->term, TTYC_CSR) || ctx->sx == 1 || - ctx->sy == 1) { + ctx->sy == 1 || + c->overlay_check != NULL) { tty_redraw_region(tty, ctx); return; } @@ -1736,14 +1771,16 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) { - u_int i; + struct client *c = tty->client; + u_int i; if (ctx->bigger || (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, &ctx->defaults, 8) || !tty_term_has(tty->term, TTYC_CSR) || ctx->sx == 1 || - ctx->sy == 1) { + ctx->sy == 1 || + c->overlay_check != NULL) { tty_redraw_region(tty, ctx); return; } @@ -1772,7 +1809,8 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) { - u_int i; + u_int i; + struct client *c = tty->client; if (ctx->bigger || (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || @@ -1781,7 +1819,8 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) (!tty_term_has(tty->term, TTYC_RI) && !tty_term_has(tty->term, TTYC_RIN)) || ctx->sx == 1 || - ctx->sy == 1) { + ctx->sy == 1 || + c->overlay_check != NULL) { tty_redraw_region(tty, ctx); return; } @@ -1891,9 +1930,28 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) { - if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1)) + const struct grid_cell *gcp = ctx->cell; + struct screen *s = ctx->s; + u_int i, px, py; + + px = ctx->xoff + ctx->ocx - ctx->wox; + py = ctx->yoff + ctx->ocy - ctx->woy; + if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1) || + (gcp->data.width == 1 && !tty_check_overlay(tty, px, py))) return; + /* Handle partially obstructed wide characters. */ + if (gcp->data.width > 1) { + for (i = 0; i < gcp->data.width; i++) { + if (!tty_check_overlay(tty, px + i, py)) { + tty_draw_line(tty, s, s->cx, s->cy, + gcp->data.width, px, py, &ctx->defaults, + ctx->palette); + return; + } + } + } + if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 && ctx->ocy == ctx->orlower && tty_full_width(tty, ctx)) @@ -1908,6 +1966,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) { + u_int i, hide = 0; + if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1)) return; @@ -1931,7 +1991,18 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette); - tty_putn(tty, ctx->ptr, ctx->num, ctx->num); + for (i = 0; i < ctx->num; i++) { + if (!tty_check_overlay(tty, tty->cx + i, tty->cy)) + break; + } + tty_putn(tty, ctx->ptr, i, i); + for (; i < ctx->num; i++) { + if (tty_check_overlay(tty, tty->cx + hide, tty->cy)) + break; + hide++; + } + tty_cursor(tty, tty->cx + hide, tty->cy); + tty_putn(tty, (char *)ctx->ptr + i, ctx->num - i, ctx->num - i); } void From 97b5962ab1ce08f50001f2bbcd87685e8dcce222 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 6 Aug 2021 03:13:05 +0000 Subject: [PATCH 0881/1006] Correctly draw wide characters that are partially obscured. --- tty.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/tty.c b/tty.c index 425d3420..cb179250 100644 --- a/tty.c +++ b/tty.c @@ -1349,7 +1349,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, struct grid_line *gl; struct client *c = tty->client; u_int i, j, ux, sx, width; - int flags, cleared = 0, wrapped = 0; + int flags, cleared = 0, wrapped = 0, hidden; char buf[512]; size_t len; u_int cellsize; @@ -1449,17 +1449,24 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, screen_select_cell(s, &last, gcp); else memcpy(&last, gcp, sizeof last); - if (!tty_check_overlay(tty, atx + ux, aty)) { - if (~gcp->flags & GRID_FLAG_PADDING) - ux += gcp->data.width; - } else if (ux + gcp->data.width > nx) { - tty_attributes(tty, &last, defaults, palette); - tty_cursor(tty, atx + ux, aty); - for (j = 0; j < gcp->data.width; j++) { - if (ux + j > nx) - break; - tty_putc(tty, ' '); - ux++; + + hidden = 0; + for (j = 0; j < gcp->data.width; j++) { + if (!tty_check_overlay(tty, atx + ux + j, aty)) { + hidden = 1; + break; + } + } + if (hidden || ux + gcp->data.width > nx) { + if (~gcp->flags & GRID_FLAG_PADDING) { + tty_attributes(tty, &last, defaults, palette); + tty_cursor(tty, atx + ux, aty); + for (j = 0; j < gcp->data.width; j++) { + if (ux + j > nx) + break; + tty_putc(tty, ' '); + ux++; + } } } else if (gcp->attr & GRID_ATTR_CHARSET) { tty_attributes(tty, &last, defaults, palette); From 950d3c5bbc99b68a353e78bbfe60933d60e3cf46 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 6 Aug 2021 03:29:15 +0000 Subject: [PATCH 0882/1006] Tweak previous not to replace complete characters with spaces. --- tty.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tty.c b/tty.c index cb179250..e0a3f6b6 100644 --- a/tty.c +++ b/tty.c @@ -1348,8 +1348,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, const struct grid_cell *gcp; struct grid_line *gl; struct client *c = tty->client; - u_int i, j, ux, sx, width; - int flags, cleared = 0, wrapped = 0, hidden; + u_int i, j, ux, sx, width, hidden; + int flags, cleared = 0, wrapped = 0; char buf[512]; size_t len; u_int cellsize; @@ -1452,12 +1452,13 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, hidden = 0; for (j = 0; j < gcp->data.width; j++) { - if (!tty_check_overlay(tty, atx + ux + j, aty)) { - hidden = 1; - break; - } + if (!tty_check_overlay(tty, atx + ux + j, aty)) + hidden++; } - if (hidden || ux + gcp->data.width > nx) { + if (hidden != 0 && hidden == gcp->data.width) { + if (~gcp->flags & GRID_FLAG_PADDING) + ux += gcp->data.width; + } else if (hidden != 0 || ux + gcp->data.width > nx) { if (~gcp->flags & GRID_FLAG_PADDING) { tty_attributes(tty, &last, defaults, palette); tty_cursor(tty, atx + ux, aty); From ef5602a5901774425d455a11f71a49258ed2ead6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 6 Aug 2021 07:32:21 +0000 Subject: [PATCH 0883/1006] Another minor fix - do not draw positions that are under the popup with spaces, from Anindya Mukherjee. Also a typo fix from Linus Arver. --- screen-write.c | 2 +- tty.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/screen-write.c b/screen-write.c index e351a5e5..8cc81f92 100644 --- a/screen-write.c +++ b/screen-write.c @@ -596,7 +596,7 @@ screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right) screen_write_set_cursor(ctx, cx, cy); } -/* Draw a horizontal line on screen. */ +/* Draw a vertical line on screen. */ void screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom) { diff --git a/tty.c b/tty.c index e0a3f6b6..cb0fdec0 100644 --- a/tty.c +++ b/tty.c @@ -1463,9 +1463,15 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, tty_attributes(tty, &last, defaults, palette); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.width; j++) { - if (ux + j > nx) + if (ux > nx) break; - tty_putc(tty, ' '); + if (tty_check_overlay(tty, atx + ux, + aty)) + tty_putc(tty, ' '); + else { + tty_cursor(tty, atx + ux + 1, + aty); + } ux++; } } From 19812b2d29e305927461c26651a3d228c22d928a Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 6 Aug 2021 09:19:02 +0000 Subject: [PATCH 0884/1006] Add client focus hooks. --- options-table.c | 2 ++ tmux.1 | 4 ++++ tty-keys.c | 7 +++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/options-table.c b/options-table.c index a4b85fd2..e0ae993a 100644 --- a/options-table.c +++ b/options-table.c @@ -1148,6 +1148,8 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("client-active", ""), OPTIONS_TABLE_HOOK("client-attached", ""), OPTIONS_TABLE_HOOK("client-detached", ""), + OPTIONS_TABLE_HOOK("client-focus-in", ""), + OPTIONS_TABLE_HOOK("client-focus-out", ""), OPTIONS_TABLE_HOOK("client-resized", ""), OPTIONS_TABLE_HOOK("client-session-changed", ""), OPTIONS_TABLE_PANE_HOOK("pane-died", ""), diff --git a/tmux.1 b/tmux.1 index 0abb4c76..00b90fe7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4402,6 +4402,10 @@ Run when a client becomes the latest active client of its session. Run when a client is attached. .It client-detached Run when a client is detached +.It client-focus-in +Run when focus enters a client +.It client-focus-out +Run when focus exits a client .It client-resized Run when a client is resized. .It client-session-changed diff --git a/tty-keys.c b/tty-keys.c index 3012be3d..02fba0e5 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -820,10 +820,13 @@ complete_key: tty->flags &= ~TTY_TIMER; /* Check for focus events. */ - if (key == KEYC_FOCUS_OUT) + if (key == KEYC_FOCUS_OUT) { tty->client->flags &= ~CLIENT_FOCUSED; - else if (key == KEYC_FOCUS_IN) + notify_client("client-focus-out", c); + } else if (key == KEYC_FOCUS_IN) { tty->client->flags |= CLIENT_FOCUSED; + notify_client("client-focus-in", c); + } /* Fire the key. */ if (key != KEYC_UNKNOWN) { From 24cd6851f6a4e0152f59d22bf446e553cd7f23f9 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 6 Aug 2021 09:34:09 +0000 Subject: [PATCH 0885/1006] Add basic support for zero width joiners, GitHub issues 1605 and 2784. --- screen-write.c | 32 ++++++++++++++++++++++++++++---- tmux.h | 1 + 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/screen-write.c b/screen-write.c index 8cc81f92..b04155c3 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1726,6 +1726,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) { struct screen *s = ctx->s; struct grid *gd = s->grid; + const struct utf8_data *ud = &gc->data; + const struct utf8_data zwj = { "\342\200\215", 0, 3, 0 }; struct grid_line *gl; struct grid_cell_entry *gce; struct grid_cell tmp_gc, now_gc; @@ -1738,16 +1740,38 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) if (gc->flags & GRID_FLAG_PADDING) return; - /* If the width is zero, combine onto the previous character. */ - if (width == 0) { + /* + * If this is a zero width joiner, set the flag so the next character + * will be treated as zero width and appended. Note that we assume a + * ZWJ will not change the width - the width of the first character is + * used. + */ + if (ud->size == 3 && memcmp(ud->data, "\342\200\215", 3) == 0) { + log_debug("zero width joiner at %u,%u", s->cx, s->cy); + ctx->flags |= SCREEN_WRITE_ZWJ; + return; + } + + /* + * If the width is zero, combine onto the previous character. We always + * combine with the cell to the left of the cursor position. In theory, + * the application could have moved the cursor somewhere else, but if + * they are silly enough to do that, who cares? + */ + if (ctx->flags & SCREEN_WRITE_ZWJ) { screen_write_collect_flush(ctx, 0, __func__); - if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { + screen_write_combine(ctx, &zwj, &xx); + } + if (width == 0 || (ctx->flags & SCREEN_WRITE_ZWJ)) { + ctx->flags &= ~SCREEN_WRITE_ZWJ; + screen_write_collect_flush(ctx, 0, __func__); + if ((gc = screen_write_combine(ctx, ud, &xx)) != 0) { cx = s->cx; cy = s->cy; screen_write_set_cursor(ctx, xx, s->cy); screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); - s->cx = cx; s->cy = cy; + s->cx = xx + gc->data.width; s->cy = cy; } return; } diff --git a/tmux.h b/tmux.h index 03c9e702..b2d059a1 100644 --- a/tmux.h +++ b/tmux.h @@ -866,6 +866,7 @@ struct screen_write_ctx { int flags; #define SCREEN_WRITE_SYNC 0x1 +#define SCREEN_WRITE_ZWJ 0x2 screen_write_init_ctx_cb init_ctx_cb; void *arg; From be5988457f2993d5a6efa5f6d5add37690ea6a30 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 9 Aug 2021 13:08:08 +0000 Subject: [PATCH 0886/1006] Change copy-line and copy-end-of-line not to cancel and add -and-cancel variants, like the other copy commands. GitHub issue 2799. --- key-bindings.c | 4 +-- tmux.1 | 4 ++- window-copy.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index de5c20ee..ddb7e0b1 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -476,7 +476,7 @@ key_bindings_init(void) "bind -Tcopy-mode C-f send -X cursor-right", "bind -Tcopy-mode C-b send -X cursor-left", "bind -Tcopy-mode C-g send -X clear-selection", - "bind -Tcopy-mode C-k send -X copy-end-of-line", + "bind -Tcopy-mode C-k send -X copy-end-of-line-and-cancel", "bind -Tcopy-mode C-n send -X cursor-down", "bind -Tcopy-mode C-p send -X cursor-up", "bind -Tcopy-mode C-r command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", @@ -575,7 +575,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi ? command-prompt -T search -p'(search up)' 'send -X search-backward \"%%%\"'", "bind -Tcopy-mode-vi A send -X append-selection-and-cancel", "bind -Tcopy-mode-vi B send -X previous-space", - "bind -Tcopy-mode-vi D send -X copy-end-of-line", + "bind -Tcopy-mode-vi D send -X copy-end-of-line-and-cancel", "bind -Tcopy-mode-vi E send -X next-space-end", "bind -Tcopy-mode-vi F command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"'", "bind -Tcopy-mode-vi G send -X history-bottom", diff --git a/tmux.1 b/tmux.1 index 00b90fe7..a2872535 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1676,8 +1676,10 @@ The following commands are supported in copy mode: .It Li "bottom-line" Ta "L" Ta "" .It Li "cancel" Ta "q" Ta "Escape" .It Li "clear-selection" Ta "Escape" Ta "C-g" -.It Li "copy-end-of-line []" Ta "D" Ta "C-k" +.It Li "copy-end-of-line []" Ta "" Ta "" +.It Li "copy-end-of-line-and-cancel []" Ta "D" Ta "C-k" .It Li "copy-line []" Ta "" Ta "" +.It Li "copy-line-and-cancel []" Ta "" Ta "" .It Li "copy-pipe [] []" Ta "" Ta "" .It Li "copy-pipe-no-clear [] []" Ta "" Ta "" .It Li "copy-pipe-and-cancel [] []" Ta "" Ta "" diff --git a/window-copy.c b/window-copy.c index 31875739..5c74cc2d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -960,6 +960,42 @@ window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs) static enum window_copy_cmd_action window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct client *c = cs->c; + struct session *s = cs->s; + struct winlink *wl = cs->wl; + struct window_pane *wp = wme->wp; + u_int np = wme->prefix, ocx, ocy, ooy; + struct window_copy_mode_data *data = wme->data; + char *prefix = NULL; + + if (cs->args->argc == 2) + prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + + ocx = data->cx; + ocy = data->cy; + ooy = data->oy; + + window_copy_start_selection(wme); + for (; np > 1; np--) + window_copy_cursor_down(wme, 0); + window_copy_cursor_end_of_line(wme); + + if (s != NULL) + window_copy_copy_selection(wme, prefix); + window_copy_clear_selection(wme); + + data->cx = ocx; + data->cy = ocy; + data->oy = ooy; + + free(prefix); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_copy_end_of_line_and_cancel(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct client *c = cs->c; @@ -990,6 +1026,44 @@ window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) static enum window_copy_cmd_action window_copy_cmd_copy_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct client *c = cs->c; + struct session *s = cs->s; + struct winlink *wl = cs->wl; + struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix, ocx, ocy, ooy; + char *prefix = NULL; + + if (cs->args->argc == 2) + prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + + ocx = data->cx; + ocy = data->cy; + ooy = data->oy; + + data->selflag = SEL_CHAR; + window_copy_cursor_start_of_line(wme); + window_copy_start_selection(wme); + for (; np > 1; np--) + window_copy_cursor_down(wme, 0); + window_copy_cursor_end_of_line(wme); + + if (s != NULL) + window_copy_copy_selection(wme, prefix); + window_copy_clear_selection(wme); + + data->cx = ocx; + data->cy = ocy; + data->oy = ooy; + + free(prefix); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_copy_line_and_cancel(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct client *c = cs->c; @@ -2264,8 +2338,12 @@ static const struct { window_copy_cmd_clear_selection }, { "copy-end-of-line", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_copy_end_of_line }, + { "copy-end-of-line-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, + window_copy_cmd_copy_end_of_line_and_cancel }, { "copy-line", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, window_copy_cmd_copy_line }, + { "copy-line-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, + window_copy_cmd_copy_line_and_cancel }, { "copy-pipe-no-clear", 0, 2, WINDOW_COPY_CMD_CLEAR_NEVER, window_copy_cmd_copy_pipe_no_clear }, { "copy-pipe", 0, 2, WINDOW_COPY_CMD_CLEAR_ALWAYS, From 705411ea5cccf4607af9424e70a153f397d1fab6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 10 Aug 2021 08:14:01 +0100 Subject: [PATCH 0887/1006] Tweak comment about logs. --- .github/CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 48171136..89c6549b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -37,7 +37,8 @@ Also include: - Your terminal, and `$TERM` inside and outside of tmux. -- Logs from tmux (see below). +- Logs from tmux (see below). Please attach logs to the issue directly rather + than using a download site or pastebin. Put in a zip file if necessary. - At most one or two screenshots, if helpful. From f6755c6f2c6fd10a90984fadad4c9ac4a8a92027 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 11 Aug 2021 07:51:31 +0000 Subject: [PATCH 0888/1006] OSC 52 can be long enough to make tmux think the output buffer is too big, so treat it as a redraw. GitHub issue 2814. --- tty.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tty.c b/tty.c index cb0fdec0..61b38eec 100644 --- a/tty.c +++ b/tty.c @@ -2041,6 +2041,7 @@ tty_set_selection(struct tty *tty, const char *buf, size_t len) b64_ntop(buf, len, encoded, size); tty_putcode_ptr2(tty, TTYC_MS, "", encoded); + tty->client->redraw = EVBUFFER_LENGTH(tty->out); free(encoded); } From 338ec859a463eaa4c0502116ef2857c566c9e468 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 11 Aug 2021 08:40:58 +0000 Subject: [PATCH 0889/1006] Make confirm-before optionally block the invoking client like run-shell, GitHub issue 2819. --- cmd-confirm-before.c | 38 +++++++++++++++++++++++++++++++------- tmux.1 | 7 ++++--- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 0b490b17..83f9a0f8 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -39,15 +39,17 @@ const struct cmd_entry cmd_confirm_before_entry = { .name = "confirm-before", .alias = "confirm", - .args = { "p:t:", 1, 1 }, - .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", + .args = { "bp:t:", 1, 1 }, + .usage = "[-b] [-p prompt] " CMD_TARGET_CLIENT_USAGE " command", .flags = CMD_CLIENT_TFLAG, .exec = cmd_confirm_before_exec }; struct cmd_confirm_before_data { - char *cmd; + char *cmd; + struct cmdq_item *item; + struct cmd_parse_input pi; }; static enum cmd_retval @@ -59,6 +61,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *target = cmdq_get_target(item); char *cmd, *copy, *new_prompt, *ptr; const char *prompt; + int wait = !args_has(args, 'b'); if ((prompt = args_get(args, 'p')) != NULL) xasprintf(&new_prompt, "%s ", prompt); @@ -72,12 +75,24 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) cdata = xmalloc(sizeof *cdata); cdata->cmd = xstrdup(args->argv[0]); + memset(&cdata->pi, 0, sizeof cdata->pi); + cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); + if (wait) + cdata->pi.item = item; + cdata->pi.c = tc; + cmd_find_copy_state(&cdata->pi.fs, target); + + if (wait) + cdata->item = item; + status_prompt_set(tc, target, new_prompt, NULL, cmd_confirm_before_callback, cmd_confirm_before_free, cdata, PROMPT_SINGLE, PROMPT_TYPE_COMMAND); free(new_prompt); - return (CMD_RETURN_NORMAL); + if (!wait) + return (CMD_RETURN_NORMAL); + return (CMD_RETURN_WAIT); } static int @@ -85,23 +100,32 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, __unused int done) { struct cmd_confirm_before_data *cdata = data; + const char *cmd = cdata->cmd; char *error; + struct cmdq_item *item = cdata->item; enum cmd_parse_status status; if (c->flags & CLIENT_DEAD) return (0); if (s == NULL || *s == '\0') - return (0); + goto out; if (tolower((u_char)s[0]) != 'y' || s[1] != '\0') - return (0); + goto out; - status = cmd_parse_and_append(cdata->cmd, NULL, c, NULL, &error); + if (item != NULL) { + status = cmd_parse_and_insert(cmd, &cdata->pi, item, + cmdq_get_state(item), &error); + } else + status = cmd_parse_and_append(cmd, &cdata->pi, c, NULL, &error); if (status == CMD_PARSE_ERROR) { cmdq_append(c, cmdq_get_error(error)); free(error); } +out: + if (item != NULL) + cmdq_continue(item); return (0); } diff --git a/tmux.1 b/tmux.1 index a2872535..95f1ba41 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5507,6 +5507,7 @@ option: .It Li "Transpose characters" Ta "" Ta "C-t" .El .It Xo Ic confirm-before +.Op Fl b .Op Fl p Ar prompt .Op Fl t Ar target-client .Ar command @@ -5523,9 +5524,9 @@ is the prompt to display; otherwise a prompt is constructed from It may contain the special character sequences supported by the .Ic status-left option. -.Pp -This command works only from inside -.Nm . +With +.Fl b , +the prompt is shown in the background and the client. .It Xo Ic display-menu .Op Fl O .Op Fl c Ar target-client From 901360007479f176164ab2756072808fc17608c1 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 11 Aug 2021 09:05:21 +0000 Subject: [PATCH 0890/1006] Return to applying pane-border-style to the area outside panes, GitHub issue 2816. --- screen-redraw.c | 17 ++++++++++++++--- tmux.h | 3 +++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 729bfb7e..4c4135a8 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -680,7 +680,10 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) { struct client *c = ctx->c; struct session *s = c->session; + struct window *w = s->curw->window; + struct options *oo = w->options; struct tty *tty = &c->tty; + struct format_tree *ft; struct window_pane *wp; u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; int pane_status = ctx->pane_status, isolates; @@ -694,9 +697,17 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) if (cell_type == CELL_INSIDE) return; - if (wp == NULL) - memcpy(&gc, &grid_default_cell, sizeof gc); - else { + if (wp == NULL) { + if (!ctx->no_pane_gc_set) { + ft = format_create_defaults(NULL, c, s, s->curw, NULL); + memcpy(&ctx->no_pane_gc, &grid_default_cell, sizeof gc); + style_add(&ctx->no_pane_gc, oo, "pane-border-style", + ft); + format_free(ft); + ctx->no_pane_gc_set = 1; + } + memcpy(&gc, &ctx->no_pane_gc, sizeof gc); + } else { tmp = screen_redraw_draw_borders_style(ctx, x, y, wp); if (tmp == NULL) return; diff --git a/tmux.h b/tmux.h index b2d059a1..42f03bd6 100644 --- a/tmux.h +++ b/tmux.h @@ -886,6 +886,9 @@ struct screen_redraw_ctx { int pane_status; int pane_lines; + struct grid_cell no_pane_gc; + int no_pane_gc_set; + u_int sx; u_int sy; u_int ox; From 01fd4b997e3a0a74ea57d6830cf97f98ea2c2a7c Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 11 Aug 2021 20:35:46 +0000 Subject: [PATCH 0891/1006] Add pipe variants of the line copy commands. While here make the command list less unreadable. GitHub issue 2813. --- key-bindings.c | 4 +- window-copy.c | 822 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 588 insertions(+), 238 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index ddb7e0b1..1bdbee98 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -476,7 +476,7 @@ key_bindings_init(void) "bind -Tcopy-mode C-f send -X cursor-right", "bind -Tcopy-mode C-b send -X cursor-left", "bind -Tcopy-mode C-g send -X clear-selection", - "bind -Tcopy-mode C-k send -X copy-end-of-line-and-cancel", + "bind -Tcopy-mode C-k send -X copy-pipe-end-of-line-and-cancel", "bind -Tcopy-mode C-n send -X cursor-down", "bind -Tcopy-mode C-p send -X cursor-up", "bind -Tcopy-mode C-r command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", @@ -575,7 +575,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi ? command-prompt -T search -p'(search up)' 'send -X search-backward \"%%%\"'", "bind -Tcopy-mode-vi A send -X append-selection-and-cancel", "bind -Tcopy-mode-vi B send -X previous-space", - "bind -Tcopy-mode-vi D send -X copy-end-of-line-and-cancel", + "bind -Tcopy-mode-vi D send -X copy-pipe-end-of-line-and-cancel", "bind -Tcopy-mode-vi E send -X next-space-end", "bind -Tcopy-mode-vi F command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"'", "bind -Tcopy-mode-vi G send -X history-bottom", diff --git a/window-copy.c b/window-copy.c index 5c74cc2d..951d7581 100644 --- a/window-copy.c +++ b/window-copy.c @@ -959,19 +959,29 @@ window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs) } static enum window_copy_cmd_action -window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) +window_copy_do_copy_end_of_line(struct window_copy_cmd_state *cs, int pipe, + int cancel) { - struct window_mode_entry *wme = cs->wme; - struct client *c = cs->c; - struct session *s = cs->s; - struct winlink *wl = cs->wl; - struct window_pane *wp = wme->wp; - u_int np = wme->prefix, ocx, ocy, ooy; - struct window_copy_mode_data *data = wme->data; - char *prefix = NULL; + struct window_mode_entry *wme = cs->wme; + int argc = cs->args->argc; + char **argv = cs->args->argv; + struct client *c = cs->c; + struct session *s = cs->s; + struct winlink *wl = cs->wl; + struct window_pane *wp = wme->wp; + u_int np = wme->prefix, ocx, ocy, ooy; + struct window_copy_mode_data *data = wme->data; + char *prefix = NULL, *command = NULL; - if (cs->args->argc == 2) - prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + if (pipe) { + if (argc == 3) + prefix = format_single(NULL, argv[2], c, s, wl, wp); + if (s != NULL && argc > 1 && *argv[1] != '\0') + command = format_single(NULL, argv[1], c, s, wl, wp); + } else { + if (argc == 2) + prefix = format_single(NULL, argv[1], c, s, wl, wp); + } ocx = data->cx; ocy = data->cy; @@ -982,8 +992,18 @@ window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) window_copy_cursor_down(wme, 0); window_copy_cursor_end_of_line(wme); - if (s != NULL) - window_copy_copy_selection(wme, prefix); + if (s != NULL) { + if (pipe) + window_copy_copy_pipe(wme, s, prefix, command); + else + window_copy_copy_selection(wme, prefix); + + if (cancel) { + free(prefix); + free(command); + return (WINDOW_COPY_CMD_CANCEL); + } + } window_copy_clear_selection(wme); data->cx = ocx; @@ -991,108 +1011,115 @@ window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) data->oy = ooy; free(prefix); + free(command); return (WINDOW_COPY_CMD_REDRAW); } +static enum window_copy_cmd_action +window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) +{ + return (window_copy_do_copy_end_of_line(cs, 0, 0)); +} + static enum window_copy_cmd_action window_copy_cmd_copy_end_of_line_and_cancel(struct window_copy_cmd_state *cs) { - struct window_mode_entry *wme = cs->wme; - struct client *c = cs->c; - struct session *s = cs->s; - struct winlink *wl = cs->wl; - struct window_pane *wp = wme->wp; - u_int np = wme->prefix; - char *prefix = NULL; + return (window_copy_do_copy_end_of_line(cs, 0, 1)); +} - if (cs->args->argc == 2) - prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); +static enum window_copy_cmd_action +window_copy_cmd_copy_pipe_end_of_line(struct window_copy_cmd_state *cs) +{ + return (window_copy_do_copy_end_of_line(cs, 1, 0)); +} +static enum window_copy_cmd_action +window_copy_cmd_copy_pipe_end_of_line_and_cancel( + struct window_copy_cmd_state *cs) +{ + return (window_copy_do_copy_end_of_line(cs, 1, 1)); +} + +static enum window_copy_cmd_action +window_copy_do_copy_line(struct window_copy_cmd_state *cs, int pipe, int cancel) +{ + struct window_mode_entry *wme = cs->wme; + int argc = cs->args->argc; + char **argv = cs->args->argv; + struct client *c = cs->c; + struct session *s = cs->s; + struct winlink *wl = cs->wl; + struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix, ocx, ocy, ooy; + char *prefix = NULL, *command = NULL; + + if (pipe) { + if (argc == 3) + prefix = format_single(NULL, argv[2], c, s, wl, wp); + if (s != NULL && argc > 1 && *argv[1] != '\0') + command = format_single(NULL, argv[1], c, s, wl, wp); + } else { + if (argc == 2) + prefix = format_single(NULL, argv[1], c, s, wl, wp); + } + + ocx = data->cx; + ocy = data->cy; + ooy = data->oy; + + data->selflag = SEL_CHAR; + window_copy_cursor_start_of_line(wme); window_copy_start_selection(wme); for (; np > 1; np--) window_copy_cursor_down(wme, 0); window_copy_cursor_end_of_line(wme); if (s != NULL) { - window_copy_copy_selection(wme, prefix); + if (pipe) + window_copy_copy_pipe(wme, s, prefix, command); + else + window_copy_copy_selection(wme, prefix); - free(prefix); - return (WINDOW_COPY_CMD_CANCEL); + if (cancel) { + free(prefix); + free(command); + return (WINDOW_COPY_CMD_CANCEL); + } } + window_copy_clear_selection(wme); + + data->cx = ocx; + data->cy = ocy; + data->oy = ooy; free(prefix); + free(command); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_copy_line(struct window_copy_cmd_state *cs) { - struct window_mode_entry *wme = cs->wme; - struct client *c = cs->c; - struct session *s = cs->s; - struct winlink *wl = cs->wl; - struct window_pane *wp = wme->wp; - struct window_copy_mode_data *data = wme->data; - u_int np = wme->prefix, ocx, ocy, ooy; - char *prefix = NULL; - - if (cs->args->argc == 2) - prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); - - ocx = data->cx; - ocy = data->cy; - ooy = data->oy; - - data->selflag = SEL_CHAR; - window_copy_cursor_start_of_line(wme); - window_copy_start_selection(wme); - for (; np > 1; np--) - window_copy_cursor_down(wme, 0); - window_copy_cursor_end_of_line(wme); - - if (s != NULL) - window_copy_copy_selection(wme, prefix); - window_copy_clear_selection(wme); - - data->cx = ocx; - data->cy = ocy; - data->oy = ooy; - - free(prefix); - return (WINDOW_COPY_CMD_REDRAW); + return (window_copy_do_copy_line(cs, 0, 0)); } static enum window_copy_cmd_action window_copy_cmd_copy_line_and_cancel(struct window_copy_cmd_state *cs) { - struct window_mode_entry *wme = cs->wme; - struct client *c = cs->c; - struct session *s = cs->s; - struct winlink *wl = cs->wl; - struct window_pane *wp = wme->wp; - struct window_copy_mode_data *data = wme->data; - u_int np = wme->prefix; - char *prefix = NULL; + return (window_copy_do_copy_line(cs, 0, 1)); +} - if (cs->args->argc == 2) - prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); +static enum window_copy_cmd_action +window_copy_cmd_copy_pipe_line(struct window_copy_cmd_state *cs) +{ + return (window_copy_do_copy_line(cs, 1, 0)); +} - data->selflag = SEL_CHAR; - window_copy_cursor_start_of_line(wme); - window_copy_start_selection(wme); - for (; np > 1; np--) - window_copy_cursor_down(wme, 0); - window_copy_cursor_end_of_line(wme); - - if (s != NULL) { - window_copy_copy_selection(wme, prefix); - - free(prefix); - return (WINDOW_COPY_CMD_CANCEL); - } - - free(prefix); - return (WINDOW_COPY_CMD_REDRAW); +static enum window_copy_cmd_action +window_copy_cmd_copy_pipe_line_and_cancel(struct window_copy_cmd_state *cs) +{ + return (window_copy_do_copy_line(cs, 1, 1)); } static enum window_copy_cmd_action @@ -2308,8 +2335,7 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) screen_free(data->backing); free(data->backing); - data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, - NULL, wme->swp != wme->wp); + data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, NULL, wme->swp != wme->wp); window_copy_size_changed(wme); return (WINDOW_COPY_CMD_REDRAW); @@ -2322,156 +2348,480 @@ static const struct { enum window_copy_cmd_clear clear; enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); } window_copy_cmd_table[] = { - { "append-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_append_selection }, - { "append-selection-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_append_selection_and_cancel }, - { "back-to-indentation", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_back_to_indentation }, - { "begin-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_begin_selection }, - { "bottom-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_bottom_line }, - { "cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_cancel }, - { "clear-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_clear_selection }, - { "copy-end-of-line", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_copy_end_of_line }, - { "copy-end-of-line-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_copy_end_of_line_and_cancel }, - { "copy-line", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_copy_line }, - { "copy-line-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_copy_line_and_cancel }, - { "copy-pipe-no-clear", 0, 2, WINDOW_COPY_CMD_CLEAR_NEVER, - window_copy_cmd_copy_pipe_no_clear }, - { "copy-pipe", 0, 2, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_copy_pipe }, - { "copy-pipe-and-cancel", 0, 2, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_copy_pipe_and_cancel }, - { "copy-selection-no-clear", 0, 1, WINDOW_COPY_CMD_CLEAR_NEVER, - window_copy_cmd_copy_selection_no_clear }, - { "copy-selection", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_copy_selection }, - { "copy-selection-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_copy_selection_and_cancel }, - { "cursor-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_cursor_down }, - { "cursor-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_cursor_down_and_cancel }, - { "cursor-left", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_cursor_left }, - { "cursor-right", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_cursor_right }, - { "cursor-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_cursor_up }, - { "end-of-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_end_of_line }, - { "goto-line", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_goto_line }, - { "halfpage-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_halfpage_down }, - { "halfpage-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_halfpage_down_and_cancel }, - { "halfpage-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_halfpage_up }, - { "history-bottom", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_history_bottom }, - { "history-top", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_history_top }, - { "jump-again", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_jump_again }, - { "jump-backward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_jump_backward }, - { "jump-forward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_jump_forward }, - { "jump-reverse", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_jump_reverse }, - { "jump-to-backward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_jump_to_backward }, - { "jump-to-forward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_jump_to_forward }, - { "jump-to-mark", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_jump_to_mark }, - { "middle-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_middle_line }, - { "next-matching-bracket", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_next_matching_bracket }, - { "next-paragraph", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_next_paragraph }, - { "next-space", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_next_space }, - { "next-space-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_next_space_end }, - { "next-word", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_next_word }, - { "next-word-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_next_word_end }, - { "other-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_other_end }, - { "page-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_page_down }, - { "page-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_page_down_and_cancel }, - { "page-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_page_up }, - { "pipe-no-clear", 0, 1, WINDOW_COPY_CMD_CLEAR_NEVER, - window_copy_cmd_pipe_no_clear }, - { "pipe", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_pipe }, - { "pipe-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_pipe_and_cancel }, - { "previous-matching-bracket", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_previous_matching_bracket }, - { "previous-paragraph", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_previous_paragraph }, - { "previous-space", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_previous_space }, - { "previous-word", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_previous_word }, - { "rectangle-on", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_rectangle_on }, - { "rectangle-off", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_rectangle_off }, - { "rectangle-toggle", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_rectangle_toggle }, - { "refresh-from-pane", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_refresh_from_pane }, - { "scroll-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_scroll_down }, - { "scroll-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_scroll_down_and_cancel }, - { "scroll-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_scroll_up }, - { "search-again", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_search_again }, - { "search-backward", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_search_backward }, - { "search-backward-text", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_search_backward_text }, - { "search-backward-incremental", 1, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_search_backward_incremental }, - { "search-forward", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_search_forward }, - { "search-forward-text", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_search_forward_text }, - { "search-forward-incremental", 1, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_search_forward_incremental }, - { "search-reverse", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_search_reverse }, - { "select-line", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_select_line }, - { "select-word", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_select_word }, - { "set-mark", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_set_mark }, - { "start-of-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_start_of_line }, - { "stop-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, - window_copy_cmd_stop_selection }, - { "top-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, - window_copy_cmd_top_line }, + { .command = "append-selection", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_append_selection + }, + { .command = "append-selection-and-cancel", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_append_selection_and_cancel + }, + { .command = "back-to-indentation", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_back_to_indentation + }, + { .command = "begin-selection", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_begin_selection + }, + { .command = "bottom-line", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_bottom_line + }, + { .command = "cancel", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_cancel + }, + { .command = "clear-selection", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_clear_selection + }, + { .command = "copy-end-of-line", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_end_of_line + }, + { .command = "copy-end-of-line-and-cancel", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_end_of_line_and_cancel + }, + { .command = "copy-pipe-end-of-line", + .minargs = 0, + .maxargs = 2, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_pipe_end_of_line + }, + { .command = "copy-pipe-end-of-line-and-cancel", + .minargs = 0, + .maxargs = 2, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_pipe_end_of_line_and_cancel + }, + { .command = "copy-line", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_line + }, + { .command = "copy-line-and-cancel", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_line_and_cancel + }, + { .command = "copy-pipe-line", + .minargs = 0, + .maxargs = 2, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_pipe_line + }, + { .command = "copy-pipe-line-and-cancel", + .minargs = 0, + .maxargs = 2, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_pipe_line_and_cancel + }, + { .command = "copy-pipe-no-clear", + .minargs = 0, + .maxargs = 2, + .clear = WINDOW_COPY_CMD_CLEAR_NEVER, + .f = window_copy_cmd_copy_pipe_no_clear + }, + { .command = "copy-pipe", + .minargs = 0, + .maxargs = 2, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_pipe + }, + { .command = "copy-pipe-and-cancel", + .minargs = 0, + .maxargs = 2, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_pipe_and_cancel + }, + { .command = "copy-selection-no-clear", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_NEVER, + .f = window_copy_cmd_copy_selection_no_clear + }, + { .command = "copy-selection", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_selection + }, + { .command = "copy-selection-and-cancel", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_copy_selection_and_cancel + }, + { .command = "cursor-down", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_cursor_down + }, + { .command = "cursor-down-and-cancel", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_cursor_down_and_cancel + }, + { .command = "cursor-left", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_cursor_left + }, + { .command = "cursor-right", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_cursor_right + }, + { .command = "cursor-up", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_cursor_up + }, + { .command = "end-of-line", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_end_of_line + }, + { .command = "goto-line", + .minargs = 1, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_goto_line + }, + { .command = "halfpage-down", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_halfpage_down + }, + { .command = "halfpage-down-and-cancel", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_halfpage_down_and_cancel + }, + { .command = "halfpage-up", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_halfpage_up + }, + { .command = "history-bottom", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_history_bottom + }, + { .command = "history-top", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_history_top + }, + { .command = "jump-again", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_jump_again + }, + { .command = "jump-backward", + .minargs = 1, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_jump_backward + }, + { .command = "jump-forward", + .minargs = 1, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_jump_forward + }, + { .command = "jump-reverse", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_jump_reverse + }, + { .command = "jump-to-backward", + .minargs = 1, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_jump_to_backward + }, + { .command = "jump-to-forward", + .minargs = 1, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_jump_to_forward + }, + { .command = "jump-to-mark", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_jump_to_mark + }, + { .command = "middle-line", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_middle_line + }, + { .command = "next-matching-bracket", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_next_matching_bracket + }, + { .command = "next-paragraph", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_next_paragraph + }, + { .command = "next-space", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_next_space + }, + { .command = "next-space-end", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_next_space_end + }, + { .command = "next-word", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_next_word + }, + { .command = "next-word-end", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_next_word_end + }, + { .command = "other-end", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_other_end + }, + { .command = "page-down", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_page_down + }, + { .command = "page-down-and-cancel", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_page_down_and_cancel + }, + { .command = "page-up", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_page_up + }, + { .command = "pipe-no-clear", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_NEVER, + .f = window_copy_cmd_pipe_no_clear + }, + { .command = "pipe", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_pipe + }, + { .command = "pipe-and-cancel", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_pipe_and_cancel + }, + { .command = "previous-matching-bracket", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_previous_matching_bracket + }, + { .command = "previous-paragraph", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_previous_paragraph + }, + { .command = "previous-space", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_previous_space + }, + { .command = "previous-word", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_previous_word + }, + { .command = "rectangle-on", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_rectangle_on + }, + { .command = "rectangle-off", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_rectangle_off + }, + { .command = "rectangle-toggle", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_rectangle_toggle + }, + { .command = "refresh-from-pane", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_refresh_from_pane + }, + { .command = "scroll-down", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_scroll_down + }, + { .command = "scroll-down-and-cancel", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_scroll_down_and_cancel + }, + { .command = "scroll-up", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_scroll_up + }, + { .command = "search-again", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_search_again + }, + { .command = "search-backward", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_search_backward + }, + { .command = "search-backward-text", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_search_backward_text + }, + { .command = "search-backward-incremental", + .minargs = 1, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_search_backward_incremental + }, + { .command = "search-forward", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_search_forward + }, + { .command = "search-forward-text", + .minargs = 0, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_search_forward_text + }, + { .command = "search-forward-incremental", + .minargs = 1, + .maxargs = 1, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_search_forward_incremental + }, + { .command = "search-reverse", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_search_reverse + }, + { .command = "select-line", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_select_line + }, + { .command = "select-word", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_select_word + }, + { .command = "set-mark", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_set_mark + }, + { .command = "start-of-line", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_start_of_line + }, + { .command = "stop-selection", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_stop_selection + }, + { .command = "top-line", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, + .f = window_copy_cmd_top_line + } }; static void From 7eea3d7ab850bb8fbeeccbb4b0fe84b9274965af Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 11 Aug 2021 20:49:55 +0000 Subject: [PATCH 0892/1006] Break the colour palette into a struct rather than just a single array and use that to support the OSC palette-setting sequences in popups. Also add a pane-colours array option to specify the defaults. GitHub issue 2815. --- cmd-send-keys.c | 3 +- colour.c | 112 ++++++++++++++++++++++++++++++++ input.c | 165 +++++++++++++++++++++++------------------------- options-table.c | 9 +++ options.c | 21 +++++- popup.c | 19 ++++-- screen-redraw.c | 18 +++--- screen-write.c | 40 +++++++----- tmux.1 | 14 +++- tmux.h | 46 ++++++++------ tty.c | 74 ++++++++++------------ window.c | 70 ++++---------------- 12 files changed, 352 insertions(+), 239 deletions(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index b362fab5..c51d413b 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -197,8 +197,9 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'R')) { - window_pane_reset_palette(wp); + colour_palette_clear(&wp->palette); input_reset(wp->ictx, 1); + wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW); } for (; np != 0; np--) { diff --git a/colour.c b/colour.c index 1d1729ca..7aa7620a 100644 --- a/colour.c +++ b/colour.c @@ -944,3 +944,115 @@ colour_byname(const char *name) } return (-1); } + +/* Initialize palette. */ +void +colour_palette_init(struct colour_palette *p) +{ + p->fg = 8; + p->bg = 8; + p->palette = NULL; + p->default_palette = NULL; +} + +/* Clear palette. */ +void +colour_palette_clear(struct colour_palette *p) +{ + p->fg = 8; + p->bg = 8; + if (p != NULL) { + free(p->palette); + p->palette = NULL; + } +} + +/* Free a palette. */ +void +colour_palette_free(struct colour_palette *p) +{ + if (p != NULL) { + free(p->palette); + p->palette = NULL; + free(p->default_palette); + p->default_palette = NULL; + } +} + +/* Get a colour from a palette. */ +int +colour_palette_get(struct colour_palette *p, int c) +{ + if (p == NULL) + return (-1); + + if (c >= 90 && c <= 97) + c = 8 + c - 90; + else if (c & COLOUR_FLAG_256) + c &= ~COLOUR_FLAG_256; + else if (c >= 8) + return (-1); + + if (p->palette != NULL && p->palette[c] != -1) + return (p->palette[c]); + if (p->default_palette != NULL && p->default_palette[c] != -1) + return (p->default_palette[c]); + return (-1); +} + +/* Set a colour in a palette. */ +int +colour_palette_set(struct colour_palette *p, int n, int c) +{ + u_int i; + + if (p == NULL || n > 255) + return (0); + + if (c == -1 && p->palette == NULL) + return (0); + + if (c != -1 && p->palette == NULL) { + if (p->palette == NULL) + p->palette = xcalloc(256, sizeof *p->palette); + for (i = 0; i < 256; i++) + p->palette[i] = -1; + } + p->palette[n] = c; + return (1); +} + +/* Build palette defaults from an option. */ +void +colour_palette_from_option(struct colour_palette *p, struct options *oo) +{ + struct options_entry *o; + struct options_array_item *a; + u_int i, n; + int c; + + if (p == NULL) + return; + + o = options_get(oo, "pane-colours"); + if ((a = options_array_first(o)) == NULL) { + if (p->default_palette != NULL) { + free(p->default_palette); + p->default_palette = NULL; + } + return; + } + if (p->default_palette == NULL) + p->default_palette = xcalloc(256, sizeof *p->default_palette); + for (i = 0; i < 256; i++) + p->default_palette[i] = -1; + while (a != NULL) { + n = options_array_item_index(a); + if (n < 256) { + c = options_array_item_value(a)->number; + p->default_palette[n] = c; + } + a = options_array_next(a); + } + +} diff --git a/input.c b/input.c index b599f27d..49ed68b7 100644 --- a/input.c +++ b/input.c @@ -77,6 +77,7 @@ struct input_ctx { struct window_pane *wp; struct bufferevent *event; struct screen_write_ctx ctx; + struct colour_palette *palette; struct input_cell cell; @@ -797,13 +798,15 @@ input_restore_state(struct input_ctx *ictx) /* Initialise input parser. */ struct input_ctx * -input_init(struct window_pane *wp, struct bufferevent *bev) +input_init(struct window_pane *wp, struct bufferevent *bev, + struct colour_palette *palette) { struct input_ctx *ictx; ictx = xcalloc(1, sizeof *ictx); ictx->wp = wp; ictx->event = bev; + ictx->palette = palette; ictx->input_space = INPUT_BUF_START; ictx->input_buf = xmalloc(INPUT_BUF_START); @@ -1252,7 +1255,6 @@ static int input_esc_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; - struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; struct input_table_entry *entry; @@ -1269,10 +1271,10 @@ input_esc_dispatch(struct input_ctx *ictx) switch (entry->type) { case INPUT_ESC_RIS: - if (wp != NULL) - window_pane_reset_palette(wp); + colour_palette_clear(ictx->palette); input_reset_cell(ictx); screen_write_reset(sctx); + screen_write_fullredraw(sctx); break; case INPUT_ESC_IND: screen_write_linefeed(sctx, 0, ictx->cell.cell.bg); @@ -1874,11 +1876,11 @@ input_csi_dispatch_winops(struct input_ctx *ictx) case 0: 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); - } + if (wp == NULL) + break; + notify_pane("pane-title-changed", wp); + server_redraw_window_borders(wp->window); + server_status_window(wp->window); break; } break; @@ -2498,37 +2500,35 @@ input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c) 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; - int c; - - if (wp == NULL) - return; + char *copy, *s, *next = NULL; + long idx; + int c, bad = 0, redraw = 0; copy = s = xstrdup(p); while (s != NULL && *s != '\0') { idx = strtol(s, &next, 10); - if (*next++ != ';') - goto bad; - if (idx < 0 || idx >= 0x100) - goto bad; + if (*next++ != ';') { + bad = 1; + break; + } + if (idx < 0 || idx >= 256) { + bad = 1; + break; + } s = strsep(&next, ";"); if ((c = input_osc_parse_colour(s)) == -1) { s = next; continue; } - - window_pane_set_palette(wp, idx, c); + if (colour_palette_set(ictx->palette, idx, c)) + redraw = 1; s = next; } - - free(copy); - return; - -bad: - log_debug("bad OSC 4: %s", p); + if (bad) + log_debug("bad OSC 4: %s", p); + if (redraw) + screen_write_fullredraw(&ictx->ctx); free(copy); } @@ -2540,24 +2540,22 @@ input_osc_10(struct input_ctx *ictx, const char *p) struct grid_cell defaults; int c; - if (wp == NULL) - return; - if (strcmp(p, "?") == 0) { - tty_default_colours(&defaults, wp); - input_osc_colour_reply(ictx, 10, defaults.fg); + if (wp != NULL) { + tty_default_colours(&defaults, wp); + input_osc_colour_reply(ictx, 10, defaults.fg); + } return; } - if ((c = input_osc_parse_colour(p)) == -1) - goto bad; - wp->fg = c; - wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); - - return; - -bad: - log_debug("bad OSC 10: %s", p); + if ((c = input_osc_parse_colour(p)) == -1) { + log_debug("bad OSC 10: %s", p); + return; + } + ictx->palette->fg = c; + if (wp != NULL) + wp->flags |= PANE_STYLECHANGED; + screen_write_fullredraw(&ictx->ctx); } /* Handle the OSC 110 sequence for resetting background colour. */ @@ -2566,14 +2564,12 @@ input_osc_110(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; - if (wp == NULL) - return; - if (*p != '\0') return; - - wp->fg = 8; - wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); + ictx->palette->fg = 8; + if (wp != NULL) + wp->flags |= PANE_STYLECHANGED; + screen_write_fullredraw(&ictx->ctx); } /* Handle the OSC 11 sequence for setting and querying background colour. */ @@ -2584,24 +2580,22 @@ input_osc_11(struct input_ctx *ictx, const char *p) struct grid_cell defaults; int c; - if (wp == NULL) - return; - if (strcmp(p, "?") == 0) { - tty_default_colours(&defaults, wp); - input_osc_colour_reply(ictx, 11, defaults.bg); + if (wp != NULL) { + tty_default_colours(&defaults, wp); + input_osc_colour_reply(ictx, 11, defaults.bg); + } return; } - if ((c = input_osc_parse_colour(p)) == -1) - goto bad; - wp->bg = c; - wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); - - return; - -bad: - log_debug("bad OSC 11: %s", p); + if ((c = input_osc_parse_colour(p)) == -1) { + log_debug("bad OSC 11: %s", p); + return; + } + ictx->palette->bg = c; + if (wp != NULL) + wp->flags |= PANE_STYLECHANGED; + screen_write_fullredraw(&ictx->ctx); } /* Handle the OSC 111 sequence for resetting background colour. */ @@ -2610,14 +2604,12 @@ input_osc_111(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; - if (wp == NULL) - return; - if (*p != '\0') return; - - wp->bg = 8; - wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); + ictx->palette->bg = 8; + if (wp != NULL) + wp->flags |= PANE_STYLECHANGED; + screen_write_fullredraw(&ictx->ctx); } /* Handle the OSC 52 sequence for setting the clipboard. */ @@ -2692,34 +2684,35 @@ input_osc_52(struct input_ctx *ictx, const char *p) static void input_osc_104(struct input_ctx *ictx, const char *p) { - struct window_pane *wp = ictx->wp; - char *copy, *s; - long idx; - - if (wp == NULL) - return; + char *copy, *s; + long idx; + int bad = 0, redraw = 0; if (*p == '\0') { - window_pane_reset_palette(wp); + colour_palette_clear(ictx->palette); + screen_write_fullredraw(&ictx->ctx); return; } copy = s = xstrdup(p); while (*s != '\0') { idx = strtol(s, &s, 10); - if (*s != '\0' && *s != ';') - goto bad; - if (idx < 0 || idx >= 0x100) - goto bad; - - window_pane_unset_palette(wp, idx); + if (*s != '\0' && *s != ';') { + bad = 1; + break; + } + if (idx < 0 || idx >= 256) { + bad = 1; + break; + } + if (colour_palette_set(ictx->palette, idx, -1)) + redraw = 1; if (*s == ';') s++; } - free(copy); - return; - -bad: - log_debug("bad OSC 104: %s", p); + if (bad) + log_debug("bad OSC 104: %s", p); + if (redraw) + screen_write_fullredraw(&ictx->ctx); free(copy); } diff --git a/options-table.c b/options-table.c index e0ae993a..2a6f262a 100644 --- a/options-table.c +++ b/options-table.c @@ -185,6 +185,7 @@ 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" }, + { "pane-colors", "pane-colours" }, { NULL, NULL } }; @@ -973,6 +974,14 @@ const struct options_table_entry options_table[] = { .text = "Style of the pane status lines." }, + { .name = "pane-colours", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_str = "", + .flags = OPTIONS_TABLE_IS_ARRAY, + .text = "The default colour palette for colours zero to 255." + }, + { .name = "remain-on-exit", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/options.c b/options.c index 52f4f67e..23c83c07 100644 --- a/options.c +++ b/options.c @@ -402,7 +402,7 @@ options_array_clear(struct options_entry *o) return; RB_FOREACH_SAFE(a, options_array, &o->value.array, a1) - options_array_free(o, a); + options_array_free(o, a); } union options_value * @@ -425,6 +425,7 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, struct options_array_item *a; char *new; struct cmd_parse_result *pr; + long long number; if (!OPTIONS_IS_ARRAY(o)) { if (cause != NULL) @@ -479,6 +480,20 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, return (0); } + if (o->tableentry->type == OPTIONS_TABLE_COLOUR) { + if ((number = colour_fromstring(value)) == -1) { + xasprintf(cause, "bad colour: %s", value); + return (-1); + } + a = options_array_item(o, idx); + if (a == NULL) + a = options_array_new(o, idx); + else + options_value_free(o, &a->value); + a->value.number = number; + return (0); + } + if (cause != NULL) *cause = xstrdup("wrong array type"); return (-1); @@ -1113,6 +1128,10 @@ options_push_changes(const char *name) RB_FOREACH(wp, window_pane_tree, &all_window_panes) wp->flags |= PANE_STYLECHANGED; } + if (strcmp(name, "pane-colours") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) + colour_palette_from_option(&wp->palette, wp->options); + } if (strcmp(name, "pane-border-status") == 0) { RB_FOREACH(w, windows, &windows) layout_fix_panes(w, NULL); diff --git a/popup.c b/popup.c index dbddc66e..57ce80be 100644 --- a/popup.c +++ b/popup.c @@ -33,6 +33,7 @@ struct popup_data { int flags; struct screen s; + struct colour_palette palette; struct job *job; struct input_ctx *ictx; int status; @@ -101,6 +102,7 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) { struct popup_data *pd = ctx->arg; + ttyctx->palette = &pd->palette; ttyctx->redraw_cb = popup_redraw_cb; ttyctx->set_client_cb = popup_set_client_cb; ttyctx->arg = pd; @@ -136,6 +138,8 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) struct screen s; struct screen_write_ctx ctx; u_int i, px = pd->px, py = pd->py; + struct colour_palette *palette = &pd->palette; + struct grid_cell gc; screen_init(&s, pd->sx, pd->sy, 0); screen_write_start(&ctx, &s); @@ -150,11 +154,13 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) } screen_write_stop(&ctx); + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.fg = pd->palette.fg; + gc.bg = pd->palette.bg; + c->overlay_check = NULL; - for (i = 0; i < pd->sy; i++){ - tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, - &grid_default_cell, NULL); - } + for (i = 0; i < pd->sy; i++) + tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &gc, palette); c->overlay_check = popup_check_cb; } @@ -180,6 +186,7 @@ popup_free_cb(struct client *c) input_free(pd->ictx); screen_free(&pd->s); + colour_palette_free(&pd->palette); free(pd); } @@ -389,6 +396,8 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->status = 128 + SIGHUP; screen_init(&pd->s, sx - 2, sy - 2, 0); + colour_palette_init(&pd->palette); + colour_palette_from_option(&pd->palette, global_w_options); pd->px = px; pd->py = py; @@ -403,7 +412,7 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->job = job_run(shellcmd, argc, argv, s, cwd, popup_job_update_cb, popup_job_complete_cb, NULL, pd, JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, pd->sx - 2, pd->sy - 2); - pd->ictx = input_init(NULL, job_get_event(pd->job)); + pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette); server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd); diff --git a/screen-redraw.c b/screen-redraw.c index 4c4135a8..3df57383 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -800,12 +800,13 @@ screen_redraw_draw_status(struct screen_redraw_ctx *ctx) static void screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) { - struct client *c = ctx->c; - struct window *w = c->session->curw->window; - struct tty *tty = &c->tty; - struct screen *s; - struct grid_cell defaults; - u_int i, j, top, x, y, width; + struct client *c = ctx->c; + struct window *w = c->session->curw->window; + struct tty *tty = &c->tty; + struct screen *s = wp->screen; + struct colour_palette *palette = &wp->palette; + struct grid_cell defaults; + u_int i, j, top, x, y, width; log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id); @@ -815,8 +816,6 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) top = ctx->statuslines; else top = 0; - - s = wp->screen; for (j = 0; j < wp->sy; j++) { if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy) continue; @@ -849,7 +848,6 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) __func__, c->name, wp->id, i, j, x, y, width); tty_default_colours(&defaults, wp); - tty_draw_line(tty, s, i, j, width, x, y, &defaults, - wp->palette); + tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette); } } diff --git a/screen-write.c b/screen-write.c index b04155c3..a11b4771 100644 --- a/screen-write.c +++ b/screen-write.c @@ -171,15 +171,6 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, memset(ttyctx, 0, sizeof *ttyctx); - if (ctx->wp != NULL) { - tty_default_colours(&ttyctx->defaults, ctx->wp); - ttyctx->palette = ctx->wp->palette; - } else { - memcpy(&ttyctx->defaults, &grid_default_cell, - sizeof ttyctx->defaults); - ttyctx->palette = NULL; - } - ttyctx->s = s; ttyctx->sx = screen_size_x(s); ttyctx->sy = screen_size_y(s); @@ -189,15 +180,21 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, ttyctx->orlower = s->rlower; ttyctx->orupper = s->rupper; - if (ctx->init_ctx_cb != NULL) + memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults); + if (ctx->init_ctx_cb != NULL) { ctx->init_ctx_cb(ctx, ttyctx); - else { + if (ttyctx->palette != NULL) { + ttyctx->defaults.fg = ttyctx->palette->fg; + ttyctx->defaults.bg = ttyctx->palette->bg; + } + } else { ttyctx->redraw_cb = screen_write_redraw_cb; - if (ctx->wp == NULL) - ttyctx->set_client_cb = NULL; - else + if (ctx->wp != NULL) { + tty_default_colours(&ttyctx->defaults, ctx->wp); + ttyctx->palette = &ctx->wp->palette; ttyctx->set_client_cb = screen_write_set_client_cb; - ttyctx->arg = ctx->wp; + ttyctx->arg = ctx->wp; + } } if (ctx->wp != NULL && @@ -680,6 +677,7 @@ screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny) memcpy(&gc, &grid_default_cell, sizeof gc); gc.attr |= GRID_ATTR_CHARSET; + gc.flags |= GRID_FLAG_NOPALETTE; screen_write_putc(ctx, &gc, 'l'); for (i = 1; i < nx - 1; i++) @@ -1423,6 +1421,18 @@ screen_write_clearhistory(struct screen_write_ctx *ctx) grid_clear_history(ctx->s->grid); } +/* Force a full redraw. */ +void +screen_write_fullredraw(struct screen_write_ctx *ctx) +{ + struct tty_ctx ttyctx; + + screen_write_collect_flush(ctx, 0, __func__); + + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.redraw_cb(&ttyctx); +} + /* Trim collected items. */ static struct screen_write_citem * screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x, diff --git a/tmux.1 b/tmux.1 index 95f1ba41..a22de76c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1677,9 +1677,13 @@ The following commands are supported in copy mode: .It Li "cancel" Ta "q" Ta "Escape" .It Li "clear-selection" Ta "Escape" Ta "C-g" .It Li "copy-end-of-line []" Ta "" Ta "" -.It Li "copy-end-of-line-and-cancel []" Ta "D" Ta "C-k" +.It Li "copy-end-of-line-and-cancel []" Ta "" Ta "" +.It Li "copy-pipe-end-of-line [] []" Ta "" Ta "" +.It Li "copy-pipe-end-of-line-and-cancel [] []" Ta "D" Ta "C-k" .It Li "copy-line []" Ta "" Ta "" .It Li "copy-line-and-cancel []" Ta "" Ta "" +.It Li "copy-pipe-line [] []" Ta "" Ta "" +.It Li "copy-pipe-line-and-cancel [] []" Ta "" Ta "" .It Li "copy-pipe [] []" Ta "" Ta "" .It Li "copy-pipe-no-clear [] []" Ta "" Ta "" .It Li "copy-pipe-and-cancel [] []" Ta "" Ta "" @@ -4143,7 +4147,6 @@ see the .Sx STYLES section. Attributes are ignored. -.Pp .It Ic pane-base-index Ar index Like .Ic base-index , @@ -4301,6 +4304,13 @@ The alternate screen feature preserves the contents of the window when an interactive application starts and restores it on exit, so that any output visible before the application starts reappears unchanged after it exits. .Pp +.It Ic pane-colours[] Ar colour +The default colour palette. +Each entry in the array defines the colour +.Nm +uses when the colour with that index is requested. +The index may be from zero to 255. +.Pp .It Xo Ic remain-on-exit .Op Ic on | off | failed .Xc diff --git a/tmux.h b/tmux.h index 42f03bd6..a3f7fdd9 100644 --- a/tmux.h +++ b/tmux.h @@ -652,6 +652,15 @@ enum utf8_state { /* Special colours. */ #define COLOUR_DEFAULT(c) ((c) == 8 || (c) == 9) +/* Replacement palette. */ +struct colour_palette { + int fg; + int bg; + + int *palette; + int *default_palette; +}; + /* Grid attributes. Anything above 0xff is stored in an extended cell. */ #define GRID_ATTR_BRIGHT 0x1 #define GRID_ATTR_DIM 0x2 @@ -989,9 +998,6 @@ struct window_pane { u_int xoff; u_int yoff; - int fg; - int bg; - int flags; #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 @@ -1029,7 +1035,7 @@ struct window_pane { struct grid_cell cached_gc; struct grid_cell cached_active_gc; - int *palette; + struct colour_palette palette; int pipe_fd; struct bufferevent *pipe_event; @@ -1427,7 +1433,7 @@ struct tty_ctx { /* The default colours and palette. */ struct grid_cell defaults; - int *palette; + struct colour_palette *palette; /* Containing region (usually window) offset and size. */ int bigger; @@ -1827,11 +1833,11 @@ RB_HEAD(key_tables, key_table); /* Option data. */ RB_HEAD(options_array, options_array_item); union options_value { - char *string; - long long number; - struct style style; - struct options_array array; - struct cmd_list *cmdlist; + char *string; + long long number; + struct style style; + struct options_array array; + struct cmd_list *cmdlist; }; /* Option table entries. */ @@ -2165,7 +2171,7 @@ void tty_update_window_offset(struct window *); void tty_update_client_offset(struct client *); void tty_raw(struct tty *, const char *); void tty_attributes(struct tty *, const struct grid_cell *, - const struct grid_cell *, int *); + const struct grid_cell *, struct colour_palette *); void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); @@ -2181,7 +2187,7 @@ void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); void tty_cell(struct tty *, const struct grid_cell *, - const struct grid_cell *, int *); + const struct grid_cell *, struct colour_palette *); int tty_init(struct tty *, struct client *); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); @@ -2191,7 +2197,7 @@ void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int, - u_int, u_int, const struct grid_cell *, int *); + u_int, u_int, const struct grid_cell *, struct colour_palette *); void tty_sync_start(struct tty *); void tty_sync_end(struct tty *); int tty_open(struct tty *, char **); @@ -2573,7 +2579,8 @@ void recalculate_sizes(void); void recalculate_sizes_now(int); /* input.c */ -struct input_ctx *input_init(struct window_pane *, struct bufferevent *); +struct input_ctx *input_init(struct window_pane *, struct bufferevent *, + struct colour_palette *); void input_free(struct input_ctx *); void input_reset(struct input_ctx *, int); struct evbuffer *input_pending(struct input_ctx *); @@ -2598,6 +2605,12 @@ int colour_fromstring(const char *s); int colour_256toRGB(int); int colour_256to16(int); int colour_byname(const char *); +void colour_palette_init(struct colour_palette *); +void colour_palette_clear(struct colour_palette *); +void colour_palette_free(struct colour_palette *); +int colour_palette_get(struct colour_palette *, int); +int colour_palette_set(struct colour_palette *, int, int); +void colour_palette_from_option(struct colour_palette *, struct options *); /* attributes.c */ const char *attributes_tostring(int); @@ -2737,6 +2750,7 @@ void screen_write_clearendofscreen(struct screen_write_ctx *, u_int); void screen_write_clearstartofscreen(struct screen_write_ctx *, u_int); void screen_write_clearscreen(struct screen_write_ctx *, u_int); void screen_write_clearhistory(struct screen_write_ctx *); +void screen_write_fullredraw(struct screen_write_ctx *); void screen_write_collect_end(struct screen_write_ctx *); void screen_write_collect_add(struct screen_write_ctx *, const struct grid_cell *); @@ -2834,10 +2848,6 @@ struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); int window_pane_destroy_ready(struct window_pane *); void window_pane_resize(struct window_pane *, u_int, u_int); -void window_pane_set_palette(struct window_pane *, u_int, int); -void window_pane_unset_palette(struct window_pane *, u_int); -void window_pane_reset_palette(struct window_pane *); -int window_pane_get_palette(struct window_pane *, int); int window_pane_set_mode(struct window_pane *, struct window_pane *, const struct window_mode *, struct cmd_find_state *, struct args *); diff --git a/tty.c b/tty.c index 61b38eec..6cebe051 100644 --- a/tty.c +++ b/tty.c @@ -45,9 +45,12 @@ static void tty_cursor_pane_unless_wrap(struct tty *, const struct tty_ctx *, u_int, u_int); static void tty_invalidate(struct tty *); static void tty_colours(struct tty *, const struct grid_cell *); -static void tty_check_fg(struct tty *, int *, struct grid_cell *); -static void tty_check_bg(struct tty *, int *, struct grid_cell *); -static void tty_check_us(struct tty *, int *, struct grid_cell *); +static void tty_check_fg(struct tty *, struct colour_palette *, + struct grid_cell *); +static void tty_check_bg(struct tty *, struct colour_palette *, + struct grid_cell *); +static void tty_check_us(struct tty *, struct colour_palette *, + struct grid_cell *); static void tty_colours_fg(struct tty *, const struct grid_cell *); static void tty_colours_bg(struct tty *, const struct grid_cell *); static void tty_colours_us(struct tty *, const struct grid_cell *); @@ -66,7 +69,7 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code, static void tty_repeat_space(struct tty *, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); static void tty_default_attributes(struct tty *, const struct grid_cell *, - int *, u_int); + struct colour_palette *, u_int); static int tty_check_overlay(struct tty *, u_int, u_int); #define tty_use_margin(tty) \ @@ -939,27 +942,6 @@ tty_update_client_offset(struct client *c) c->flags |= (CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS); } -/* Get a palette entry. */ -static int -tty_get_palette(int *palette, int c) -{ - int new; - - if (palette == NULL) - return (-1); - - new = -1; - if (c < 8) - new = palette[c]; - else if (c >= 90 && c <= 97) - new = palette[8 + c - 90]; - else if (c & COLOUR_FLAG_256) - new = palette[c & ~COLOUR_FLAG_256]; - if (new == 0) - return (-1); - return (new); -} - /* * Is the region large enough to be worth redrawing once later rather than * probably several times now? Currently yes if it is more than 50% of the @@ -1341,7 +1323,8 @@ tty_check_overlay(struct tty *tty, u_int px, u_int py) void tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, - u_int atx, u_int aty, const struct grid_cell *defaults, int *palette) + u_int atx, u_int aty, const struct grid_cell *defaults, + struct colour_palette *palette) { struct grid *gd = s->grid; struct grid_cell gc, last; @@ -1356,6 +1339,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__, px, py, nx, atx, aty); + log_debug("%s: defaults: fg=%d, bg=%d", __func__, defaults->fg, + defaults->bg); /* * py is the line in the screen to draw. @@ -2061,7 +2046,7 @@ tty_cmd_syncstart(struct tty *tty, __unused const struct tty_ctx *ctx) void tty_cell(struct tty *tty, const struct grid_cell *gc, - const struct grid_cell *defaults, int *palette) + const struct grid_cell *defaults, struct colour_palette *palette) { const struct grid_cell *gcp; @@ -2381,17 +2366,19 @@ out: void tty_attributes(struct tty *tty, const struct grid_cell *gc, - const struct grid_cell *defaults, int *palette) + const struct grid_cell *defaults, struct colour_palette *palette) { struct grid_cell *tc = &tty->cell, gc2; int changed; /* Copy cell and update default colours. */ memcpy(&gc2, gc, sizeof gc2); - if (gc2.fg == 8) - gc2.fg = defaults->fg; - if (gc2.bg == 8) - gc2.bg = defaults->bg; + if (~gc->flags & GRID_FLAG_NOPALETTE) { + if (gc2.fg == 8) + gc2.fg = defaults->fg; + if (gc2.bg == 8) + gc2.bg = defaults->bg; + } /* Ignore cell if it is the same as the last one. */ if (gc2.attr == tty->last_cell.attr && @@ -2533,13 +2520,14 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg) tty_colours_bg(tty, gc); - /* Set the underscore color. */ + /* Set the underscore colour. */ if (gc->us != tc->us) tty_colours_us(tty, gc); } static void -tty_check_fg(struct tty *tty, int *palette, struct grid_cell *gc) +tty_check_fg(struct tty *tty, struct colour_palette *palette, + struct grid_cell *gc) { u_char r, g, b; u_int colours; @@ -2554,7 +2542,7 @@ tty_check_fg(struct tty *tty, int *palette, struct grid_cell *gc) c = gc->fg; if (c < 8 && gc->attr & GRID_ATTR_BRIGHT) c += 90; - if ((c = tty_get_palette(palette, c)) != -1) + if ((c = colour_palette_get(palette, c)) != -1) gc->fg = c; } @@ -2595,7 +2583,8 @@ tty_check_fg(struct tty *tty, int *palette, struct grid_cell *gc) } static void -tty_check_bg(struct tty *tty, int *palette, struct grid_cell *gc) +tty_check_bg(struct tty *tty, struct colour_palette *palette, + struct grid_cell *gc) { u_char r, g, b; u_int colours; @@ -2603,7 +2592,7 @@ tty_check_bg(struct tty *tty, int *palette, struct grid_cell *gc) /* Perform substitution if this pane has a palette. */ if (~gc->flags & GRID_FLAG_NOPALETTE) { - if ((c = tty_get_palette(palette, gc->bg)) != -1) + if ((c = colour_palette_get(palette, gc->bg)) != -1) gc->bg = c; } @@ -2646,13 +2635,14 @@ tty_check_bg(struct tty *tty, int *palette, struct grid_cell *gc) } static void -tty_check_us(__unused struct tty *tty, int *palette, struct grid_cell *gc) +tty_check_us(__unused struct tty *tty, struct colour_palette *palette, + struct grid_cell *gc) { int c; /* Perform substitution if this pane has a palette. */ if (~gc->flags & GRID_FLAG_NOPALETTE) { - if ((c = tty_get_palette(palette, gc->us)) != -1) + if ((c = colour_palette_get(palette, gc->us)) != -1) gc->us = c; } @@ -2793,8 +2783,8 @@ static void tty_window_default_style(struct grid_cell *gc, struct window_pane *wp) { memcpy(gc, &grid_default_cell, sizeof *gc); - gc->fg = wp->fg; - gc->bg = wp->bg; + gc->fg = wp->palette.fg; + gc->bg = wp->palette.bg; } void @@ -2831,7 +2821,7 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) static void tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, - int *palette, u_int bg) + struct colour_palette *palette, u_int bg) { struct grid_cell gc; diff --git a/window.c b/window.c index aa448e1f..f94acfcb 100644 --- a/window.c +++ b/window.c @@ -478,6 +478,14 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) return (1); } +static int +window_pane_get_palette(struct window_pane *wp, int c) +{ + if (wp == NULL) + return (-1); + return (colour_palette_get(&wp->palette, c)); +} + void window_redraw_active_switch(struct window *w, struct window_pane *wp) { @@ -855,9 +863,6 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->fd = -1; - wp->fg = 8; - wp->bg = 8; - TAILQ_INIT(&wp->modes); TAILQ_INIT (&wp->resize_queue); @@ -867,6 +872,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->pipe_fd = -1; + colour_palette_init(&wp->palette); screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; @@ -916,7 +922,7 @@ window_pane_destroy(struct window_pane *wp) free((void *)wp->cwd); free(wp->shell); cmd_free_argv(wp->argc, wp->argv); - free(wp->palette); + colour_palette_free(&wp->palette); free(wp); } @@ -968,7 +974,7 @@ window_pane_set_event(struct window_pane *wp) wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, window_pane_error_callback, wp); - wp->ictx = input_init(wp, wp->event); + wp->ictx = input_init(wp, wp->event, &wp->palette); bufferevent_enable(wp->event, EV_READ|EV_WRITE); } @@ -1000,60 +1006,6 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wme->mode->resize(wme, sx, sy); } -void -window_pane_set_palette(struct window_pane *wp, u_int n, int colour) -{ - if (n > 0xff) - return; - - if (wp->palette == NULL) - wp->palette = xcalloc(0x100, sizeof *wp->palette); - - wp->palette[n] = colour; - wp->flags |= PANE_REDRAW; -} - -void -window_pane_unset_palette(struct window_pane *wp, u_int n) -{ - if (n > 0xff || wp->palette == NULL) - return; - - wp->palette[n] = 0; - wp->flags |= PANE_REDRAW; -} - -void -window_pane_reset_palette(struct window_pane *wp) -{ - if (wp->palette == NULL) - return; - - free(wp->palette); - wp->palette = NULL; - wp->flags |= PANE_REDRAW; -} - -int -window_pane_get_palette(struct window_pane *wp, int c) -{ - int new; - - if (wp == NULL || wp->palette == NULL) - return (-1); - - new = -1; - if (c < 8) - new = wp->palette[c]; - else if (c >= 90 && c <= 97) - new = wp->palette[8 + c - 90]; - else if (c & COLOUR_FLAG_256) - new = wp->palette[c & ~COLOUR_FLAG_256]; - if (new == 0) - return (-1); - return (new); -} - int window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, const struct window_mode *mode, struct cmd_find_state *fs, From 163908fe8a0af8cf2d24510415bd4f81ace3a4d1 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Aug 2021 08:05:11 +0000 Subject: [PATCH 0893/1006] Move hook format setup earlier and add a hook_client, GitHub issue 2809. --- cmd-queue.c | 9 +++++++++ format.c | 22 ++++++++++++++++++++++ notify.c | 49 ++++++++++++++++++++++++------------------------- tmux.1 | 1 + tmux.h | 2 ++ 5 files changed, 58 insertions(+), 25 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 54163919..6dd5f9dd 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -269,6 +269,15 @@ cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...) free(value); } +/* Add formats to command queue. */ +void +cmdq_add_formats(struct cmdq_state *state, struct format_tree *ft) +{ + if (state->formats == NULL) + state->formats = format_create(NULL, NULL, FORMAT_NONE, 0); + format_merge(state->formats, ft); +} + /* Merge formats from item. */ void cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft) diff --git a/format.c b/format.c index 479b97c4..b5f09649 100644 --- a/format.c +++ b/format.c @@ -929,6 +929,9 @@ format_cb_pane_fg(struct format_tree *ft) struct window_pane *wp = ft->wp; struct grid_cell gc; + if (wp == NULL) + return (NULL); + tty_default_colours(&gc, wp); return (xstrdup(colour_tostring(gc.fg))); } @@ -940,6 +943,9 @@ format_cb_pane_bg(struct format_tree *ft) struct window_pane *wp = ft->wp; struct grid_cell gc; + if (wp == NULL) + return (NULL); + tty_default_colours(&gc, wp); return (xstrdup(colour_tostring(gc.bg))); } @@ -3079,6 +3085,22 @@ format_free(struct format_tree *ft) free(ft); } +/* Log each format. */ +static void +format_log_debug_cb(const char *key, const char *value, void *arg) +{ + const char *prefix = arg; + + log_debug("%s: %s=%s", prefix, key, value); +} + +/* Log a format tree. */ +void +format_log_debug(struct format_tree *ft, const char *prefix) +{ + format_each(ft, format_log_debug_cb, prefix); +} + /* Walk each format. */ void format_each(struct format_tree *ft, void (*cb)(const char *, const char *, diff --git a/notify.c b/notify.c index c645657f..8b2610c2 100644 --- a/notify.c +++ b/notify.c @@ -26,31 +26,15 @@ struct notify_entry { const char *name; + struct cmd_find_state fs; + struct format_tree *formats; struct client *client; struct session *session; struct window *window; int pane; - - struct cmd_find_state fs; }; -static void -notify_hook_formats(struct cmdq_state *state, struct session *s, - struct window *w, int pane) -{ - if (s != NULL) { - cmdq_add_format(state, "hook_session", "$%u", s->id); - cmdq_add_format(state, "hook_session_name", "%s", s->name); - } - if (w != NULL) { - cmdq_add_format(state, "hook_window", "@%u", w->id); - cmdq_add_format(state, "hook_window_name", "%s", w->name); - } - if (pane != -1) - cmdq_add_format(state, "hook_pane", "%%%d", pane); -} - static void notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) { @@ -58,8 +42,6 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) struct options *oo; struct cmdq_item *new_item; struct cmdq_state *new_state; - struct session *s = ne->session; - struct window *w = ne->window; struct options_entry *o; struct options_array_item *a; struct cmd_list *cmdlist; @@ -89,8 +71,7 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) return; new_state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); - cmdq_add_format(new_state, "hook", "%s", ne->name); - notify_hook_formats(new_state, s, w, ne->pane); + cmdq_add_formats(new_state, ne->formats); a = options_array_first(o); while (a != NULL) { @@ -149,6 +130,7 @@ notify_callback(struct cmdq_item *item, void *data) if (ne->fs.s != NULL) session_remove_ref(ne->fs.s, __func__); + format_free(ne->formats); free((void *)ne->name); free(ne); @@ -172,11 +154,23 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c, ne->client = c; ne->session = s; ne->window = w; + ne->pane = (wp != NULL ? wp->id : -1); + ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); + format_add(ne->formats, "hook", "%s", name); + if (c != NULL) + format_add(ne->formats, "hook_client", "%s", c->name); + if (s != NULL) { + format_add(ne->formats, "hook_session", "$%u", s->id); + format_add(ne->formats, "hook_session_name", "%s", s->name); + } + if (w != NULL) { + format_add(ne->formats, "hook_window", "@%u", w->id); + format_add(ne->formats, "hook_window_name", "%s", w->name); + } if (wp != NULL) - ne->pane = wp->id; - else - ne->pane = -1; + format_add(ne->formats, "hook_pane", "%%%d", wp->id); + format_log_debug(ne->formats, __func__); if (c != NULL) c->references++; @@ -208,7 +202,12 @@ notify_hook(struct cmdq_item *item, const char *name) ne.window = target->w; ne.pane = target->wp->id; + ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); + format_add(ne.formats, "hook", "%s", name); + format_log_debug(ne.formats, __func__); + notify_insert_hook(item, &ne); + format_free(ne.formats); } void diff --git a/tmux.1 b/tmux.1 index a22de76c..f6f50096 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4925,6 +4925,7 @@ The following variables are available, where appropriate: .It Li "history_limit" Ta "" Ta "Maximum window history lines" .It Li "history_size" Ta "" Ta "Size of history in lines" .It Li "hook" Ta "" Ta "Name of running hook, if any" +.It Li "hook_client" Ta "" Ta "Name of client where hook was run, if any" .It Li "hook_pane" Ta "" Ta "ID of pane where hook was run, if any" .It Li "hook_session" Ta "" Ta "ID of session where hook was run, if any" .It Li "hook_session_name" Ta "" Ta "Name of session where hook was run, if any" diff --git a/tmux.h b/tmux.h index a3f7fdd9..3ac272f8 100644 --- a/tmux.h +++ b/tmux.h @@ -2023,6 +2023,7 @@ void printflike(3, 4) format_add(struct format_tree *, const char *, void format_add_tv(struct format_tree *, const char *, struct timeval *); void format_add_cb(struct format_tree *, const char *, format_cb); +void format_log_debug(struct format_tree *, const char *); void format_each(struct format_tree *, void (*)(const char *, const char *, void *), void *); char *format_expand_time(struct format_tree *, const char *); @@ -2382,6 +2383,7 @@ struct cmdq_state *cmdq_copy_state(struct cmdq_state *); void cmdq_free_state(struct cmdq_state *); void printflike(3, 4) cmdq_add_format(struct cmdq_state *, const char *, const char *, ...); +void cmdq_add_formats(struct cmdq_state *, struct format_tree *); void cmdq_merge_formats(struct cmdq_item *, struct format_tree *); struct cmdq_list *cmdq_new(void); void cmdq_free(struct cmdq_list *); From 26773ea9efd3b555833618719446309cf7a024de Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Aug 2021 08:10:20 +0000 Subject: [PATCH 0894/1006] Do not dereference pane when it is NULL, fixes a crash when creating a hook from the config, GitHub issue 2820. --- format.c | 2 +- notify.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index b5f09649..5a295198 100644 --- a/format.c +++ b/format.c @@ -3098,7 +3098,7 @@ format_log_debug_cb(const char *key, const char *value, void *arg) void format_log_debug(struct format_tree *ft, const char *prefix) { - format_each(ft, format_log_debug_cb, prefix); + format_each(ft, format_log_debug_cb, (void *)prefix); } /* Walk each format. */ diff --git a/notify.c b/notify.c index 8b2610c2..2510a394 100644 --- a/notify.c +++ b/notify.c @@ -200,7 +200,7 @@ notify_hook(struct cmdq_item *item, const char *name) ne.client = cmdq_get_client(item); ne.session = target->s; ne.window = target->w; - ne.pane = target->wp->id; + ne.pane = (target->wp != NULL ? target->wp->id : -1); ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); format_add(ne.formats, "hook", "%s", name); From 5d451551b6457d972897fa60b3dba38e13d6589f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Aug 2021 11:35:53 +0000 Subject: [PATCH 0895/1006] Restore saved cursor position after a ZWJ rather than recalculating it. --- screen-write.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/screen-write.c b/screen-write.c index a11b4771..01f8a097 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1775,13 +1775,13 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) if (width == 0 || (ctx->flags & SCREEN_WRITE_ZWJ)) { ctx->flags &= ~SCREEN_WRITE_ZWJ; screen_write_collect_flush(ctx, 0, __func__); - if ((gc = screen_write_combine(ctx, ud, &xx)) != 0) { + if ((gc = screen_write_combine(ctx, ud, &xx)) != NULL) { cx = s->cx; cy = s->cy; screen_write_set_cursor(ctx, xx, s->cy); screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); - s->cx = xx + gc->data.width; s->cy = cy; + s->cx = cx; s->cy = cy; } return; } From 9b004728202d03241009ef5f73980909e9234bc4 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Aug 2021 19:47:05 +0000 Subject: [PATCH 0896/1006] Evaluate styles with the pane variables. --- cmd-select-pane.c | 2 ++ format.c | 9 ++++----- tty.c | 13 +++++++++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 7871fe05..c5b4ee13 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -145,10 +145,12 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) markedwp = marked_pane.wp; if (lastwp != NULL) { + lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); server_redraw_window_borders(lastwp->window); server_status_window(lastwp->window); } if (markedwp != NULL) { + markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); server_redraw_window_borders(markedwp->window); server_status_window(markedwp->window); } diff --git a/format.c b/format.c index 5a295198..9ef4e6e9 100644 --- a/format.c +++ b/format.c @@ -4300,15 +4300,14 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, if (strcmp(found, condition) == 0) { free(found); found = xstrdup(""); - format_log(es, "condition '%s' found: %s", - condition, found); - } else { format_log(es, "condition '%s' not found; assuming false", condition); } - } else - format_log(es, "condition '%s' found", condition); + } else { + format_log(es, "condition '%s' found: %s", condition, + found); + } if (format_choose(es, cp + 1, &left, &right, 0) != 0) { format_log(es, "condition '%s' syntax error: %s", diff --git a/tty.c b/tty.c index 6cebe051..ecad9249 100644 --- a/tty.c +++ b/tty.c @@ -2790,18 +2790,23 @@ tty_window_default_style(struct grid_cell *gc, struct window_pane *wp) void tty_default_colours(struct grid_cell *gc, struct window_pane *wp) { - struct options *oo = wp->options; + struct options *oo = wp->options; + struct format_tree *ft; memcpy(gc, &grid_default_cell, sizeof *gc); if (wp->flags & PANE_STYLECHANGED) { + log_debug("%%%u: style changed", wp->id); wp->flags &= ~PANE_STYLECHANGED; + ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, + FORMAT_NOJOBS); + format_defaults(ft, NULL, NULL, NULL, wp); tty_window_default_style(&wp->cached_active_gc, wp); - style_add(&wp->cached_active_gc, oo, "window-active-style", - NULL); + style_add(&wp->cached_active_gc, oo, "window-active-style", ft); tty_window_default_style(&wp->cached_gc, wp); - style_add(&wp->cached_gc, oo, "window-style", NULL); + style_add(&wp->cached_gc, oo, "window-style", ft); + format_free(ft); } if (gc->fg == 8) { From 6feb8f6505441583bb9f289da2227954c8335ce4 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Aug 2021 20:09:34 +0000 Subject: [PATCH 0897/1006] Use COLOUR_DEFAULT not hardcoded 8. --- status.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/status.c b/status.c index 7435b31a..853f46d3 100644 --- a/status.c +++ b/status.c @@ -390,10 +390,10 @@ status_redraw(struct client *c) /* Set up default colour. */ style_apply(&gc, s->options, "status-style", ft); fg = options_get_number(s->options, "status-fg"); - if (fg != 8) + if (!COLOUR_DEFAULT(fg)) gc.fg = fg; bg = options_get_number(s->options, "status-bg"); - if (bg != 8) + if (!COLOUR_DEFAULT(bg)) gc.bg = bg; if (!grid_cells_equal(&gc, &sl->style)) { force = 1; From db9195463d454d076328e69543194452da26c691 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Aug 2021 20:44:49 +0000 Subject: [PATCH 0898/1006] Now that styles can contain formats, they need to be expanded when inserted into the status line. --- options-table.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/options-table.c b/options-table.c index 2a6f262a..607a90b6 100644 --- a/options-table.c +++ b/options-table.c @@ -81,7 +81,7 @@ static const char *options_table_extended_keys_list[] = { /* Status line format. */ #define OPTIONS_TABLE_STATUS_FORMAT1 \ - "#[align=left range=left #{status-left-style}]" \ + "#[align=left range=left #{E:status-left-style}]" \ "#[push-default]" \ "#{T;=/#{status-left-length}:status-left}" \ "#[pop-default]" \ @@ -90,20 +90,20 @@ static const char *options_table_extended_keys_list[] = { "#[list=left-marker]<#[list=right-marker]>#[list=on]" \ "#{W:" \ "#[range=window|#{window_index} " \ - "#{window-status-style}" \ + "#{E:window-status-style}" \ "#{?#{&&:#{window_last_flag}," \ - "#{!=:#{window-status-last-style},default}}, " \ - "#{window-status-last-style}," \ + "#{!=:#{E:window-status-last-style},default}}, " \ + "#{E:window-status-last-style}," \ "}" \ "#{?#{&&:#{window_bell_flag}," \ - "#{!=:#{window-status-bell-style},default}}, " \ - "#{window-status-bell-style}," \ + "#{!=:#{E:window-status-bell-style},default}}, " \ + "#{E:window-status-bell-style}," \ "#{?#{&&:#{||:#{window_activity_flag}," \ "#{window_silence_flag}}," \ "#{!=:" \ - "#{window-status-activity-style}," \ + "#{E:window-status-activity-style}," \ "default}}, " \ - "#{window-status-activity-style}," \ + "#{E:window-status-activity-style}," \ "}" \ "}" \ "]" \ @@ -114,23 +114,23 @@ static const char *options_table_extended_keys_list[] = { "#{?window_end_flag,,#{window-status-separator}}" \ "," \ "#[range=window|#{window_index} list=focus " \ - "#{?#{!=:#{window-status-current-style},default}," \ - "#{window-status-current-style}," \ - "#{window-status-style}" \ + "#{?#{!=:#{E:window-status-current-style},default}," \ + "#{E:window-status-current-style}," \ + "#{E:window-status-style}" \ "}" \ "#{?#{&&:#{window_last_flag}," \ - "#{!=:#{window-status-last-style},default}}, " \ - "#{window-status-last-style}," \ + "#{!=:#{E:window-status-last-style},default}}, " \ + "#{E:window-status-last-style}," \ "}" \ "#{?#{&&:#{window_bell_flag}," \ - "#{!=:#{window-status-bell-style},default}}, " \ - "#{window-status-bell-style}," \ + "#{!=:#{E:window-status-bell-style},default}}, " \ + "#{E:window-status-bell-style}," \ "#{?#{&&:#{||:#{window_activity_flag}," \ "#{window_silence_flag}}," \ "#{!=:" \ - "#{window-status-activity-style}," \ + "#{E:window-status-activity-style}," \ "default}}, " \ - "#{window-status-activity-style}," \ + "#{E:window-status-activity-style}," \ "}" \ "}" \ "]" \ @@ -140,7 +140,7 @@ static const char *options_table_extended_keys_list[] = { "#[norange list=on default]" \ "#{?window_end_flag,,#{window-status-separator}}" \ "}" \ - "#[nolist align=right range=right #{status-right-style}]" \ + "#[nolist align=right range=right #{E:status-right-style}]" \ "#[push-default]" \ "#{T;=/#{status-right-length}:status-right}" \ "#[pop-default]" \ From e2f6f58fe50e233dcd0d924bd30c94d1161c666d Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Aug 2021 20:46:30 +0000 Subject: [PATCH 0899/1006] Make newline a style delimiter as well so they can cross multiple lines for readability. --- style.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style.c b/style.c index 24b09882..89a4e63a 100644 --- a/style.c +++ b/style.c @@ -51,7 +51,7 @@ int style_parse(struct style *sy, const struct grid_cell *base, const char *in) { struct style saved; - const char delimiters[] = " ,", *cp; + const char delimiters[] = " ,\n", *cp; char tmp[256], *found; int value; size_t end; From a2b85069171413aa30c812d44bf8ee4d32a2f834 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 06:50:42 +0000 Subject: [PATCH 0900/1006] Set return code for confirm-before and make command-prompt also block, GitHub issue 2822. --- cmd-command-prompt.c | 62 ++++++++++++++++++++++++++++---------------- cmd-confirm-before.c | 15 +++++++---- cmd-if-shell.c | 5 ---- cmd-run-shell.c | 1 - tmux.1 | 10 +++++-- 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index a955ac32..7e3d23c5 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,8 +40,8 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1FkiI:Np:t:T:", 0, 1 }, - .usage = "[-1FkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE + .args = { "1bFkiI:Np:t:T:", 0, 1 }, + .usage = "[-1bFkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [-T type] [template]", .flags = CMD_CLIENT_TFLAG, @@ -49,17 +49,20 @@ const struct cmd_entry cmd_command_prompt_entry = { }; struct cmd_command_prompt_cdata { - int flags; - enum prompt_type prompt_type; + struct cmdq_item *item; + struct cmd_parse_input pi; - char *inputs; - char *next_input; + int flags; + enum prompt_type prompt_type; - char *prompts; - char *next_prompt; + char *inputs; + char *next_input; - char *template; - int idx; + char *prompts; + char *next_prompt; + + char *template; + int idx; }; static enum cmd_retval @@ -72,21 +75,23 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) struct cmd_command_prompt_cdata *cdata; char *prompt, *ptr, *input = NULL; size_t n; + int wait = !args_has(args, 'b'); if (tc->prompt_string != NULL) return (CMD_RETURN_NORMAL); cdata = xcalloc(1, sizeof *cdata); - - cdata->inputs = NULL; - cdata->next_input = NULL; - - cdata->prompts = NULL; - cdata->next_prompt = NULL; - - cdata->template = NULL; cdata->idx = 1; + cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); + if (wait) + cdata->pi.item = item; + cdata->pi.c = tc; + cmd_find_copy_state(&cdata->pi.fs, target); + + if (wait) + cdata->item = item; + if (args->argc != 0 && args_has(args, 'F')) cdata->template = format_single_from_target(item, args->argv[0]); else if (args->argc != 0) @@ -140,7 +145,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags, cdata->prompt_type); free(prompt); - return (CMD_RETURN_NORMAL); + if (!wait) + return (CMD_RETURN_NORMAL); + return (CMD_RETURN_WAIT); } static int @@ -150,12 +157,13 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, struct cmd_command_prompt_cdata *cdata = data; char *new_template, *prompt, *ptr, *error; char *input = NULL; + struct cmdq_item *item = cdata->item; enum cmd_parse_status status; if (s == NULL) - return (0); + goto out; if (done && (cdata->flags & PROMPT_INCREMENTAL)) - return (0); + goto out; new_template = cmd_template_replace(cdata->template, s, cdata->idx); if (done) { @@ -177,7 +185,13 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, return (1); } - status = cmd_parse_and_append(new_template, NULL, c, NULL, &error); + if (item != NULL) { + status = cmd_parse_and_insert(new_template, &cdata->pi, item, + cmdq_get_state(item), &error); + } else { + status = cmd_parse_and_append(new_template, &cdata->pi, c, NULL, + &error); + } if (status == CMD_PARSE_ERROR) { cmdq_append(c, cmdq_get_error(error)); free(error); @@ -187,6 +201,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, free(new_template); if (c->prompt_inputcb != cmd_command_prompt_callback) return (1); + +out: + if (item != NULL) + cmdq_continue(item); return (0); } diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 83f9a0f8..6b754370 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -75,7 +75,6 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) cdata = xmalloc(sizeof *cdata); cdata->cmd = xstrdup(args->argv[0]); - memset(&cdata->pi, 0, sizeof cdata->pi); cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); if (wait) cdata->pi.item = item; @@ -88,8 +87,8 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) status_prompt_set(tc, target, new_prompt, NULL, cmd_confirm_before_callback, cmd_confirm_before_free, cdata, PROMPT_SINGLE, PROMPT_TYPE_COMMAND); - free(new_prompt); + if (!wait) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); @@ -104,14 +103,16 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, char *error; struct cmdq_item *item = cdata->item; enum cmd_parse_status status; + int retcode = 1; if (c->flags & CLIENT_DEAD) - return (0); + goto out; if (s == NULL || *s == '\0') goto out; if (tolower((u_char)s[0]) != 'y' || s[1] != '\0') goto out; + retcode = 0; if (item != NULL) { status = cmd_parse_and_insert(cmd, &cdata->pi, item, @@ -124,8 +125,12 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, } out: - if (item != NULL) - cmdq_continue(item); + if (item != NULL) { + if (cmdq_get_client(item) != NULL && + cmdq_get_client(item)->session == NULL) + cmdq_get_client(item)->retval = retcode; + cmdq_continue(item); + } return (0); } diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 65fbf19b..f4c81074 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -104,8 +104,6 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->cmd_if = xstrdup(args->argv[1]); if (args->argc == 3) cdata->cmd_else = xstrdup(args->argv[2]); - else - cdata->cmd_else = NULL; if (!args_has(args, 'b')) cdata->client = cmdq_get_client(item); @@ -116,10 +114,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'b')) cdata->item = item; - else - cdata->item = NULL; - memset(&cdata->input, 0, sizeof cdata->input); cmd_get_source(self, &file, &cdata->input.line); if (file != NULL) cdata->input.file = xstrdup(file); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 56d5f723..7bc1d7cc 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -121,7 +121,6 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->shell = !args_has(args, 'C'); if (!cdata->shell) { - memset(&cdata->pi, 0, sizeof cdata->pi); cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); if (wait) cdata->pi.item = item; diff --git a/tmux.1 b/tmux.1 index f6f50096..75d96c58 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5414,7 +5414,7 @@ See for possible values for .Ar prompt-type . .It Xo Ic command-prompt -.Op Fl 1FikN +.Op Fl 1bFikN .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client @@ -5516,7 +5516,12 @@ option: .It Li "Move cursor to previous word" Ta "b" Ta "M-b" .It Li "Move cursor to start" Ta "0" Ta "C-a" .It Li "Transpose characters" Ta "" Ta "C-t" +.Pp .El +With +.Fl b , +the prompt is shown in the background and the invoking client does not exit +until it is dismissed. .It Xo Ic confirm-before .Op Fl b .Op Fl p Ar prompt @@ -5537,7 +5542,8 @@ It may contain the special character sequences supported by the option. With .Fl b , -the prompt is shown in the background and the client. +the prompt is shown in the background and the invoking client does not exit +until it is dismissed. .It Xo Ic display-menu .Op Fl O .Op Fl c Ar target-client From 2bb0b9d6c5edd7c4127c971f5ccebed969f86c1c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 06:52:51 +0000 Subject: [PATCH 0901/1006] Change focus to be driven by events rather than walking all panes at end of event loop, this way the ordering of in and out can be enforced. GitHub issue 2808. --- cmd-attach-session.c | 23 +---------- cmd-new-session.c | 10 +---- cmd-switch-client.c | 16 +------- input.c | 8 +++- server-client.c | 96 ++++++++++++++++++-------------------------- server-fn.c | 16 +------- session.c | 6 +++ tmux.h | 5 ++- tty-keys.c | 6 ++- window.c | 51 +++++++++++++++++++++++ 10 files changed, 115 insertions(+), 122 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 6a7ebba7..c2074f4f 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -124,17 +124,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (!Eflag) environ_update(s->options, c->environ, s->environ); - c->session = s; + server_client_set_session(c, s); if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); - tty_update_client_offset(c); - status_timer_start(c); - notify_client("client-session-changed", c); - session_update_activity(s, NULL); - gettimeofday(&s->last_attached_time, NULL); - server_redraw_client(c); - s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = c; } else { if (server_client_open(c, &cause) != 0) { cmdq_error(item, "open terminal failed: %s", cause); @@ -156,25 +148,14 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (!Eflag) environ_update(s->options, c->environ, s->environ); - c->session = s; + server_client_set_session(c, s); server_client_set_key_table(c, NULL); - tty_update_client_offset(c); - status_timer_start(c); - notify_client("client-session-changed", c); - session_update_activity(s, NULL); - gettimeofday(&s->last_attached_time, NULL); - server_redraw_client(c); - s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = c; if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); notify_client("client-attached", c); c->flags |= CLIENT_ATTACHED; } - recalculate_sizes(); - alerts_check_session(s); - server_update_socket(); return (CMD_RETURN_NORMAL); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 033c707f..f3a5de26 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -329,18 +329,10 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) proc_send(c->peer, MSG_READY, -1, NULL, 0); } else if (c->session != NULL) c->last_session = c->session; - c->session = s; + server_client_set_session(c, s); if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); - tty_update_client_offset(c); - status_timer_start(c); - notify_client("client-session-changed", c); - session_update_activity(s, NULL); - gettimeofday(&s->last_attached_time, NULL); - server_redraw_client(c); } - recalculate_sizes(); - server_update_socket(); /* * If there are still configuration file errors to display, put the new diff --git a/cmd-switch-client.c b/cmd-switch-client.c index b10496e3..bc6baa6a 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -134,23 +134,9 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'E')) environ_update(s->options, tc->environ, s->environ); - if (tc->session != NULL && tc->session != s) - tc->last_session = tc->session; - tc->session = s; + server_client_set_session(tc, s); if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(tc, NULL); - tty_update_client_offset(tc); - status_timer_start(tc); - notify_client("client-session-changed", tc); - session_update_activity(s, NULL); - gettimeofday(&s->last_attached_time, NULL); - - server_check_unattached(); - server_redraw_client(tc); - s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = tc; - recalculate_sizes(); - alerts_check_session(s); return (CMD_RETURN_NORMAL); } diff --git a/input.c b/input.c index 49ed68b7..155144b7 100644 --- a/input.c +++ b/input.c @@ -1792,8 +1792,12 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) if (sctx->s->mode & MODE_FOCUSON) break; screen_write_mode_set(sctx, MODE_FOCUSON); - if (wp != NULL) - wp->flags |= PANE_FOCUSPUSH; /* force update */ + if (wp == NULL) + break; + if (wp->flags & PANE_FOCUSED) + bufferevent_write(wp->event, "\033[I", 3); + else + bufferevent_write(wp->event, "\033[O", 3); break; case 1005: screen_write_mode_set(sctx, MODE_MOUSE_UTF8); diff --git a/server-client.c b/server-client.c index 3b102beb..f4cc7865 100644 --- a/server-client.c +++ b/server-client.c @@ -33,7 +33,6 @@ #include "tmux.h" static void server_client_free(int, short, void *); -static void server_client_check_pane_focus(struct window_pane *); static void server_client_check_pane_resize(struct window_pane *); static void server_client_check_pane_buffer(struct window_pane *); static void server_client_check_window_resize(struct window *); @@ -301,9 +300,8 @@ server_client_attached_lost(struct client *c) s = loop->session; if (loop == c || s == NULL || s->curw->window != w) continue; - if (found == NULL || - timercmp(&loop->activity_time, &found->activity_time, - >)) + if (found == NULL || timercmp(&loop->activity_time, + &found->activity_time, >)) found = loop; } if (found != NULL) @@ -311,6 +309,40 @@ server_client_attached_lost(struct client *c) } } + +/* Set client session. */ +void +server_client_set_session(struct client *c, struct session *s) +{ + struct session *old = c->session; + + if (s != NULL && c->session != NULL && c->session != s) + c->last_session = c->session; + else if (s == NULL) + c->last_session = NULL; + c->session = s; + c->flags |= CLIENT_FOCUSED; + recalculate_sizes(); + + if (old != NULL && old->curw != NULL) + window_update_focus(old->curw->window); + if (s != NULL) { + window_update_focus(s->curw->window); + session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); + s->curw->flags &= ~WINLINK_ALERTFLAGS; + s->curw->window->latest = c; + alerts_check_session(s); + tty_update_client_offset(c); + status_timer_start(c); + notify_client("client-session-changed", c); + server_redraw_client(c); + } + + server_check_unattached(); + server_update_socket(); +} + /* Lost a client. */ void server_client_lost(struct client *c) @@ -1389,7 +1421,6 @@ server_client_loop(void) struct client *c; struct window *w; struct window_pane *wp; - int focus; /* Check for window resize. This is done before redrawing. */ RB_FOREACH(w, windows, &windows) @@ -1407,14 +1438,11 @@ server_client_loop(void) /* * Any windows will have been redrawn as part of clients, so clear - * their flags now. Also check pane focus and resize. + * their flags now. */ - focus = options_get_number(global_options, "focus-events"); RB_FOREACH(w, windows, &windows) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { - if (focus) - server_client_check_pane_focus(wp); server_client_check_pane_resize(wp); server_client_check_pane_buffer(wp); } @@ -1615,54 +1643,6 @@ out: bufferevent_enable(wp->event, EV_READ); } -/* Check whether pane should be focused. */ -static void -server_client_check_pane_focus(struct window_pane *wp) -{ - struct client *c; - int push; - - /* Do we need to push the focus state? */ - push = wp->flags & PANE_FOCUSPUSH; - wp->flags &= ~PANE_FOCUSPUSH; - - /* If we're not the active pane in our window, we're not focused. */ - if (wp->window->active != wp) - goto not_focused; - - /* - * If our window is the current window in any focused clients with an - * attached session, we're focused. - */ - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL || !(c->flags & CLIENT_FOCUSED)) - continue; - if (c->session->attached == 0) - continue; - - if (c->session->curw->window == wp->window) - goto focused; - } - -not_focused: - if (push || (wp->flags & PANE_FOCUSED)) { - if (wp->base.mode & MODE_FOCUSON) - bufferevent_write(wp->event, "\033[O", 3); - notify_pane("pane-focus-out", wp); - } - wp->flags &= ~PANE_FOCUSED; - return; - -focused: - if (push || !(wp->flags & PANE_FOCUSED)) { - if (wp->base.mode & MODE_FOCUSON) - bufferevent_write(wp->event, "\033[I", 3); - notify_pane("pane-focus-in", wp); - session_update_activity(c->session, NULL); - } - wp->flags |= PANE_FOCUSED; -} - /* * Update cursor position and mode settings. The scroll region and attributes * are cleared when idle (waiting for an event) as this is the most likely time @@ -2078,7 +2058,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) case MSG_EXITING: if (datalen != 0) fatalx("bad MSG_EXITING size"); - c->session = NULL; + server_client_set_session(c, NULL); tty_close(&c->tty); proc_send(c->peer, MSG_EXITED, -1, NULL, 0); break; diff --git a/server-fn.c b/server-fn.c index 4358fa5c..9b2f073b 100644 --- a/server-fn.c +++ b/server-fn.c @@ -445,21 +445,9 @@ server_destroy_session(struct session *s) TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; - if (s_new == NULL) { - c->session = NULL; + server_client_set_session(c, NULL); + if (s_new == NULL) c->flags |= CLIENT_EXIT; - } else { - c->last_session = NULL; - c->session = s_new; - server_client_set_key_table(c, NULL); - tty_update_client_offset(c); - status_timer_start(c); - notify_client("client-session-changed", c); - session_update_activity(s_new, NULL); - gettimeofday(&s_new->last_attached_time, NULL); - server_redraw_client(c); - alerts_check_session(s_new); - } } recalculate_sizes(); } diff --git a/session.c b/session.c index cb0093a2..73f44e18 100644 --- a/session.c +++ b/session.c @@ -489,6 +489,8 @@ session_last(struct session *s) int session_set_current(struct session *s, struct winlink *wl) { + struct winlink *old = s->curw; + if (wl == NULL) return (-1); if (wl == s->curw) @@ -497,6 +499,10 @@ session_set_current(struct session *s, struct winlink *wl) winlink_stack_remove(&s->lastw, wl); winlink_stack_push(&s->lastw, s->curw); s->curw = wl; + if (options_get_number(global_options, "focus-events")) { + window_update_focus(old->window); + window_update_focus(wl->window); + } winlink_clear_flags(wl); window_update_activity(wl->window); tty_update_window_offset(wl->window); diff --git a/tmux.h b/tmux.h index 3ac272f8..55df8005 100644 --- a/tmux.h +++ b/tmux.h @@ -1004,7 +1004,7 @@ struct window_pane { #define PANE_FOCUSED 0x4 /* 0x8 unused */ /* 0x10 unused */ -#define PANE_FOCUSPUSH 0x20 +/* 0x20 unused */ #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 #define PANE_EXITED 0x100 @@ -2506,6 +2506,7 @@ int server_client_handle_key(struct client *, struct key_event *); struct client *server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); +void server_client_set_session(struct client *, struct session *); void server_client_lost(struct client *); void server_client_suspend(struct client *); void server_client_detach(struct client *, enum msgtype); @@ -2826,6 +2827,8 @@ struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *, int); +void window_update_focus(struct window *); +void window_pane_update_focus(struct window_pane *); void window_redraw_active_switch(struct window *, struct window_pane *); struct window_pane *window_add_pane(struct window *, struct window_pane *, diff --git a/tty-keys.c b/tty-keys.c index 02fba0e5..156fceba 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -821,11 +821,13 @@ complete_key: /* Check for focus events. */ if (key == KEYC_FOCUS_OUT) { - tty->client->flags &= ~CLIENT_FOCUSED; + c->flags &= ~CLIENT_FOCUSED; + window_update_focus(c->session->curw->window); notify_client("client-focus-out", c); } else if (key == KEYC_FOCUS_IN) { - tty->client->flags |= CLIENT_FOCUSED; + c->flags |= CLIENT_FOCUSED; notify_client("client-focus-in", c); + window_update_focus(c->session->curw->window); } /* Fire the key. */ diff --git a/window.c b/window.c index f94acfcb..446898cb 100644 --- a/window.c +++ b/window.c @@ -458,6 +458,52 @@ window_has_pane(struct window *w, struct window_pane *wp) return (0); } +void +window_update_focus(struct window *w) +{ + if (w != NULL) { + log_debug("%s: @%u", __func__, w->id); + window_pane_update_focus(w->active); + } +} + +void +window_pane_update_focus(struct window_pane *wp) +{ + struct client *c; + int focused = 0; + + if (wp != NULL) { + if (wp != wp->window->active) + focused = 0; + else { + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL && + c->session->attached != 0 && + (c->flags & CLIENT_FOCUSED) && + c->session->curw->window == wp->window) { + focused = 1; + break; + } + } + } + if (!focused && (wp->flags & PANE_FOCUSED)) { + log_debug("%s: %%%u focus out", __func__, wp->id); + if (wp->base.mode & MODE_FOCUSON) + bufferevent_write(wp->event, "\033[O", 3); + notify_pane("pane-focus-out", wp); + wp->flags &= ~PANE_FOCUSED; + } else if (focused && (~wp->flags & PANE_FOCUSED)) { + log_debug("%s: %%%u focus in", __func__, wp->id); + if (wp->base.mode & MODE_FOCUSON) + bufferevent_write(wp->event, "\033[I", 3); + notify_pane("pane-focus-in", wp); + wp->flags |= PANE_FOCUSED; + } else + log_debug("%s: %%%u focus unchanged", __func__, wp->id); + } +} + int window_set_active_pane(struct window *w, struct window_pane *wp, int notify) { @@ -471,6 +517,11 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) w->active->active_point = next_active_point++; w->active->flags |= PANE_CHANGED; + if (options_get_number(global_options, "focus-events")) { + window_pane_update_focus(w->last); + window_pane_update_focus(w->active); + } + tty_update_window_offset(w); if (notify) From 13a0da205b8c24995de11776b93244e914e4694d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 07:37:58 +0000 Subject: [PATCH 0902/1006] Break message type stuff out into its own header. --- tmux-protocol.h | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ tmux.h | 111 +++------------------------------------------- tty-keys.c | 11 +++++ 3 files changed, 131 insertions(+), 105 deletions(-) create mode 100644 tmux-protocol.h diff --git a/tmux-protocol.h b/tmux-protocol.h new file mode 100644 index 00000000..08422291 --- /dev/null +++ b/tmux-protocol.h @@ -0,0 +1,114 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 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. + */ + +#ifndef TMUX_PROTOCOL_H +#define TMUX_PROTOCOL_H + +/* Protocol version. */ +#define PROTOCOL_VERSION 8 + +/* Message types. */ +enum msgtype { + MSG_VERSION = 12, + + MSG_IDENTIFY_FLAGS = 100, + MSG_IDENTIFY_TERM, + MSG_IDENTIFY_TTYNAME, + MSG_IDENTIFY_OLDCWD, /* unused */ + MSG_IDENTIFY_STDIN, + MSG_IDENTIFY_ENVIRON, + MSG_IDENTIFY_DONE, + MSG_IDENTIFY_CLIENTPID, + MSG_IDENTIFY_CWD, + MSG_IDENTIFY_FEATURES, + MSG_IDENTIFY_STDOUT, + MSG_IDENTIFY_LONGFLAGS, + MSG_IDENTIFY_TERMINFO, + + MSG_COMMAND = 200, + MSG_DETACH, + MSG_DETACHKILL, + MSG_EXIT, + MSG_EXITED, + MSG_EXITING, + MSG_LOCK, + MSG_READY, + MSG_RESIZE, + MSG_SHELL, + MSG_SHUTDOWN, + MSG_OLDSTDERR, /* unused */ + MSG_OLDSTDIN, /* unused */ + MSG_OLDSTDOUT, /* unused */ + MSG_SUSPEND, + MSG_UNLOCK, + MSG_WAKEUP, + MSG_EXEC, + MSG_FLAGS, + + MSG_READ_OPEN = 300, + MSG_READ, + MSG_READ_DONE, + MSG_WRITE_OPEN, + MSG_WRITE, + MSG_WRITE_READY, + MSG_WRITE_CLOSE +}; + +/* + * Message data. + * + * Don't forget to bump PROTOCOL_VERSION if any of these change! + */ +struct msg_command { + int argc; +}; /* followed by packed argv */ + +struct msg_read_open { + int stream; + int fd; +}; /* followed by path */ + +struct msg_read_data { + int stream; +}; + +struct msg_read_done { + int stream; + int error; +}; + +struct msg_write_open { + int stream; + int fd; + int flags; +}; /* followed by path */ + +struct msg_write_data { + int stream; +}; /* followed by data */ + +struct msg_write_ready { + int stream; + int error; +}; + +struct msg_write_close { + int stream; +}; + +#endif /* TMUX_PROTOCOL_H */ diff --git a/tmux.h b/tmux.h index 55df8005..587e0626 100644 --- a/tmux.h +++ b/tmux.h @@ -31,6 +31,7 @@ #include #include +#include "tmux-protocol.h" #include "xmalloc.h" extern char **environ; @@ -60,13 +61,12 @@ struct screen_write_cline; struct screen_write_ctx; struct session; struct tty_ctx; +struct tty_code; +struct tty_key; struct tmuxpeer; struct tmuxproc; struct winlink; -/* Client-server protocol version. */ -#define PROTOCOL_VERSION 8 - /* Default configuration files and socket paths. */ #ifndef TMUX_CONF #define TMUX_CONF "/etc/tmux.conf:~/.tmux.conf" @@ -504,95 +504,6 @@ enum tty_code_code { TTYC_XT }; -/* Message codes. */ -enum msgtype { - MSG_VERSION = 12, - - MSG_IDENTIFY_FLAGS = 100, - MSG_IDENTIFY_TERM, - MSG_IDENTIFY_TTYNAME, - MSG_IDENTIFY_OLDCWD, /* unused */ - MSG_IDENTIFY_STDIN, - MSG_IDENTIFY_ENVIRON, - MSG_IDENTIFY_DONE, - MSG_IDENTIFY_CLIENTPID, - MSG_IDENTIFY_CWD, - MSG_IDENTIFY_FEATURES, - MSG_IDENTIFY_STDOUT, - MSG_IDENTIFY_LONGFLAGS, - MSG_IDENTIFY_TERMINFO, - - MSG_COMMAND = 200, - MSG_DETACH, - MSG_DETACHKILL, - MSG_EXIT, - MSG_EXITED, - MSG_EXITING, - MSG_LOCK, - MSG_READY, - MSG_RESIZE, - MSG_SHELL, - MSG_SHUTDOWN, - MSG_OLDSTDERR, /* unused */ - MSG_OLDSTDIN, /* unused */ - MSG_OLDSTDOUT, /* unused */ - MSG_SUSPEND, - MSG_UNLOCK, - MSG_WAKEUP, - MSG_EXEC, - MSG_FLAGS, - - MSG_READ_OPEN = 300, - MSG_READ, - MSG_READ_DONE, - MSG_WRITE_OPEN, - MSG_WRITE, - MSG_WRITE_READY, - MSG_WRITE_CLOSE -}; - -/* - * Message data. - * - * Don't forget to bump PROTOCOL_VERSION if any of these change! - */ -struct msg_command { - int argc; -}; /* followed by packed argv */ - -struct msg_read_open { - int stream; - int fd; -}; /* followed by path */ - -struct msg_read_data { - int stream; -}; - -struct msg_read_done { - int stream; - int error; -}; - -struct msg_write_open { - int stream; - int fd; - int flags; -}; /* followed by path */ - -struct msg_write_data { - int stream; -}; /* followed by data */ - -struct msg_write_ready { - int stream; - int error; -}; - -struct msg_write_close { - int stream; -}; - /* Character classes. */ #define WHITESPACE " " @@ -1292,18 +1203,7 @@ struct key_event { struct mouse_event m; }; -/* TTY information. */ -struct tty_key { - char ch; - key_code key; - - struct tty_key *left; - struct tty_key *right; - - struct tty_key *next; -}; - -struct tty_code; +/* Terminal definition. */ struct tty_term { char *name; struct tty *tty; @@ -1325,6 +1225,7 @@ struct tty_term { }; LIST_HEAD(tty_terms, tty_term); +/* Client terminal. */ struct tty { struct client *client; struct event start_timer; @@ -1393,7 +1294,7 @@ struct tty { struct tty_key *key_tree; }; -/* TTY command context. */ +/* Terminal command context. */ typedef void (*tty_ctx_redraw_cb)(const struct tty_ctx *); typedef int (*tty_ctx_set_client_cb)(struct tty_ctx *, struct client *); struct tty_ctx { diff --git a/tty-keys.c b/tty-keys.c index 156fceba..6dfa70f3 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -58,6 +58,17 @@ static int tty_keys_device_attributes(struct tty *, const char *, size_t, static int tty_keys_extended_device_attributes(struct tty *, const char *, size_t, size_t *); +/* A key tree entry. */ +struct tty_key { + char ch; + key_code key; + + struct tty_key *left; + struct tty_key *right; + + struct tty_key *next; +}; + /* Default raw keys. */ struct tty_default_key_raw { const char *string; From 4c07367bfe626fce990f5b5b2d00516ae8249a93 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 13 Aug 2021 13:45:45 +0100 Subject: [PATCH 0903/1006] Fix fuzzer wrapper. --- fuzz/input-fuzzer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/input-fuzzer.c b/fuzz/input-fuzzer.c index 27f2be3d..81fbf6b4 100644 --- a/fuzz/input-fuzzer.c +++ b/fuzz/input-fuzzer.c @@ -43,7 +43,7 @@ LLVMFuzzerTestOneInput(const unsigned char *data, size_t size) w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0); wp = window_add_pane(w, NULL, 0, 0); bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty); - wp->ictx = input_init(wp, vpty[0]); + wp->ictx = input_init(wp, vpty[0], NULL); window_add_ref(w, __func__); input_parse_buffer(wp, (u_char*) data, size); From 614611a8bd65e82efccdf44d4f9c23c0f35ed293 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 17:03:29 +0000 Subject: [PATCH 0904/1006] Add -B flag to remove border from popup. --- cmd-display-menu.c | 6 ++-- popup.c | 89 +++++++++++++++++++++++++++++++++++----------- tmux.1 | 4 ++- tmux.h | 1 + 4 files changed, 76 insertions(+), 24 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index d021e477..c774284c 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -51,8 +51,8 @@ const struct cmd_entry cmd_display_popup_entry = { .name = "display-popup", .alias = "popup", - .args = { "Cc:d:Eh:t:w:x:y:", 0, -1 }, - .usage = "[-CE] [-c target-client] [-d start-directory] [-h height] " + .args = { "BCc:d:Eh:t:w:x:y:", 0, -1 }, + .usage = "[-BCE] [-c target-client] [-d start-directory] [-h height] " CMD_TARGET_PANE_USAGE " [-w width] " "[-x position] [-y position] [command]", @@ -391,6 +391,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) flags |= POPUP_CLOSEEXITZERO; else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; + if (args_has(args, 'B')) + flags |= POPUP_NOBORDER; if (popup_display(flags, item, px, py, w, h, shellcmd, argc, argv, cwd, tc, s, NULL, NULL) != 0) return (CMD_RETURN_NORMAL); diff --git a/popup.c b/popup.c index 57ce80be..f9a8e067 100644 --- a/popup.c +++ b/popup.c @@ -91,8 +91,13 @@ popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) ttyctx->wsx = c->tty.sx; ttyctx->wsy = c->tty.sy; - ttyctx->xoff = ttyctx->rxoff = pd->px + 1; - ttyctx->yoff = ttyctx->ryoff = pd->py + 1; + if (pd->flags & POPUP_NOBORDER) { + ttyctx->xoff = ttyctx->rxoff = pd->px; + ttyctx->yoff = ttyctx->ryoff = pd->py; + } else { + ttyctx->xoff = ttyctx->rxoff = pd->px + 1; + ttyctx->yoff = ttyctx->ryoff = pd->py + 1; + } return (1); } @@ -113,8 +118,13 @@ popup_mode_cb(struct client *c, u_int *cx, u_int *cy) { struct popup_data *pd = c->overlay_data; - *cx = pd->px + 1 + pd->s.cx; - *cy = pd->py + 1 + pd->s.cy; + if (pd->flags & POPUP_NOBORDER) { + *cx = pd->px + pd->s.cx; + *cy = pd->py + pd->s.cy; + } else { + *cx = pd->px + 1 + pd->s.cx; + *cy = pd->py + 1 + pd->s.cy; + } return (&pd->s); } @@ -145,8 +155,10 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) screen_write_start(&ctx, &s); screen_write_clearscreen(&ctx, 8); - /* Skip drawing popup if the terminal is too small. */ - if (pd->sx > 2 && pd->sy > 2) { + if (pd->flags & POPUP_NOBORDER) { + screen_write_cursormove(&ctx, 0, 0, 0); + screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx, pd->sy); + } else if (pd->sx > 2 && pd->sy > 2) { screen_write_box(&ctx, pd->sx, pd->sy); screen_write_cursormove(&ctx, 1, 1, 0); screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, @@ -218,7 +230,11 @@ popup_resize_cb(struct client *c) pd->px = pd->ppx; /* Avoid zero size screens. */ - if (pd->sx > 2 && pd->sy > 2) { + if (pd->flags & POPUP_NOBORDER) { + screen_resize(&pd->s, pd->sx, pd->sy, 0); + if (pd->job != NULL) + job_resize(pd->job, pd->sx, pd->sy ); + } else if (pd->sx > 2 && pd->sy > 2) { screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); if (pd->job != NULL) job_resize(pd->job, pd->sx - 2, pd->sy - 2); @@ -254,18 +270,31 @@ popup_handle_drag(struct client *c, struct popup_data *pd, pd->ppy = py; server_redraw_client(c); } else if (pd->dragging == SIZE) { - if (m->x < pd->px + 3) - return; - if (m->y < pd->py + 3) - return; + if (pd->flags & POPUP_NOBORDER) { + if (m->x < pd->px + 1) + return; + if (m->y < pd->py + 1) + return; + } else { + if (m->x < pd->px + 3) + return; + if (m->y < pd->py + 3) + return; + } pd->sx = m->x - pd->px; pd->sy = m->y - pd->py; pd->psx = pd->sx; pd->psy = pd->sy; - screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); - if (pd->job != NULL) - job_resize(pd->job, pd->sx - 2, pd->sy - 2); + if (pd->flags & POPUP_NOBORDER) { + screen_resize(&pd->s, pd->sx, pd->sy, 0); + if (pd->job != NULL) + job_resize(pd->job, pd->sx, pd->sy); + } else { + screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); + if (pd->job != NULL) + job_resize(pd->job, pd->sx - 2, pd->sy - 2); + } server_redraw_client(c); } } @@ -277,6 +306,7 @@ popup_key_cb(struct client *c, struct key_event *event) struct mouse_event *m = &event->m; const char *buf; size_t len; + u_int px, py; if (KEYC_IS_MOUSE(event->key)) { if (pd->dragging != OFF) { @@ -292,10 +322,11 @@ popup_key_cb(struct client *c, struct key_event *event) return (0); } if ((m->b & MOUSE_MASK_META) || - m->x == pd->px || + ((~pd->flags & POPUP_NOBORDER) && + (m->x == pd->px || m->x == pd->px + pd->sx - 1 || m->y == pd->py || - m->y == pd->py + pd->sy - 1) { + m->y == pd->py + pd->sy - 1))) { if (!MOUSE_DRAG(m->b)) goto out; if (MOUSE_BUTTONS(m->lb) == 0) @@ -315,8 +346,14 @@ popup_key_cb(struct client *c, struct key_event *event) if (pd->job != NULL) { if (KEYC_IS_MOUSE(event->key)) { /* Must be inside, checked already. */ - if (!input_key_get_mouse(&pd->s, m, m->x - pd->px - 1, - m->y - pd->py - 1, &buf, &len)) + if (pd->flags & POPUP_NOBORDER) { + px = m->x - pd->px; + py = m->y - pd->py; + } else { + px = m->x - pd->px - 1; + py = m->y - pd->py - 1; + } + if (!input_key_get_mouse(&pd->s, m, px, py, &buf, &len)) return (0); bufferevent_write(job_get_event(pd->job), buf, len); return (0); @@ -378,9 +415,19 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, struct client *c, struct session *s, popup_close_cb cb, void *arg) { struct popup_data *pd; + u_int jx, jy; - if (sx < 3 || sy < 3) - return (-1); + if (flags & POPUP_NOBORDER) { + if (sx < 1 || sy < 1) + return (-1); + jx = sx; + jy = sy; + } else { + if (sx < 3 || sy < 3) + return (-1); + jx = sx - 2; + jy = sy - 2; + } if (c->tty.sx < sx || c->tty.sy < sy) return (-1); @@ -411,7 +458,7 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->job = job_run(shellcmd, argc, argv, s, cwd, popup_job_update_cb, popup_job_complete_cb, NULL, pd, - JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, pd->sx - 2, pd->sy - 2); + JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, jx, jy); pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette); server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, diff --git a/tmux.1 b/tmux.1 index 75d96c58..c5d907a2 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5677,7 +5677,7 @@ lists the format variables and their values. forwards any input read from stdin to the empty pane given by .Ar target-pane . .It Xo Ic display-popup -.Op Fl CE +.Op Fl BCE .Op Fl c Ar target-client .Op Fl d Ar start-directory .Op Fl h Ar height @@ -5717,6 +5717,8 @@ and give the width and height - both may be a percentage (followed by .Ql % ) . If omitted, half of the terminal size is used. +.Fl B +does not surround the popup by a border. .Pp The .Fl C diff --git a/tmux.h b/tmux.h index 587e0626..f23cf5e8 100644 --- a/tmux.h +++ b/tmux.h @@ -3025,6 +3025,7 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int, /* popup.c */ #define POPUP_CLOSEEXIT 0x1 #define POPUP_CLOSEEXITZERO 0x2 +#define POPUP_NOBORDER 0x4 typedef void (*popup_close_cb)(int, void *); typedef void (*popup_finish_edit_cb)(char *, size_t, void *); int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, From 7789639b5d1818ebbac7bfab74959e4968d7cdf2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 18:54:54 +0000 Subject: [PATCH 0905/1006] Add a menu when a popup is present (mouse only for now). --- cmd-display-panes.c | 11 ++-- menu.c | 57 +++++++++++++----- popup.c | 141 ++++++++++++++++++++++++++++++++++++++------ screen-redraw.c | 5 +- server-client.c | 8 +-- tmux.1 | 2 +- tmux.h | 24 ++++++-- tty.c | 2 +- 8 files changed, 199 insertions(+), 51 deletions(-) diff --git a/cmd-display-panes.c b/cmd-display-panes.c index f03b80ed..beadae53 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -186,7 +186,8 @@ out: } static void -cmd_display_panes_draw(struct client *c, struct screen_redraw_ctx *ctx) +cmd_display_panes_draw(struct client *c, __unused void *data, + struct screen_redraw_ctx *ctx) { struct window *w = c->session->curw->window; struct window_pane *wp; @@ -200,9 +201,9 @@ cmd_display_panes_draw(struct client *c, struct screen_redraw_ctx *ctx) } static void -cmd_display_panes_free(struct client *c) +cmd_display_panes_free(__unused struct client *c, void *data) { - struct cmd_display_panes_data *cdata = c->overlay_data; + struct cmd_display_panes_data *cdata = data; if (cdata->item != NULL) cmdq_continue(cdata->item); @@ -211,9 +212,9 @@ cmd_display_panes_free(struct client *c) } static int -cmd_display_panes_key(struct client *c, struct key_event *event) +cmd_display_panes_key(struct client *c, void *data, struct key_event *event) { - struct cmd_display_panes_data *cdata = c->overlay_data; + struct cmd_display_panes_data *cdata = data; char *cmd, *expanded, *error; struct window *w = c->session->curw->window; struct window_pane *wp; diff --git a/menu.c b/menu.c index c8de02a8..7fb1996b 100644 --- a/menu.c +++ b/menu.c @@ -131,18 +131,33 @@ menu_free(struct menu *menu) free(menu); } -static struct screen * -menu_mode_cb(struct client *c, __unused u_int *cx, __unused u_int *cy) +struct screen * +menu_mode_cb(__unused struct client *c, void *data, __unused u_int *cx, + __unused u_int *cy) { - struct menu_data *md = c->overlay_data; + struct menu_data *md = data; return (&md->s); } -static void -menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) +int +menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py) { - struct menu_data *md = c->overlay_data; + struct menu_data *md = data; + struct menu *menu = md->menu; + + if (px < md->px || px > md->px + menu->width + 3) + return (1); + if (py < md->py || py > md->py + menu->count + 1) + return (1); + return (0); +} + +void +menu_draw_cb(struct client *c, void *data, + __unused struct screen_redraw_ctx *rctx) +{ + struct menu_data *md = data; struct tty *tty = &c->tty; struct screen *s = &md->s; struct menu *menu = md->menu; @@ -163,10 +178,10 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) } } -static void -menu_free_cb(struct client *c) +void +menu_free_cb(__unused struct client *c, void *data) { - struct menu_data *md = c->overlay_data; + struct menu_data *md = data; if (md->item != NULL) cmdq_continue(md->item); @@ -179,10 +194,10 @@ menu_free_cb(struct client *c) free(md); } -static int -menu_key_cb(struct client *c, struct key_event *event) +int +menu_key_cb(struct client *c, void *data, struct key_event *event) { - struct menu_data *md = c->overlay_data; + struct menu_data *md = data; struct menu *menu = md->menu; struct mouse_event *m = &event->m; u_int i; @@ -342,8 +357,8 @@ chosen: return (1); } -int -menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, +struct menu_data * +menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px, u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, void *data) { @@ -352,7 +367,7 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, const char *name; if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) - return (-1); + return (NULL); if (px + menu->width + 4 > c->tty.sx) px = c->tty.sx - menu->width - 4; if (py + menu->count + 2 > c->tty.sy) @@ -388,7 +403,19 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, md->cb = cb; md->data = data; + return (md); +} +int +menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, + u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, + void *data) +{ + struct menu_data *md; + + md = menu_prepare(menu, flags, item, px, py, c, fs, cb, data); + if (md == NULL) + return (-1); server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, menu_key_cb, menu_free_cb, NULL, md); return (0); diff --git a/popup.c b/popup.c index f9a8e067..0edd5c08 100644 --- a/popup.c +++ b/popup.c @@ -40,6 +40,10 @@ struct popup_data { popup_close_cb cb; void *arg; + struct menu *menu; + struct menu_data *md; + int close; + /* Current position and size. */ u_int px; u_int py; @@ -67,6 +71,16 @@ struct popup_editor { void *arg; }; +static const struct menu_item popup_menu_items[] = { + { "Close", 'q', NULL }, + { "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL }, + { "", KEYC_NONE, NULL }, + { "Fill Space", 'F', NULL }, + { "Centre", 'C', NULL }, + + { NULL, KEYC_NONE, NULL } +}; + static void popup_redraw_cb(const struct tty_ctx *ttyctx) { @@ -114,9 +128,12 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) } static struct screen * -popup_mode_cb(struct client *c, u_int *cx, u_int *cy) +popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; + + if (pd->md != NULL) + return (menu_mode_cb(c, pd->md, cx, cy)); if (pd->flags & POPUP_NOBORDER) { *cx = pd->px + pd->s.cx; @@ -129,21 +146,23 @@ popup_mode_cb(struct client *c, u_int *cx, u_int *cy) } static int -popup_check_cb(struct client *c, u_int px, u_int py) +popup_check_cb(struct client *c, void *data, u_int px, u_int py) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; if (px < pd->px || px > pd->px + pd->sx - 1) return (1); if (py < pd->py || py > pd->py + pd->sy - 1) return (1); + if (pd->md != NULL) + return (menu_check_cb(c, pd->md, px, py)); return (0); } static void -popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) +popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; struct tty *tty = &c->tty; struct screen s; struct screen_write_ctx ctx; @@ -170,18 +189,33 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) gc.fg = pd->palette.fg; gc.bg = pd->palette.bg; - c->overlay_check = NULL; + if (pd->md != NULL) { + c->overlay_check = menu_check_cb; + c->overlay_data = pd->md; + } else { + c->overlay_check = NULL; + c->overlay_data = NULL; + } for (i = 0; i < pd->sy; i++) tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &gc, palette); + if (pd->md != NULL) { + c->overlay_check = NULL; + c->overlay_data = NULL; + menu_draw_cb(c, pd->md, rctx); + } c->overlay_check = popup_check_cb; + c->overlay_data = pd; } static void -popup_free_cb(struct client *c) +popup_free_cb(struct client *c, void *data) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; struct cmdq_item *item = pd->item; + if (pd->md != NULL) + menu_free_cb(c, pd->md); + if (pd->cb != NULL) pd->cb(pd->status, pd->arg); @@ -199,17 +233,20 @@ popup_free_cb(struct client *c) screen_free(&pd->s); colour_palette_free(&pd->palette); + free(pd); } static void -popup_resize_cb(struct client *c) +popup_resize_cb(__unused struct client *c, void *data) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; struct tty *tty = &c->tty; if (pd == NULL) return; + if (pd->md != NULL) + menu_free_cb(c, pd->md); /* Adjust position and size. */ if (pd->psy > tty->sy) @@ -241,6 +278,46 @@ popup_resize_cb(struct client *c) } } +static void +popup_menu_done(__unused struct menu *menu, __unused u_int choice, + key_code key, void *data) +{ + struct popup_data *pd = data; + struct client *c = pd->c; + struct paste_buffer *pb; + const char *buf; + size_t len; + + pd->md = NULL; + pd->menu = NULL; + server_redraw_client(pd->c); + + switch (key) { + case 'p': + pb = paste_get_top(NULL); + if (pb != NULL) { + buf = paste_buffer_data(pb, &len); + bufferevent_write(job_get_event(pd->job), buf, len); + } + break; + case 'F': + pd->sx = c->tty.sx; + pd->sy = c->tty.sy; + pd->px = 0; + pd->py = 0; + server_redraw_client(c); + break; + case 'C': + pd->px = c->tty.sx / 2 - pd->sx / 2; + pd->py = c->tty.sy / 2 - pd->sy / 2; + server_redraw_client(c); + break; + case 'q': + pd->close = 1; + break; + } +} + static void popup_handle_drag(struct client *c, struct popup_data *pd, struct mouse_event *m) @@ -300,13 +377,25 @@ popup_handle_drag(struct client *c, struct popup_data *pd, } static int -popup_key_cb(struct client *c, struct key_event *event) +popup_key_cb(struct client *c, void *data, struct key_event *event) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; struct mouse_event *m = &event->m; const char *buf; size_t len; - u_int px, py; + u_int px, py, x; + + if (pd->md != NULL) { + if (menu_key_cb(c, pd->md, event) == 1) { + pd->md = NULL; + pd->menu = NULL; + if (pd->close) + server_client_clear_overlay(c); + else + server_redraw_client(c); + } + return (0); + } if (KEYC_IS_MOUSE(event->key)) { if (pd->dragging != OFF) { @@ -317,10 +406,18 @@ popup_key_cb(struct client *c, struct key_event *event) m->x > pd->px + pd->sx - 1 || m->y < pd->py || m->y > pd->py + pd->sy - 1) { - if (MOUSE_BUTTONS (m->b) == 1) - return (1); + if (MOUSE_BUTTONS(m->b) == 2) + goto menu; return (0); } + if ((~pd->flags & POPUP_NOBORDER) && + (~m->b & MOUSE_MASK_META) && + MOUSE_BUTTONS(m->b) == 2 && + (m->x == pd->px || + m->x == pd->px + pd->sx - 1 || + m->y == pd->py || + m->y == pd->py + pd->sy - 1)) + goto menu; if ((m->b & MOUSE_MASK_META) || ((~pd->flags & POPUP_NOBORDER) && (m->x == pd->px || @@ -338,7 +435,6 @@ popup_key_cb(struct client *c, struct key_event *event) goto out; } } - if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) || pd->job == NULL) && (event->key == '\033' || event->key == '\003')) @@ -362,6 +458,17 @@ popup_key_cb(struct client *c, struct key_event *event) } return (0); +menu: + pd->menu = menu_create(""); + menu_add_items(pd->menu, popup_menu_items, NULL, NULL, NULL); + if (m->x >= (pd->menu->width + 4) / 2) + x = m->x - (pd->menu->width + 4) / 2; + else + x = 0; + pd->md = menu_prepare(pd->menu, 0, NULL, x, m->y, c, NULL, + popup_menu_done, pd); + c->flags |= CLIENT_REDRAWOVERLAY; + out: pd->lx = m->x; pd->ly = m->y; diff --git a/screen-redraw.c b/screen-redraw.c index 3df57383..82e390cd 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -624,7 +624,7 @@ screen_redraw_screen(struct client *c) } if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) { log_debug("%s: redrawing overlay", c->name); - c->overlay_draw(c, &ctx); + c->overlay_draw(c, c->overlay_data, &ctx); } tty_reset(&c->tty); @@ -690,7 +690,8 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) struct grid_cell gc; const struct grid_cell *tmp; - if (c->overlay_check != NULL && !c->overlay_check(c, x, y)) + if (c->overlay_check != NULL && + !c->overlay_check(c, c->overlay_data, x, y)) return; cell_type = screen_redraw_check_cell(c, x, y, pane_status, &wp); diff --git a/server-client.c b/server-client.c index f4cc7865..08584e7b 100644 --- a/server-client.c +++ b/server-client.c @@ -134,7 +134,7 @@ server_client_clear_overlay(struct client *c) evtimer_del(&c->overlay_timer); if (c->overlay_free != NULL) - c->overlay_free(c); + c->overlay_free(c, c->overlay_data); c->overlay_check = NULL; c->overlay_mode = NULL; @@ -1390,7 +1390,7 @@ server_client_handle_key(struct client *c, struct key_event *event) status_message_clear(c); } if (c->overlay_key != NULL) { - switch (c->overlay_key(c, event)) { + switch (c->overlay_key(c, c->overlay_data, event)) { case 0: return (0); case 1: @@ -1673,7 +1673,7 @@ server_client_reset_state(struct client *c) /* Get mode from overlay if any, else from screen. */ if (c->overlay_draw != NULL) { if (c->overlay_mode != NULL) - s = c->overlay_mode(c, &cx, &cy); + s = c->overlay_mode(c, c->overlay_data, &cx, &cy); } else s = wp->screen; if (s != NULL) @@ -2050,7 +2050,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) if (c->overlay_resize == NULL) server_client_clear_overlay(c); else - c->overlay_resize(c); + c->overlay_resize(c, c->overlay_data); server_redraw_client(c); if (c->session != NULL) notify_client("client-resized", c); diff --git a/tmux.1 b/tmux.1 index c5d907a2..28c54905 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5516,8 +5516,8 @@ option: .It Li "Move cursor to previous word" Ta "b" Ta "M-b" .It Li "Move cursor to start" Ta "0" Ta "C-a" .It Li "Transpose characters" Ta "" Ta "C-t" -.Pp .El +.Pp With .Fl b , the prompt is shown in the background and the invoking client does not exit diff --git a/tmux.h b/tmux.h index f23cf5e8..49142203 100644 --- a/tmux.h +++ b/tmux.h @@ -51,6 +51,7 @@ struct format_job_tree; struct format_tree; struct input_ctx; struct job; +struct menu_data; struct mode_tree_data; struct mouse_event; struct options; @@ -1535,12 +1536,14 @@ RB_HEAD(client_windows, client_window); /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); -typedef int (*overlay_check_cb)(struct client *, u_int, u_int); -typedef struct screen *(*overlay_mode_cb)(struct client *, u_int *, u_int *); -typedef void (*overlay_draw_cb)(struct client *, struct screen_redraw_ctx *); -typedef int (*overlay_key_cb)(struct client *, struct key_event *); -typedef void (*overlay_free_cb)(struct client *); -typedef void (*overlay_resize_cb)(struct client *); +typedef int (*overlay_check_cb)(struct client *, void *, u_int, u_int); +typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *, + u_int *); +typedef void (*overlay_draw_cb)(struct client *, void *, + struct screen_redraw_ctx *); +typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *); +typedef void (*overlay_free_cb)(struct client *, void *); +typedef void (*overlay_resize_cb)(struct client *, void *); struct client { const char *name; struct tmuxpeer *peer; @@ -3018,9 +3021,18 @@ void menu_add_item(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); void menu_free(struct menu *); +struct menu_data *menu_prepare(struct menu *, int, struct cmdq_item *, u_int, + u_int, struct client *, struct cmd_find_state *, + menu_choice_cb, void *); int menu_display(struct menu *, int, struct cmdq_item *, u_int, u_int, struct client *, struct cmd_find_state *, menu_choice_cb, void *); +struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *); +int menu_check_cb(struct client *, void *, u_int, u_int); +void menu_draw_cb(struct client *, void *, + struct screen_redraw_ctx *); +void menu_free_cb(struct client *, void *); +int menu_key_cb(struct client *, void *, struct key_event *); /* popup.c */ #define POPUP_CLOSEEXIT 0x1 diff --git a/tty.c b/tty.c index ecad9249..2a9f8b01 100644 --- a/tty.c +++ b/tty.c @@ -1318,7 +1318,7 @@ tty_check_overlay(struct tty *tty, u_int px, u_int py) if (c->overlay_check == NULL) return (1); - return (c->overlay_check(c, px, py)); + return (c->overlay_check(c, c->overlay_data, px, py)); } void From 92615b534a887980ac7b52ca8322b6947cd1a47d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 19:25:24 +0000 Subject: [PATCH 0906/1006] Adjust overlay check callback before drawing data from pty. --- popup.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/popup.c b/popup.c index 0edd5c08..87aaa69f 100644 --- a/popup.c +++ b/popup.c @@ -489,9 +489,16 @@ popup_job_update_cb(struct job *job) if (size == 0) return; - c->overlay_check = NULL; + if (pd->md != NULL) { + c->overlay_check = menu_check_cb; + c->overlay_data = pd->md; + } else { + c->overlay_check = NULL; + c->overlay_data = NULL; + } input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size); c->overlay_check = popup_check_cb; + c->overlay_data = pd; evbuffer_drain(evb, size); } From 2588c3e52e24a8726ac3d1277ede6ddf07dd6257 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 19:27:25 +0000 Subject: [PATCH 0907/1006] Add menu options to convert a popup into a pane. --- job.c | 21 ++++++++++++++++++++ popup.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- tmux.h | 2 ++ 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/job.c b/job.c index a972bc0e..adad71e9 100644 --- a/job.c +++ b/job.c @@ -201,6 +201,27 @@ fail: return (NULL); } +/* Take job's file descriptor and free the job. */ +int +job_transfer(struct job *job) +{ + int fd = job->fd; + + log_debug("transfer job %p: %s", job, job->cmd); + + LIST_REMOVE(job, entry); + free(job->cmd); + + if (job->freecb != NULL && job->data != NULL) + job->freecb(job->data); + + if (job->event != NULL) + bufferevent_free(job->event); + + free(job); + return (fd); +} + /* Kill and free an individual job. */ void job_free(struct job *job) diff --git a/popup.c b/popup.c index 87aaa69f..f6867f91 100644 --- a/popup.c +++ b/popup.c @@ -77,6 +77,18 @@ static const struct menu_item popup_menu_items[] = { { "", KEYC_NONE, NULL }, { "Fill Space", 'F', NULL }, { "Centre", 'C', NULL }, + { "", KEYC_NONE, NULL }, + { "Make Pane (H)", 'h', NULL }, + { "Make Pane (V)", 'v', NULL }, + + { NULL, KEYC_NONE, NULL } +}; + +static const struct menu_item popup_internal_menu_items[] = { + { "Close", 'q', NULL }, + { "", KEYC_NONE, NULL }, + { "Fill Space", 'F', NULL }, + { "Centre", 'C', NULL }, { NULL, KEYC_NONE, NULL } }; @@ -278,6 +290,37 @@ popup_resize_cb(__unused struct client *c, void *data) } } +static void +popup_make_pane(struct popup_data *pd, enum layout_type type) +{ + struct client *c = pd->c; + struct session *s = c->session; + struct window *w = s->curw->window; + struct layout_cell *lc; + struct window_pane *wp = w->active, *new_wp; + u_int hlimit; + + window_unzoom(w); + + lc = layout_split_pane(wp, type, -1, 0); + hlimit = options_get_number(s->options, "history-limit"); + new_wp = window_add_pane(wp->window, NULL, hlimit, 0); + layout_assign_pane(lc, new_wp, 0); + + new_wp->fd = job_transfer(pd->job); + pd->job = NULL; + + screen_free(&new_wp->base); + memcpy(&new_wp->base, &pd->s, sizeof wp->base); + screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1); + screen_init(&pd->s, 1, 1, 0); + + window_pane_set_event(new_wp); + window_set_active_pane(w, new_wp, 1); + + pd->close = 1; +} + static void popup_menu_done(__unused struct menu *menu, __unused u_int choice, key_code key, void *data) @@ -312,6 +355,12 @@ popup_menu_done(__unused struct menu *menu, __unused u_int choice, pd->py = c->tty.sy / 2 - pd->sy / 2; server_redraw_client(c); break; + case 'h': + popup_make_pane(pd, LAYOUT_LEFTRIGHT); + break; + case 'v': + popup_make_pane(pd, LAYOUT_TOPBOTTOM); + break; case 'q': pd->close = 1; break; @@ -460,7 +509,11 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) menu: pd->menu = menu_create(""); - menu_add_items(pd->menu, popup_menu_items, NULL, NULL, NULL); + if (pd->flags & POPUP_INTERNAL) { + menu_add_items(pd->menu, popup_internal_menu_items, NULL, NULL, + NULL); + } else + menu_add_items(pd->menu, popup_menu_items, NULL, NULL, NULL); if (m->x >= (pd->menu->width + 4) / 2) x = m->x - (pd->menu->width + 4) / 2; else @@ -659,8 +712,8 @@ popup_editor(struct client *c, const char *buf, size_t len, py = (c->tty.sy / 2) - (sy / 2); xasprintf(&cmd, "%s %s", editor, path); - if (popup_display(POPUP_CLOSEEXIT, NULL, px, py, sx, sy, cmd, 0, NULL, - _PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) { + if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, + cmd, 0, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) { popup_editor_free(pe); free(cmd); return (-1); diff --git a/tmux.h b/tmux.h index 49142203..f8c3bbd9 100644 --- a/tmux.h +++ b/tmux.h @@ -2042,6 +2042,7 @@ struct job *job_run(const char *, int, char **, struct session *, const char *, job_update_cb, job_complete_cb, job_free_cb, void *, int, int, int); void job_free(struct job *); +int job_transfer(struct job *); void job_resize(struct job *, u_int, u_int); void job_check_died(pid_t, int); int job_get_status(struct job *); @@ -3038,6 +3039,7 @@ int menu_key_cb(struct client *, void *, struct key_event *); #define POPUP_CLOSEEXIT 0x1 #define POPUP_CLOSEEXITZERO 0x2 #define POPUP_NOBORDER 0x4 +#define POPUP_INTERNAL 0x8 typedef void (*popup_close_cb)(int, void *); typedef void (*popup_finish_edit_cb)(char *, size_t, void *); int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, From 7a0cec5ecf0d0a89caaee4a16b629155efe23f22 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 19:55:11 +0000 Subject: [PATCH 0908/1006] Fill in some other bits on new panes. --- job.c | 13 ++++++++++--- popup.c | 15 ++++++++++++--- tmux.h | 2 +- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/job.c b/job.c index adad71e9..66a25557 100644 --- a/job.c +++ b/job.c @@ -52,6 +52,7 @@ struct job { char *cmd; pid_t pid; + char tty[TTY_NAME_MAX]; int status; int fd; @@ -81,7 +82,7 @@ job_run(const char *cmd, int argc, char **argv, struct session *s, const char *home; sigset_t set, oldset; struct winsize ws; - char **argvp; + char **argvp, tty[TTY_NAME_MAX]; /* * Do not set TERM during .tmux.conf, it is nice to be able to use @@ -96,7 +97,7 @@ job_run(const char *cmd, int argc, char **argv, struct session *s, memset(&ws, 0, sizeof ws); ws.ws_col = sx; ws.ws_row = sy; - pid = fdforkpty(ptm_fd, &master, NULL, NULL, &ws); + pid = fdforkpty(ptm_fd, &master, tty, NULL, &ws); } else { if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) goto fail; @@ -170,6 +171,7 @@ job_run(const char *cmd, int argc, char **argv, struct session *s, else job->cmd = cmd_stringify_argv(argc, argv); job->pid = pid; + strlcpy(job->tty, tty, sizeof job->tty); job->status = 0; LIST_INSERT_HEAD(&all_jobs, job, entry); @@ -203,12 +205,17 @@ fail: /* Take job's file descriptor and free the job. */ int -job_transfer(struct job *job) +job_transfer(struct job *job, pid_t *pid, char *tty, size_t ttylen) { int fd = job->fd; log_debug("transfer job %p: %s", job, job->cmd); + if (pid != NULL) + *pid = job->pid; + if (tty != NULL) + strlcpy(tty, job->tty, ttylen); + LIST_REMOVE(job, entry); free(job->cmd); diff --git a/popup.c b/popup.c index f6867f91..1783411a 100644 --- a/popup.c +++ b/popup.c @@ -78,8 +78,8 @@ static const struct menu_item popup_menu_items[] = { { "Fill Space", 'F', NULL }, { "Centre", 'C', NULL }, { "", KEYC_NONE, NULL }, - { "Make Pane (H)", 'h', NULL }, - { "Make Pane (V)", 'v', NULL }, + { "To Horizontal Pane", 'h', NULL }, + { "To Vertical Pane", 'v', NULL }, { NULL, KEYC_NONE, NULL } }; @@ -299,6 +299,7 @@ popup_make_pane(struct popup_data *pd, enum layout_type type) struct layout_cell *lc; struct window_pane *wp = w->active, *new_wp; u_int hlimit; + const char *shell; window_unzoom(w); @@ -307,16 +308,24 @@ popup_make_pane(struct popup_data *pd, enum layout_type type) new_wp = window_add_pane(wp->window, NULL, hlimit, 0); layout_assign_pane(lc, new_wp, 0); - new_wp->fd = job_transfer(pd->job); + new_wp->fd = job_transfer(pd->job, &new_wp->pid, new_wp->tty, + sizeof new_wp->tty); pd->job = NULL; + screen_set_title(&pd->s, new_wp->base.title); screen_free(&new_wp->base); memcpy(&new_wp->base, &pd->s, sizeof wp->base); screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1); screen_init(&pd->s, 1, 1, 0); + shell = options_get_string(s->options, "default-shell"); + if (!checkshell(shell)) + shell = _PATH_BSHELL; + new_wp->shell = xstrdup(shell); + window_pane_set_event(new_wp); window_set_active_pane(w, new_wp, 1); + new_wp->flags |= PANE_CHANGED; pd->close = 1; } diff --git a/tmux.h b/tmux.h index f8c3bbd9..d1ae5e43 100644 --- a/tmux.h +++ b/tmux.h @@ -2042,7 +2042,7 @@ struct job *job_run(const char *, int, char **, struct session *, const char *, job_update_cb, job_complete_cb, job_free_cb, void *, int, int, int); void job_free(struct job *); -int job_transfer(struct job *); +int job_transfer(struct job *, pid_t *, char *, size_t); void job_resize(struct job *, u_int, u_int); void job_check_died(pid_t, int); int job_get_status(struct job *); From 63aa96864280ff07f706ebbae302b7c15abb964f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 20:04:45 +0000 Subject: [PATCH 0909/1006] Check callback needs to only return 0 (text should be suppressed) if menu returns 0, otherwise it should check the popup also. --- popup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/popup.c b/popup.c index 1783411a..95de58a9 100644 --- a/popup.c +++ b/popup.c @@ -162,12 +162,12 @@ popup_check_cb(struct client *c, void *data, u_int px, u_int py) { struct popup_data *pd = data; + if (pd->md != NULL && menu_check_cb(c, pd->md, px, py) == 0) + return (0); if (px < pd->px || px > pd->px + pd->sx - 1) return (1); if (py < pd->py || py > pd->py + pd->sy - 1) return (1); - if (pd->md != NULL) - return (menu_check_cb(c, pd->md, px, py)); return (0); } From 7d7d7c960593de4c37962ca2d74dbd13e5a3cc2b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 23:05:40 +0000 Subject: [PATCH 0910/1006] Tweak how mouse works on popup: only Meta alone resizes or moves, not Meta with other modifiers; button 2 on the left or top border opens menu, right or bottom resizes; button 1 on any border moves. --- popup.c | 27 +++++++++++++++------------ tmux.h | 1 + 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/popup.c b/popup.c index 95de58a9..d4fc3833 100644 --- a/popup.c +++ b/popup.c @@ -442,6 +442,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) const char *buf; size_t len; u_int px, py, x; + enum { NONE, LEFT, RIGHT, TOP, BOTTOM } border = NONE; if (pd->md != NULL) { if (menu_key_cb(c, pd->md, event) == 1) { @@ -468,20 +469,22 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) goto menu; return (0); } - if ((~pd->flags & POPUP_NOBORDER) && - (~m->b & MOUSE_MASK_META) && + if (~pd->flags & POPUP_NOBORDER) { + if (m->x == pd->px) + border = LEFT; + else if (m->x == pd->px + pd->sx - 1) + border = RIGHT; + else if (m->y == pd->py) + border = TOP; + else if (m->y == pd->py + pd->sy - 1) + border = BOTTOM; + } + if ((m->b & MOUSE_MASK_MODIFIERS) == 0 && MOUSE_BUTTONS(m->b) == 2 && - (m->x == pd->px || - m->x == pd->px + pd->sx - 1 || - m->y == pd->py || - m->y == pd->py + pd->sy - 1)) + (border == LEFT || border == TOP)) goto menu; - if ((m->b & MOUSE_MASK_META) || - ((~pd->flags & POPUP_NOBORDER) && - (m->x == pd->px || - m->x == pd->px + pd->sx - 1 || - m->y == pd->py || - m->y == pd->py + pd->sy - 1))) { + if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) || + border != NONE) { if (!MOUSE_DRAG(m->b)) goto out; if (MOUSE_BUTTONS(m->lb) == 0) diff --git a/tmux.h b/tmux.h index d1ae5e43..e6ba8e86 100644 --- a/tmux.h +++ b/tmux.h @@ -1158,6 +1158,7 @@ RB_HEAD(sessions, session); #define MOUSE_MASK_CTRL 16 #define MOUSE_MASK_DRAG 32 #define MOUSE_MASK_WHEEL 64 +#define MOUSE_MASK_MODIFIERS (MOUSE_MASK_SHIFT|MOUSE_MASK_META|MOUSE_MASK_CTRL) /* Mouse wheel states. */ #define MOUSE_WHEEL_UP 0 From 4cc6db7281e2b640c71c01446b2204d4cc1a5d15 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Aug 2021 08:06:37 +0000 Subject: [PATCH 0911/1006] Missing argument specifier for -c. --- cmd-display-message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index 0522d37f..8fd6a8ff 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_display_message_entry = { .name = "display-message", .alias = "display", - .args = { "acd:INpt:F:v", 0, 1 }, + .args = { "ac:d:INpt:F:v", 0, 1 }, .usage = "[-aINpv] [-c target-client] [-d delay] [-F format] " CMD_TARGET_PANE_USAGE " [message]", From befe7cb1c5d29e03c8a0a27f6b4e3db7afae9d79 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Aug 2021 14:00:07 +0000 Subject: [PATCH 0912/1006] Do not use NULL palette when clearing. --- colour.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/colour.c b/colour.c index 7aa7620a..9ac07415 100644 --- a/colour.c +++ b/colour.c @@ -959,10 +959,10 @@ colour_palette_init(struct colour_palette *p) void colour_palette_clear(struct colour_palette *p) { - p->fg = 8; - p->bg = 8; if (p != NULL) { - free(p->palette); + p->fg = 8; + p->bg = 8; + free(p->palette); p->palette = NULL; } } From 30786abe0ebc99ebc4556eb70bc46cb670892cbd Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Aug 2021 16:26:29 +0000 Subject: [PATCH 0913/1006] Some other missing palette NULL checks, from oss-fuzz. --- input.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/input.c b/input.c index 155144b7..7a320c56 100644 --- a/input.c +++ b/input.c @@ -2556,10 +2556,12 @@ input_osc_10(struct input_ctx *ictx, const char *p) log_debug("bad OSC 10: %s", p); return; } - ictx->palette->fg = c; - if (wp != NULL) - wp->flags |= PANE_STYLECHANGED; - screen_write_fullredraw(&ictx->ctx); + if (ictx->palette != NULL) { + ictx->palette->fg = c; + if (wp != NULL) + wp->flags |= PANE_STYLECHANGED; + screen_write_fullredraw(&ictx->ctx); + } } /* Handle the OSC 110 sequence for resetting background colour. */ @@ -2570,10 +2572,12 @@ input_osc_110(struct input_ctx *ictx, const char *p) if (*p != '\0') return; - ictx->palette->fg = 8; - if (wp != NULL) - wp->flags |= PANE_STYLECHANGED; - screen_write_fullredraw(&ictx->ctx); + if (ictx->palette != NULL) { + ictx->palette->fg = 8; + if (wp != NULL) + wp->flags |= PANE_STYLECHANGED; + screen_write_fullredraw(&ictx->ctx); + } } /* Handle the OSC 11 sequence for setting and querying background colour. */ @@ -2596,10 +2600,12 @@ input_osc_11(struct input_ctx *ictx, const char *p) log_debug("bad OSC 11: %s", p); return; } - ictx->palette->bg = c; - if (wp != NULL) - wp->flags |= PANE_STYLECHANGED; - screen_write_fullredraw(&ictx->ctx); + if (ictx->palette != NULL) { + ictx->palette->bg = c; + if (wp != NULL) + wp->flags |= PANE_STYLECHANGED; + screen_write_fullredraw(&ictx->ctx); + } } /* Handle the OSC 111 sequence for resetting background colour. */ @@ -2610,10 +2616,12 @@ input_osc_111(struct input_ctx *ictx, const char *p) if (*p != '\0') return; - ictx->palette->bg = 8; - if (wp != NULL) - wp->flags |= PANE_STYLECHANGED; - screen_write_fullredraw(&ictx->ctx); + if (ictx->palette != NULL) { + ictx->palette->bg = 8; + if (wp != NULL) + wp->flags |= PANE_STYLECHANGED; + screen_write_fullredraw(&ictx->ctx); + } } /* Handle the OSC 52 sequence for setting the clipboard. */ From c1be1b351d07f95784c1f05ba1359b578fb57af9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 14 Aug 2021 18:33:13 +0100 Subject: [PATCH 0914/1006] Minor cleanups, GitHub issue 2824. --- CHANGES | 2 +- SYNCING | 2 +- example_tmux.conf | 2 +- regress/new-session-command.sh | 2 +- regress/new-window-command.sh | 2 +- tools/UTF-8-demo.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index bf2cff05..38c888c1 100644 --- a/CHANGES +++ b/CHANGES @@ -133,7 +133,7 @@ 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. - + * Add some formats for search in copy mode (search_present, search_match). * Do not wait on shutdown for commands started with run -b. diff --git a/SYNCING b/SYNCING index b8cc4b5c..07be40c4 100644 --- a/SYNCING +++ b/SYNCING @@ -89,7 +89,7 @@ of the remote name "obsd-tmux", we can now create the master branch from % git checkout -b obsd-master obsd-tmux/master Adding in the fake history points -================================= +================================= To tie both the "master" branch from "tmux" and the "obsd-master" branch from "tmux-openbsd" together, the fake history points added to the diff --git a/example_tmux.conf b/example_tmux.conf index 11db9ec6..1bd9afdc 100644 --- a/example_tmux.conf +++ b/example_tmux.conf @@ -61,7 +61,7 @@ bind y set synchronize-panes\; display 'synchronize-panes #{?synchronize-panes,o # should be started with "tmux attach" rather than "tmux new" new -d -s0 -nirssi 'exec irssi' set -t0:0 monitor-activity on -set -t0:0 aggressive-resize on +set -t0:0 aggressive-resize on neww -d -ntodo 'exec emacs ~/TODO' setw -t0:1 aggressive-resize on neww -d -nmutt 'exec mutt' diff --git a/regress/new-session-command.sh b/regress/new-session-command.sh index 02ba55d9..8dec322a 100644 --- a/regress/new-session-command.sh +++ b/regress/new-session-command.sh @@ -11,7 +11,7 @@ $TMUX kill-server 2>/dev/null TMP=$(mktemp) trap "rm -f $TMP" 0 1 15 - + cat <$TMP new sleep 101 new -- sleep 102 diff --git a/regress/new-window-command.sh b/regress/new-window-command.sh index 176bffb5..b83376e4 100644 --- a/regress/new-window-command.sh +++ b/regress/new-window-command.sh @@ -11,7 +11,7 @@ $TMUX kill-server 2>/dev/null TMP=$(mktemp) trap "rm -f $TMP" 0 1 15 - + cat <$TMP new neww sleep 101 diff --git a/tools/UTF-8-demo.txt b/tools/UTF-8-demo.txt index 4363f27b..ff915b26 100644 --- a/tools/UTF-8-demo.txt +++ b/tools/UTF-8-demo.txt @@ -2,7 +2,7 @@ UTF-8 encoded sample plain-text file ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -Markus Kuhn [ˈmaʳkʊs kuːn] — 2002-07-25 +Markus Kuhn [ˈmaʳkʊs kuːn] — 2002-07-25 CC BY The ASCII compatible UTF-8 encoding used in this plain-text file From 21ce1e04fe7125b646f7888387596c4dd122fcc3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 14 Aug 2021 22:30:20 +0100 Subject: [PATCH 0915/1006] Fuzzer needs some other bits it seems. --- fuzz/input-fuzzer.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fuzz/input-fuzzer.c b/fuzz/input-fuzzer.c index 81fbf6b4..0d80690b 100644 --- a/fuzz/input-fuzzer.c +++ b/fuzz/input-fuzzer.c @@ -26,7 +26,7 @@ struct event_base *libevent; int -LLVMFuzzerTestOneInput(const unsigned char *data, size_t size) +LLVMFuzzerTestOneInput(const u_char *data, size_t size) { struct bufferevent *vpty[2]; struct window *w; @@ -46,7 +46,12 @@ LLVMFuzzerTestOneInput(const unsigned char *data, size_t size) wp->ictx = input_init(wp, vpty[0], NULL); window_add_ref(w, __func__); - input_parse_buffer(wp, (u_char*) data, size); + wp->fd = open("/dev/null", O_WRONLY); + if (wp->fd == -1) + errx(1, "open(\"/dev/null\") failed"); + wp->event = bufferevent_new(wp->fd, NULL, NULL, NULL, NULL); + + input_parse_buffer(wp, (u_char *)data, size); while (cmdq_next(NULL) != 0) ; error = event_base_loop(libevent, EVLOOP_NONBLOCK); @@ -84,6 +89,7 @@ LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv) options_set_number(global_w_options, "monitor-bell", 0); options_set_number(global_w_options, "allow-rename", 1); options_set_number(global_options, "set-clipboard", 2); + socket_path = xstrdup("dummy"); return 0; } From f2d4a1f02275e5b4f273a3228e0e9c099851dadd Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 16 Aug 2021 13:51:55 +0100 Subject: [PATCH 0916/1006] Needs fcntl.h. --- fuzz/input-fuzzer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzz/input-fuzzer.c b/fuzz/input-fuzzer.c index 0d80690b..049762b8 100644 --- a/fuzz/input-fuzzer.c +++ b/fuzz/input-fuzzer.c @@ -16,6 +16,7 @@ #include #include +#include #include "tmux.h" From 158f0e8c414e9525358189bd7135e12e6e6ada49 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Aug 2021 07:14:33 +0000 Subject: [PATCH 0917/1006] Start sync before drawing popup. --- popup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/popup.c b/popup.c index d4fc3833..33765d9b 100644 --- a/popup.c +++ b/popup.c @@ -554,6 +554,7 @@ popup_job_update_cb(struct job *job) if (size == 0) return; + tty_sync_start(&c->tty); if (pd->md != NULL) { c->overlay_check = menu_check_cb; c->overlay_data = pd->md; From 1a7eb6ca9006208b8b76c6a2a5a92c66648c03da Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Aug 2021 08:22:44 +0000 Subject: [PATCH 0918/1006] Revert previous; this is not how it should work. --- popup.c | 1 - 1 file changed, 1 deletion(-) diff --git a/popup.c b/popup.c index 33765d9b..d4fc3833 100644 --- a/popup.c +++ b/popup.c @@ -554,7 +554,6 @@ popup_job_update_cb(struct job *job) if (size == 0) return; - tty_sync_start(&c->tty); if (pd->md != NULL) { c->overlay_check = menu_check_cb; c->overlay_data = pd->md; From 41ababdf6c0529d4336a1daf3418ad314c373614 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Aug 2021 08:44:52 +0000 Subject: [PATCH 0919/1006] Be more sophisticated about enabling synchronized updates when there is an overlay and treat it like the active pane (use for commands which move the cursor only). When there is an overlay also use it for all panes and not just the active pane. GitHub issue 2826. --- screen-write.c | 17 ++++++++++++++--- tty.c | 17 +++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/screen-write.c b/screen-write.c index 01f8a097..c09d09ab 100644 --- a/screen-write.c +++ b/screen-write.c @@ -197,9 +197,20 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, } } - if (ctx->wp != NULL && - (~ctx->flags & SCREEN_WRITE_SYNC) && - (sync || ctx->wp != ctx->wp->window->active)) { + if (~ctx->flags & SCREEN_WRITE_SYNC) { + /* + * For the active pane or for an overlay (no pane), we want to + * only use synchronized updates if requested (commands that + * move the cursor); for other panes, always use it, since the + * cursor will have to move. + */ + if (ctx->wp != NULL) { + if (ctx->wp != ctx->wp->window->active) + ttyctx->num = 1; + else + ttyctx->num = sync; + } else + ttyctx->num = 0x10|sync; tty_write(tty_cmd_syncstart, ttyctx); ctx->flags |= SCREEN_WRITE_SYNC; } diff --git a/tty.c b/tty.c index 2a9f8b01..f93f6c57 100644 --- a/tty.c +++ b/tty.c @@ -2039,9 +2039,22 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) } void -tty_cmd_syncstart(struct tty *tty, __unused const struct tty_ctx *ctx) +tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx) { - tty_sync_start(tty); + if (ctx->num == 0x11) { + /* + * This is an overlay and a command that moves, the cursor so + * start synchronized updates. + */ + tty_sync_start(tty); + } else if (~ctx->num & 0x10) { + /* + * This is a pane. If there is an overlay, always start; + * otherwise, only if requested. + */ + if (ctx->num || tty->client->overlay_draw != NULL) + tty_sync_start(tty); + } } void From de9697b456c39b668ee387a17cd4abd3858e731a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Aug 2021 11:20:13 +0000 Subject: [PATCH 0920/1006] calloc for confirm-before data since the item needs to start NULL. --- cmd-confirm-before.c | 2 +- tty.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 6b754370..51c2fe8e 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -72,7 +72,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) free(copy); } - cdata = xmalloc(sizeof *cdata); + cdata = xcalloc(1, sizeof *cdata); cdata->cmd = xstrdup(args->argv[0]); cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); diff --git a/tty.c b/tty.c index f93f6c57..ba58aa2b 100644 --- a/tty.c +++ b/tty.c @@ -2043,7 +2043,7 @@ tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->num == 0x11) { /* - * This is an overlay and a command that moves, the cursor so + * This is an overlay and a command that moves the cursor so * start synchronized updates. */ tty_sync_start(tty); From 4f62aadc934b2331a654202ff22fc928e3279a5a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Aug 2021 16:19:00 +0000 Subject: [PATCH 0921/1006] Set the right session if detach-on-destroy is off. --- server-fn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-fn.c b/server-fn.c index 9b2f073b..b29bc15c 100644 --- a/server-fn.c +++ b/server-fn.c @@ -445,7 +445,7 @@ server_destroy_session(struct session *s) TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; - server_client_set_session(c, NULL); + server_client_set_session(c, s_new); if (s_new == NULL) c->flags |= CLIENT_EXIT; } From 2b0d798982d7c0cf6f2f7b00a6a3d98cd43e979e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Aug 2021 19:26:42 +0000 Subject: [PATCH 0922/1006] Do not block with incremental command prompt. --- cmd-command-prompt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 7e3d23c5..4943bc15 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -79,6 +79,8 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) if (tc->prompt_string != NULL) return (CMD_RETURN_NORMAL); + if (args_has(args, 'i')) + wait = 0; cdata = xcalloc(1, sizeof *cdata); cdata->idx = 1; From 66aaa9e48423c4699843e4a1dc52f1d27f73fe45 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Aug 2021 19:37:55 +0000 Subject: [PATCH 0923/1006] Fix pipe-pane usage. --- cmd-pipe-pane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index d9d9f436..0a518e1b 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -45,7 +45,7 @@ const struct cmd_entry cmd_pipe_pane_entry = { .alias = "pipep", .args = { "IOot:", 0, 1 }, - .usage = "[-IOo] " CMD_TARGET_PANE_USAGE " [command]", + .usage = "[-IOo] " CMD_TARGET_PANE_USAGE " [shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, From 5fdea440cede1690db9a242a091df72f16e53d24 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Aug 2021 20:17:21 +0000 Subject: [PATCH 0924/1006] Treat a pane that has died the same as no pane when sending data to control mode clients, GitHub issue 2828. --- control.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/control.c b/control.c index e86429cf..efcbc0ff 100644 --- a/control.c +++ b/control.c @@ -665,7 +665,7 @@ control_write_pending(struct client *c, struct control_pane *cp, size_t limit) uint64_t age, t = get_timer(); wp = control_window_pane(c, cp->pane); - if (wp == NULL) { + if (wp == NULL || wp->fd == -1) { TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { TAILQ_REMOVE(&cp->blocks, cb, entry); control_free_block(cs, cb); @@ -865,7 +865,7 @@ control_check_subs_pane(struct client *c, struct control_sub *csub) struct control_sub_pane *csp, find; wp = window_pane_find_by_id(csub->id); - if (wp == NULL) + if (wp == NULL || wp->fd == -1) return; w = wp->window; From 82836c739441d9bf33815b330a2c4cfe71d4616d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Aug 2021 10:15:08 +0000 Subject: [PATCH 0925/1006] Push the conversion of {} to string up out of the parser and into the command builder. --- cmd-parse.y | 267 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 189 insertions(+), 78 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 7b42c621..0388bd6d 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -42,13 +42,28 @@ struct cmd_parse_scope { TAILQ_ENTRY (cmd_parse_scope) entry; }; +enum cmd_parse_argument_type { + CMD_PARSE_STRING, + CMD_PARSE_COMMANDS +}; + +struct cmd_parse_argument { + enum cmd_parse_argument_type type; + char *string; + struct cmd_parse_commands *commands; + + TAILQ_ENTRY(cmd_parse_argument) entry; +}; +TAILQ_HEAD(cmd_parse_arguments, cmd_parse_argument); + struct cmd_parse_command { - u_int line; + u_int line; + struct cmd_parse_arguments arguments; - int argc; - char **argv; + int argc; + char **argv; - TAILQ_ENTRY(cmd_parse_command) entry; + TAILQ_ENTRY(cmd_parse_command) entry; }; TAILQ_HEAD(cmd_parse_commands, cmd_parse_command); @@ -80,16 +95,15 @@ static void cmd_parse_free_commands(struct cmd_parse_commands *); static char *cmd_parse_commands_to_string(struct cmd_parse_commands *); static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, struct cmd_list *); +static void cmd_parse_flatten_command(struct cmd_parse_command *); %} %union { char *token; - struct { - int argc; - char **argv; - } arguments; + struct cmd_parse_arguments *arguments; + struct cmd_parse_argument *argument; int flag; struct { int flag; @@ -107,8 +121,9 @@ static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, %token ENDIF %token FORMAT TOKEN EQUALS -%type argument expanded format +%type expanded format %type arguments +%type argument %type if_open if_elif %type elif elif1 %type argument_statements statements statement @@ -360,7 +375,7 @@ commands : command struct cmd_parse_state *ps = &parse_state; $$ = cmd_parse_new_commands(); - if ($1->argc != 0 && + if (!TAILQ_EMPTY(&$1->arguments) && (ps->scope == NULL || ps->scope->flag)) TAILQ_INSERT_TAIL($$, $1, entry); else @@ -380,7 +395,7 @@ commands : command { struct cmd_parse_state *ps = &parse_state; - if ($3->argc != 0 && + if (!TAILQ_EMPTY(&$3->arguments) && (ps->scope == NULL || ps->scope->flag)) { $$ = $1; TAILQ_INSERT_TAIL($$, $3, entry); @@ -401,27 +416,38 @@ command : assignment $$ = xcalloc(1, sizeof *$$); $$->line = ps->input->line; + TAILQ_INIT(&$$->arguments); } | optional_assignment TOKEN { - struct cmd_parse_state *ps = &parse_state; + struct cmd_parse_state *ps = &parse_state; + struct cmd_parse_argument *arg; $$ = xcalloc(1, sizeof *$$); $$->line = ps->input->line; + TAILQ_INIT(&$$->arguments); - cmd_prepend_argv(&$$->argc, &$$->argv, $2); - + arg = xcalloc(1, sizeof *arg); + arg->type = CMD_PARSE_STRING; + arg->string = xstrdup($2); + TAILQ_INSERT_HEAD(&$$->arguments, arg, entry); } | optional_assignment TOKEN arguments { - struct cmd_parse_state *ps = &parse_state; + struct cmd_parse_state *ps = &parse_state; + struct cmd_parse_argument *arg; $$ = xcalloc(1, sizeof *$$); $$->line = ps->input->line; + TAILQ_INIT(&$$->arguments); - $$->argc = $3.argc; - $$->argv = $3.argv; - cmd_prepend_argv(&$$->argc, &$$->argv, $2); + TAILQ_CONCAT(&$$->arguments, $3, entry); + free($3); + + arg = xcalloc(1, sizeof *arg); + arg->type = CMD_PARSE_STRING; + arg->string = xstrdup($2); + TAILQ_INSERT_HEAD(&$$->arguments, arg, entry); } condition1 : if_open commands if_close @@ -505,30 +531,34 @@ elif1 : if_elif commands arguments : argument { - $$.argc = 1; - $$.argv = xreallocarray(NULL, 1, sizeof *$$.argv); + $$ = xcalloc(1, sizeof *$$); + TAILQ_INIT($$); - $$.argv[0] = $1; + TAILQ_INSERT_HEAD($$, $1, entry); } | argument arguments { - cmd_prepend_argv(&$2.argc, &$2.argv, $1); - free($1); + TAILQ_INSERT_HEAD($2, $1, entry); $$ = $2; } argument : TOKEN { - $$ = $1; + $$ = xcalloc(1, sizeof *$$); + $$->type = CMD_PARSE_STRING; + $$->string = xstrdup($1); } | EQUALS { - $$ = $1; + $$ = xcalloc(1, sizeof *$$); + $$->type = CMD_PARSE_STRING; + $$->string = xstrdup($1); } | '{' argument_statements { - $$ = cmd_parse_commands_to_string($2); - cmd_parse_free_commands($2); + $$ = xcalloc(1, sizeof *$$); + $$->type = CMD_PARSE_COMMANDS; + $$->commands = $2; } argument_statements : statement '}' @@ -572,10 +602,30 @@ cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line, } } +static void +cmd_parse_free_arguments(struct cmd_parse_arguments *args) +{ + struct cmd_parse_argument *arg, *arg1; + + TAILQ_FOREACH_SAFE(arg, args, entry, arg1) { + switch (arg->type) { + case CMD_PARSE_STRING: + free(arg->string); + break; + case CMD_PARSE_COMMANDS: + cmd_parse_free_commands(arg->commands); + break; + } + TAILQ_REMOVE(args, arg, entry); + free(arg); + } +} + static void cmd_parse_free_command(struct cmd_parse_command *cmd) { cmd_free_argv(cmd->argc, cmd->argv); + cmd_parse_free_arguments(&cmd->arguments); free(cmd); } @@ -585,7 +635,7 @@ cmd_parse_new_commands(void) struct cmd_parse_commands *cmds; cmds = xmalloc(sizeof *cmds); - TAILQ_INIT (cmds); + TAILQ_INIT(cmds); return (cmds); } @@ -601,30 +651,6 @@ cmd_parse_free_commands(struct cmd_parse_commands *cmds) free(cmds); } -static char * -cmd_parse_commands_to_string(struct cmd_parse_commands *cmds) -{ - struct cmd_parse_command *cmd; - char *string = NULL, *s, *line; - - TAILQ_FOREACH(cmd, cmds, entry) { - line = cmd_stringify_argv(cmd->argc, cmd->argv); - if (string == NULL) - s = line; - else { - xasprintf(&s, "%s ; %s", s, line); - free(line); - } - - free(string); - string = s; - } - if (string == NULL) - string = xstrdup(""); - log_debug("%s: %s", __func__, string); - return (string); -} - static struct cmd_parse_commands * cmd_parse_run_parser(char **cause) { @@ -674,6 +700,84 @@ cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi, return (cmd_parse_run_parser(cause)); } +static void +cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix) +{ + struct cmd_parse_command *cmd; + struct cmd_parse_argument *arg; + u_int i, j; + char *s; + + i = 0; + TAILQ_FOREACH(cmd, cmds, entry) { + j = 0; + TAILQ_FOREACH(arg, &cmd->arguments, entry) { + switch (arg->type) { + case CMD_PARSE_STRING: + log_debug("%s %u:%u: %s", prefix, i, j, + arg->string); + break; + case CMD_PARSE_COMMANDS: + xasprintf(&s, "%s %u:%u", prefix, i, j); + cmd_parse_log_commands(arg->commands, s); + free(s); + break; + } + j++; + } + i++; + } +} + +static char * +cmd_parse_commands_to_string(struct cmd_parse_commands *cmds) +{ + struct cmd_parse_command *cmd; + char *string = NULL, *s, *line; + + TAILQ_FOREACH(cmd, cmds, entry) { + cmd_parse_flatten_command(cmd); + + line = cmd_stringify_argv(cmd->argc, cmd->argv); + if (string == NULL) + s = line; + else { + xasprintf(&s, "%s ; %s", s, line); + free(line); + } + + free(string); + string = s; + } + if (string == NULL) + string = xstrdup(""); + log_debug("%s: %s", __func__, string); + return (string); +} + +static void +cmd_parse_flatten_command(struct cmd_parse_command *cmd) +{ + struct cmd_parse_argument *arg; + char *s; + + cmd->argc = 0; + cmd->argv = NULL; + + TAILQ_FOREACH(arg, &cmd->arguments, entry) { + switch (arg->type) { + case CMD_PARSE_STRING: + cmd_append_argv(&cmd->argc, &cmd->argv, arg->string); + break; + case CMD_PARSE_COMMANDS: + s = cmd_parse_commands_to_string(arg->commands); + cmd_append_argv(&cmd->argc, &cmd->argv, s); + free(s); + break; + } + } +} + static struct cmd_parse_result * cmd_parse_build_commands(struct cmd_parse_commands *cmds, struct cmd_parse_input *pi) @@ -694,6 +798,11 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, return (&pr); } + /* Flatten command arguments. */ + cmd_parse_log_commands(cmds, __func__); + TAILQ_FOREACH(cmd, cmds, entry) + cmd_parse_flatten_command(cmd); + /* * Walk the commands and expand any aliases. Each alias is parsed * individually to a new command list, any trailing arguments appended @@ -918,12 +1027,35 @@ cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi) return (cmd_parse_build_commands(cmds, pi)); } +static void +cmd_parse_add_command(struct cmd_parse_commands *cmds, + struct cmd_parse_input *pi, int argc, char **argv) +{ + struct cmd_parse_command *cmd; + struct cmd_parse_argument *arg; + int i; + + cmd_log_argv(argc, argv, "%s", __func__); + + cmd = xcalloc(1, sizeof *cmd); + cmd->line = pi->line; + + TAILQ_INIT(&cmd->arguments); + for (i = 0; i < argc; i++) { + arg = xcalloc(1, sizeof *arg); + arg->type = CMD_PARSE_STRING; + arg->string = xstrdup(argv[i]); + TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); + } + + TAILQ_INSERT_TAIL(cmds, cmd, entry); +} + struct cmd_parse_result * cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) { struct cmd_parse_input input; struct cmd_parse_commands *cmds; - struct cmd_parse_command *cmd; char **copy, **new_argv; size_t size; int i, last, new_argc; @@ -958,37 +1090,16 @@ cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) if (size != 0) new_argc++; - if (new_argc != 0) { - cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__, - i); - - cmd = xcalloc(1, sizeof *cmd); - cmd->line = pi->line; - - cmd->argc = new_argc; - cmd->argv = cmd_copy_argv(new_argc, new_argv); - - TAILQ_INSERT_TAIL(cmds, cmd, entry); - } - + if (new_argc != 0) + cmd_parse_add_command(cmds, pi, new_argc, new_argv); last = i + 1; } if (last != argc) { new_argv = copy + last; new_argc = argc - last; - if (new_argc != 0) { - cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__, - last); - - cmd = xcalloc(1, sizeof *cmd); - cmd->line = pi->line; - - cmd->argc = new_argc; - cmd->argv = cmd_copy_argv(new_argc, new_argv); - - TAILQ_INSERT_TAIL(cmds, cmd, entry); - } + if (new_argc != 0) + cmd_parse_add_command(cmds, pi, new_argc, new_argv); } cmd_free_argv(argc, copy); From 5413a73ded8ee34e4565036c296c11b5e508ded5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Aug 2021 15:16:33 +0000 Subject: [PATCH 0926/1006] Need to flatten arguments for aliases. --- cmd-parse.y | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd-parse.y b/cmd-parse.y index 0388bd6d..9211c436 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -834,6 +834,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, cmd_parse_free_command(cmd); continue; } + cmd_parse_flatten_command(cmd2); for (i = 1; i < cmd->argc; i++) cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]); From 3177d7b61751c9adbd3da3de4c053a1498b43b93 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 06:30:57 +0000 Subject: [PATCH 0927/1006] Add a helper function for actually parsing the command. --- cmd-parse.y | 55 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 9211c436..f082b677 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -778,6 +778,23 @@ cmd_parse_flatten_command(struct cmd_parse_command *cmd) } } +static struct cmd * +cmd_parse_build_command(struct cmd_parse_command *cmd, + struct cmd_parse_input *pi, u_int line, struct cmd_parse_result *pr) +{ + struct cmd *add; + char *cause; + + add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause); + if (add == NULL) { + pr->status = CMD_PARSE_ERROR; + pr->error = cmd_parse_get_error(pi->file, line, cause); + free(cause); + return (NULL); + } + return (add); +} + static struct cmd_parse_result * cmd_parse_build_commands(struct cmd_parse_commands *cmds, struct cmd_parse_input *pi) @@ -787,7 +804,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, struct cmd_parse_command *cmd, *cmd2, *next, *next2, *after; u_int line = UINT_MAX; int i; - struct cmd_list *cmdlist = NULL, *result; + struct cmd_list *current = NULL, *result; struct cmd *add; char *name, *alias, *cause, *s; @@ -859,36 +876,30 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, */ result = cmd_list_new(); TAILQ_FOREACH(cmd, cmds, entry) { - name = cmd->argv[0]; - log_debug("%s: %u %s", __func__, cmd->line, name); - cmd_log_argv(cmd->argc, cmd->argv, __func__); - - if (cmdlist == NULL || - ((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) { - if (cmdlist != NULL) { - cmd_parse_print_commands(pi, line, cmdlist); - cmd_list_move(result, cmdlist); - cmd_list_free(cmdlist); + if (((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) { + if (current != NULL) { + cmd_parse_print_commands(pi, line, current); + cmd_list_move(result, current); + cmd_list_free(current); } - cmdlist = cmd_list_new(); + current = cmd_list_new(); } + if (current == NULL) + current = cmd_list_new(); line = cmd->line; - add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause); + add = cmd_parse_build_command(cmd, pi, line, &pr); if (add == NULL) { cmd_list_free(result); - pr.status = CMD_PARSE_ERROR; - pr.error = cmd_parse_get_error(pi->file, line, cause); - free(cause); - cmd_list_free(cmdlist); + cmd_list_free(current); goto out; } - cmd_list_append(cmdlist, add); + cmd_list_append(current, add); } - if (cmdlist != NULL) { - cmd_parse_print_commands(pi, line, cmdlist); - cmd_list_move(result, cmdlist); - cmd_list_free(cmdlist); + if (current != NULL) { + cmd_parse_print_commands(pi, line, current); + cmd_list_move(result, current); + cmd_list_free(current); } s = cmd_list_print(result, 0); From 3676779156dcef4d23b83f0a9f7930e3c8fd4263 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Aug 2021 11:20:22 +0100 Subject: [PATCH 0928/1006] Fix format test for new behaviour. --- regress/format-strings.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/regress/format-strings.sh b/regress/format-strings.sh index 726b46bc..0ae27386 100644 --- a/regress/format-strings.sh +++ b/regress/format-strings.sh @@ -125,14 +125,13 @@ test_conditional_with_pane_in_mode "#{?pane_in_mode,[abc,xyz],bonus}" "[abc" "xy test_conditional_with_pane_in_mode "#{?pane_in_mode,[abc#,xyz],bonus}" "[abc,xyz]" "bonus" -# Escape comma inside of #(...) -# Note: #() commands are run asynchronous and are substituted with result of the -# *previous* run or a placeholder (like "<'echo ,' not ready") if the command -# has not been run before. The format is updated as soon as the command -# finishes. As we are printing the message only once it never gets updated -# and the displayed message is "<'echo ,' not ready>" +# Escape comma inside of #(...) Note: #() commands are run asynchronous and are +# substituted with result of the *previous* run, an empty string if the command +# is new, or a placeholder after a few seconds. The format is updated as soon +# as the command finishes. As we are printing the message only once it never +# gets updated and the displayed message is empty. test_format "#{?pane_in_mode,#(echo #,),xyz}" "xyz" -test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo #,),xyz}" "<'echo ,' not ready>" "xyz" +test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo #,),xyz}" "" "xyz" # This caching does not work :-( #$TMUX display-message -p "#(echo #,)" > /dev/null #test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo #,),xyz}" "," "xyz" From 28d26fca355831c2af0ebe004d9c3fa396794473 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 20 Aug 2021 07:58:25 +0100 Subject: [PATCH 0929/1006] Update a regress conf for new syntax. --- ...8.conf => 872441a98b06444acc5ce08eb24aabde.conf} | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) rename regress/conf/{2e0f96ac3e1c144ce48261a4c9d68a48.conf => 872441a98b06444acc5ce08eb24aabde.conf} (68%) diff --git a/regress/conf/2e0f96ac3e1c144ce48261a4c9d68a48.conf b/regress/conf/872441a98b06444acc5ce08eb24aabde.conf similarity index 68% rename from regress/conf/2e0f96ac3e1c144ce48261a4c9d68a48.conf rename to regress/conf/872441a98b06444acc5ce08eb24aabde.conf index df4c2b1c..b074ad76 100644 --- a/regress/conf/2e0f96ac3e1c144ce48261a4c9d68a48.conf +++ b/regress/conf/872441a98b06444acc5ce08eb24aabde.conf @@ -14,12 +14,13 @@ bind C { copy-mode -s'{last}' } +set -g word-separators "" bind -n C-DoubleClick1Pane if -F '#{m/r:^[^:]*:[0-9]+:,#{mouse_word}}' { - popup -w90% -h90% -KE -d '#{pane_current_path}' -R { - emacs `echo #{mouse_word}|awk -F: '{print "+" $2,$1}'` - } + run -C { popup -w90% -h90% -E -d '#{pane_current_path}' ' + emacs `echo #{mouse_word}|awk -F: "{print \"+\"\\$2,\\$1}"` + ' } } { - popup -w90% -h90% -KE -d '#{pane_current_path}' -R { - emacs "#{mouse_word}" - } + run -C { popup -w90% -h90% -E -d '#{pane_current_path}' ' + emacs "#{mouse_word}" + ' } } From f984446d196b5a542cc6935b22c20607b8a07ff3 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 09:06:26 +0000 Subject: [PATCH 0930/1006] Actually parse contents of {} as a command and then convert to a string instead of just copying arguments around as strings. --- cmd-parse.y | 271 ++++++++++++++++++++++++---------------------------- 1 file changed, 126 insertions(+), 145 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index f082b677..86cb357e 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -60,9 +60,6 @@ struct cmd_parse_command { u_int line; struct cmd_parse_arguments arguments; - int argc; - char **argv; - TAILQ_ENTRY(cmd_parse_command) entry; }; TAILQ_HEAD(cmd_parse_commands, cmd_parse_command); @@ -92,10 +89,10 @@ static char *cmd_parse_get_error(const char *, u_int, const char *); static void cmd_parse_free_command(struct cmd_parse_command *); static struct cmd_parse_commands *cmd_parse_new_commands(void); static void cmd_parse_free_commands(struct cmd_parse_commands *); -static char *cmd_parse_commands_to_string(struct cmd_parse_commands *); -static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, +static void cmd_parse_build_commands(struct cmd_parse_commands *, + struct cmd_parse_input *, struct cmd_parse_result *); +static void cmd_parse_print_commands(struct cmd_parse_input *, struct cmd_list *); -static void cmd_parse_flatten_command(struct cmd_parse_command *); %} @@ -587,19 +584,32 @@ cmd_parse_get_error(const char *file, u_int line, const char *error) } static void -cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line, - struct cmd_list *cmdlist) +cmd_parse_print_commands(struct cmd_parse_input *pi, struct cmd_list *cmdlist) { char *s; - if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) { - s = cmd_list_print(cmdlist, 0); - if (pi->file != NULL) - cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s); - else - cmdq_print(pi->item, "%u: %s", line, s); - free(s); + if (pi->item == NULL || (~pi->flags & CMD_PARSE_VERBOSE)) + return; + s = cmd_list_print(cmdlist, 0); + if (pi->file != NULL) + cmdq_print(pi->item, "%s:%u: %s", pi->file, pi->line, s); + else + cmdq_print(pi->item, "%u: %s", pi->line, s); + free(s); +} + +static void +cmd_parse_free_argument(struct cmd_parse_argument *arg) +{ + switch (arg->type) { + case CMD_PARSE_STRING: + free(arg->string); + break; + case CMD_PARSE_COMMANDS: + cmd_parse_free_commands(arg->commands); + break; } + free(arg); } static void @@ -608,23 +618,14 @@ cmd_parse_free_arguments(struct cmd_parse_arguments *args) struct cmd_parse_argument *arg, *arg1; TAILQ_FOREACH_SAFE(arg, args, entry, arg1) { - switch (arg->type) { - case CMD_PARSE_STRING: - free(arg->string); - break; - case CMD_PARSE_COMMANDS: - cmd_parse_free_commands(arg->commands); - break; - } TAILQ_REMOVE(args, arg, entry); - free(arg); + cmd_parse_free_argument(arg); } } static void cmd_parse_free_command(struct cmd_parse_command *cmd) { - cmd_free_argv(cmd->argc, cmd->argv); cmd_parse_free_arguments(&cmd->arguments); free(cmd); } @@ -729,144 +730,120 @@ cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix) } } -static char * -cmd_parse_commands_to_string(struct cmd_parse_commands *cmds) +static int +cmd_parse_expand_alias(struct cmd_parse_command *cmd, + struct cmd_parse_input *pi, struct cmd_parse_result *pr, + struct cmd_list **cmdlist) { - struct cmd_parse_command *cmd; - char *string = NULL, *s, *line; + struct cmd_parse_argument *arg, *arg1, *first, *after; + struct cmd_parse_commands *cmds; + struct cmd_parse_command *last; + char *alias, *name, *cause; - TAILQ_FOREACH(cmd, cmds, entry) { - cmd_parse_flatten_command(cmd); + *cmdlist = NULL; - line = cmd_stringify_argv(cmd->argc, cmd->argv); - if (string == NULL) - s = line; - else { - xasprintf(&s, "%s ; %s", s, line); - free(line); - } - - free(string); - string = s; + first = TAILQ_FIRST(&cmd->arguments); + if (first == NULL || first->type != CMD_PARSE_STRING) { + pr->status = CMD_PARSE_EMPTY; + return (1); } - if (string == NULL) - string = xstrdup(""); - log_debug("%s: %s", __func__, string); - return (string); + name = first->string; + + alias = cmd_get_alias(name); + if (alias == NULL) + return (0); + log_debug("%s: %u alias %s = %s", __func__, pi->line, name, alias); + + cmds = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause); + free(alias); + if (cmds == NULL) { + pr->status = CMD_PARSE_ERROR; + pr->error = cause; + return (1); + } + + last = TAILQ_LAST(cmds, cmd_parse_commands); + if (last == NULL) { + *cmdlist = cmd_list_new(); + return (1); + } + + TAILQ_REMOVE(&cmd->arguments, first, entry); + cmd_parse_free_argument(first); + + after = TAILQ_NEXT(TAILQ_FIRST(&last->arguments), entry); + TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) { + TAILQ_REMOVE(&cmd->arguments, arg, entry); + if (after == NULL) + TAILQ_INSERT_TAIL(&last->arguments, arg, entry); + else + TAILQ_INSERT_AFTER(&last->arguments, after, arg, entry); + after = arg; + } + cmd_parse_log_commands(cmds, __func__); + + cmd_parse_build_commands(cmds, pi, pr); + if (pr->status != CMD_PARSE_SUCCESS) + *cmdlist = pr->cmdlist; + return (1); } -static void -cmd_parse_flatten_command(struct cmd_parse_command *cmd) +static struct cmd_list * +cmd_parse_build_command(struct cmd_parse_command *cmd, + struct cmd_parse_input *pi, struct cmd_parse_result *pr) { struct cmd_parse_argument *arg; - char *s; + struct cmd_list *cmdlist; + struct cmd *add; + char *s, **argv = NULL, *cause; + int argc = 0; - cmd->argc = 0; - cmd->argv = NULL; + if (cmd_parse_expand_alias(cmd, pi, pr, &cmdlist)) + return (cmdlist); TAILQ_FOREACH(arg, &cmd->arguments, entry) { switch (arg->type) { case CMD_PARSE_STRING: - cmd_append_argv(&cmd->argc, &cmd->argv, arg->string); + cmd_append_argv(&argc, &argv, arg->string); break; case CMD_PARSE_COMMANDS: - s = cmd_parse_commands_to_string(arg->commands); - cmd_append_argv(&cmd->argc, &cmd->argv, s); + cmd_parse_build_commands(arg->commands, pi, pr); + if (pr->status != CMD_PARSE_SUCCESS) + return (NULL); + s = cmd_list_print(pr->cmdlist, 0); + cmd_append_argv(&argc, &argv, s); free(s); break; } } -} -static struct cmd * -cmd_parse_build_command(struct cmd_parse_command *cmd, - struct cmd_parse_input *pi, u_int line, struct cmd_parse_result *pr) -{ - struct cmd *add; - char *cause; - - add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause); + add = cmd_parse(argc, argv, pi->file, pi->line, &cause); if (add == NULL) { pr->status = CMD_PARSE_ERROR; - pr->error = cmd_parse_get_error(pi->file, line, cause); + pr->error = cmd_parse_get_error(pi->file, pi->line, cause); free(cause); return (NULL); } - return (add); + cmdlist = cmd_list_new(); + cmd_list_append(cmdlist, add); + return (cmdlist); } -static struct cmd_parse_result * +static void cmd_parse_build_commands(struct cmd_parse_commands *cmds, - struct cmd_parse_input *pi) + struct cmd_parse_input *pi, struct cmd_parse_result *pr) { - static struct cmd_parse_result pr; - struct cmd_parse_commands *cmds2; - struct cmd_parse_command *cmd, *cmd2, *next, *next2, *after; + struct cmd_parse_command *cmd; u_int line = UINT_MAX; - int i; - struct cmd_list *current = NULL, *result; - struct cmd *add; - char *name, *alias, *cause, *s; + struct cmd_list *current = NULL, *result, *add; + char *s; /* Check for an empty list. */ if (TAILQ_EMPTY(cmds)) { - cmd_parse_free_commands(cmds); - pr.status = CMD_PARSE_EMPTY; - return (&pr); - } - - /* Flatten command arguments. */ - cmd_parse_log_commands(cmds, __func__); - TAILQ_FOREACH(cmd, cmds, entry) - cmd_parse_flatten_command(cmd); - - /* - * Walk the commands and expand any aliases. Each alias is parsed - * individually to a new command list, any trailing arguments appended - * to the last command, and all commands inserted into the original - * command list. - */ - TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) { - name = cmd->argv[0]; - - alias = cmd_get_alias(name); - if (alias == NULL) - continue; - - line = cmd->line; - log_debug("%s: %u %s = %s", __func__, line, name, alias); - - pi->line = line; - cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause); - free(alias); - if (cmds2 == NULL) { - pr.status = CMD_PARSE_ERROR; - pr.error = cause; - goto out; - } - - cmd2 = TAILQ_LAST(cmds2, cmd_parse_commands); - if (cmd2 == NULL) { - TAILQ_REMOVE(cmds, cmd, entry); - cmd_parse_free_command(cmd); - continue; - } - cmd_parse_flatten_command(cmd2); - for (i = 1; i < cmd->argc; i++) - cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]); - - after = cmd; - TAILQ_FOREACH_SAFE(cmd2, cmds2, entry, next2) { - cmd2->line = line; - TAILQ_REMOVE(cmds2, cmd2, entry); - TAILQ_INSERT_AFTER(cmds, after, cmd2, entry); - after = cmd2; - } - cmd_parse_free_commands(cmds2); - - TAILQ_REMOVE(cmds, cmd, entry); - cmd_parse_free_command(cmd); + pr->status = CMD_PARSE_EMPTY; + return; } + cmd_parse_log_commands(cmds, __func__); /* * Parse each command into a command list. Create a new command list @@ -878,7 +855,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, TAILQ_FOREACH(cmd, cmds, entry) { if (((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) { if (current != NULL) { - cmd_parse_print_commands(pi, line, current); + cmd_parse_print_commands(pi, current); cmd_list_move(result, current); cmd_list_free(current); } @@ -886,18 +863,19 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, } if (current == NULL) current = cmd_list_new(); - line = cmd->line; + line = pi->line = cmd->line; - add = cmd_parse_build_command(cmd, pi, line, &pr); + add = cmd_parse_build_command(cmd, pi, pr); if (add == NULL) { cmd_list_free(result); cmd_list_free(current); - goto out; + return; } - cmd_list_append(current, add); + cmd_list_move(current, add); + cmd_list_free(add); } if (current != NULL) { - cmd_parse_print_commands(pi, line, current); + cmd_parse_print_commands(pi, current); cmd_list_move(result, current); cmd_list_free(current); } @@ -906,13 +884,8 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, log_debug("%s: %s", __func__, s); free(s); - pr.status = CMD_PARSE_SUCCESS; - pr.cmdlist = result; - -out: - cmd_parse_free_commands(cmds); - - return (&pr); + pr->status = CMD_PARSE_SUCCESS; + pr->cmdlist = result; } struct cmd_parse_result * @@ -935,7 +908,10 @@ cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi) pr.error = cause; return (&pr); } - return (cmd_parse_build_commands(cmds, pi)); + cmd_parse_build_commands(cmds, pi, &pr); + cmd_parse_free_commands(cmds); + return (&pr); + } struct cmd_parse_result * @@ -1036,7 +1012,9 @@ cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi) pr.error = cause; return (&pr); } - return (cmd_parse_build_commands(cmds, pi)); + cmd_parse_build_commands(cmds, pi, &pr); + cmd_parse_free_commands(cmds); + return (&pr); } static void @@ -1066,6 +1044,7 @@ cmd_parse_add_command(struct cmd_parse_commands *cmds, struct cmd_parse_result * cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) { + static struct cmd_parse_result pr; struct cmd_parse_input input; struct cmd_parse_commands *cmds; char **copy, **new_argv; @@ -1115,7 +1094,9 @@ cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) } cmd_free_argv(argc, copy); - return (cmd_parse_build_commands(cmds, pi)); + cmd_parse_build_commands(cmds, pi, &pr); + cmd_parse_free_commands(cmds); + return (&pr); } static int printflike(1, 2) From 5ed5b11b45bfa2e2a40569e818284695408469cc Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 20 Aug 2021 14:33:57 +0100 Subject: [PATCH 0931/1006] Add zeraphel complex binding to regress. --- .../f4f1cdb9d518c2f7808a4915299f2524.conf | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 regress/conf/f4f1cdb9d518c2f7808a4915299f2524.conf diff --git a/regress/conf/f4f1cdb9d518c2f7808a4915299f2524.conf b/regress/conf/f4f1cdb9d518c2f7808a4915299f2524.conf new file mode 100644 index 00000000..de01b812 --- /dev/null +++ b/regress/conf/f4f1cdb9d518c2f7808a4915299f2524.conf @@ -0,0 +1,47 @@ +bind m-4 run -C '#{@layout-vertical-two}' + +set -g @layout-vertical-two { + selectl main-vertical + if -F '#{==:#{@vertical-two-active},true}' { + set -wu @vertical-two-active + } { + if -F '#{&&:#{==:#{N/s:layout_overflow},0},#{e|>=:#{n:#{P:x}},3}}' { + run -C '#{@layout-vertical-two-init}' + } + } +} + +set -g @layout-vertical-two-init { + set -gF @total_panes '#{n:#{P:x}}' + set -gF @cur_window '#S:#I' + new -ds layout_overflow + run -C '\ + swapw -t layout_overflow: -s . ;\ + splitw -fh -l 40% -t #{@cur_window} ;\ + splitw -h -t #{@cur_window}.2 ;\ + swapp -s #{@cur_window}.1 -t layout_overflow:1.1 ; killp -t layout_overflow:1.1 ;\ + swapp -s #{@cur_window}.2 -t layout_overflow:1.1 ; killp -t layout_overflow:1.1 ;\ + swapp -s #{@cur_window}.3 -t layout_overflow:1.1 ; killp -t layout_overflow:1.1 ;\ + #{@layout-vertical-two-loop}' +} + +set -g @layout-vertical-two-cleanup { + set -gu @cur_window + set -gu @total_panes '#{n:#{P:x}}' + set -w @vertical-two-active true + selectp -t .1 +} + +# (x - 1) % 2 == 0 ? (x - 1) / 2 + 1 : x +# #{?#{==:#{e|%:#{e|-:#{cur_panes},1},2},0} <-- TODO: inserting horizontally shuffles windows. +# ,#{e|+:#{e|/:#{e|-:#{cur_panes},1},2},1} <-- end of first column +# ,#{cur_panes}} <-- end of second column +set -g @layout-vertical-two-loop { + # count(panes) < count(original.panes) + if -F '#{e|<:#{n:#{P:x}},#{@total_panes}}' { + run -C "joinp -s layout_overflow:1.1 -vt '#{@cur_window}.#{?#{==:#{e|%:#{e|-:#{#{n:#{P:x}}},1},2},0},#{e|+:#{e|/:#{e|-:#{#{n:#{P:x}}},1},2},1},#{#{n:#{P:x}}}}' ;\ + selectl -E ; #{@layout-vertical-two-loop}" + } { + run -C '#{@layout-vertical-two-cleanup}' + } +} From 41822ef782122be42305c0c21e2f166077d09002 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 20 Aug 2021 18:20:49 +0100 Subject: [PATCH 0932/1006] Regress fixes. --- regress/control-client-sanity.sh | 3 +-- regress/osc-11colours.sh | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/regress/control-client-sanity.sh b/regress/control-client-sanity.sh index bf76b4d5..691d3d87 100644 --- a/regress/control-client-sanity.sh +++ b/regress/control-client-sanity.sh @@ -8,8 +8,7 @@ TMUX="$TEST_TMUX -Ltest" $TMUX kill-server 2>/dev/null TMP=$(mktemp) -OUT=$(mktemp) -trap "rm -f $TMP $OUT" 0 1 15 +trap "rm -f $TMP" 0 1 15 $TMUX -f/dev/null new -d -x200 -y200 || exit 1 $TMUX -f/dev/null splitw || exit 1 diff --git a/regress/osc-11colours.sh b/regress/osc-11colours.sh index a0dd605d..a049a49a 100644 --- a/regress/osc-11colours.sh +++ b/regress/osc-11colours.sh @@ -7,11 +7,12 @@ TERM=screen TMUX="$TEST_TMUX -Ltest" $TMUX kill-server 2>/dev/null -$TMUX -vv new -d +$TMUX new -d $TMUX set -g remain-on-exit on do_test() { $TMUX splitw "printf '$1'" + sleep 0.25 c="$($TMUX display -p '#{pane_bg}')" $TMUX kill-pane [ "$c" != "$2" ] && return 1 From 01b13de655cae6cd619bf872d16f82600ff6081e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 17:36:03 +0000 Subject: [PATCH 0933/1006] Fill colour palette correctly from option for new panes, GitHub issue 2831. --- window.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/window.c b/window.c index 446898cb..584dbf9c 100644 --- a/window.c +++ b/window.c @@ -924,6 +924,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->pipe_fd = -1; colour_palette_init(&wp->palette); + colour_palette_from_option(&wp->palette, wp->options); + screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; From e463e8622dff28394145868a124b10101afc2269 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 17:50:42 +0000 Subject: [PATCH 0934/1006] Remove stray spaces after function names. --- cmd-list-keys.c | 5 ++--- format.c | 7 +++---- input-keys.c | 2 +- mode-tree.c | 2 +- names.c | 2 +- screen.c | 2 +- server-client.c | 2 +- status.c | 4 ++-- window-copy.c | 6 +++--- 9 files changed, 15 insertions(+), 17 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 91715f93..dbb510fb 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -211,7 +211,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) repeat = 0; tablewidth = keywidth = 0; - table = key_bindings_first_table (); + table = key_bindings_first_table(); while (table != NULL) { if (tablename != NULL && strcmp(table->name, tablename) != 0) { table = key_bindings_next_table(table); @@ -243,8 +243,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) tmpsize = 256; tmp = xmalloc(tmpsize); - - table = key_bindings_first_table (); + table = key_bindings_first_table(); while (table != NULL) { if (tablename != NULL && strcmp(table->name, tablename) != 0) { table = key_bindings_next_table(table); diff --git a/format.c b/format.c index 9ef4e6e9..1cf852c5 100644 --- a/format.c +++ b/format.c @@ -4078,8 +4078,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 's': if (fm->argc < 2) break; - sub = xreallocarray (sub, nsub + 1, - sizeof *sub); + sub = xreallocarray(sub, nsub + 1, sizeof *sub); sub[nsub++] = fm; break; case '=': @@ -4188,7 +4187,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, value = xstrdup(""); else xasprintf(&value, "%c", c); - free (new); + free(new); goto done; } @@ -4752,7 +4751,7 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s, if (wp != NULL) format_defaults_pane(ft, wp); - pb = paste_get_top (NULL); + pb = paste_get_top(NULL); if (pb != NULL) format_defaults_paste_buffer(ft, pb); } diff --git a/input-keys.c b/input-keys.c index d6f48e6c..c2f2fe19 100644 --- a/input-keys.c +++ b/input-keys.c @@ -334,7 +334,7 @@ input_key_cmp(struct input_key_entry *ike1, struct input_key_entry *ike2) /* Look for key in tree. */ static struct input_key_entry * -input_key_get (key_code key) +input_key_get(key_code key) { struct input_key_entry entry = { .key = key }; diff --git a/mode-tree.c b/mode-tree.c index 807c1dcb..c92f7cff 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -922,7 +922,7 @@ mode_tree_menu_callback(__unused struct menu *menu, __unused u_int idx, if (mti->itemdata != mtm->itemdata) goto out; mtd->current = mtm->line; - mtd->menucb (mtd->modedata, mtm->c, key); + mtd->menucb(mtd->modedata, mtm->c, key); out: mode_tree_remove_ref(mtd); diff --git a/names.c b/names.c index 09b33082..aeb67338 100644 --- a/names.c +++ b/names.c @@ -146,7 +146,7 @@ parse_window_name(const char *in) name = copy = xstrdup(in); if (*name == '"') name++; - name[strcspn (name, "\"")] = '\0'; + name[strcspn(name, "\"")] = '\0'; if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) name = name + (sizeof "exec ") - 1; diff --git a/screen.c b/screen.c index e7ee1df9..a7801f45 100644 --- a/screen.c +++ b/screen.c @@ -698,6 +698,6 @@ screen_mode_to_string(int mode) strlcat(tmp, "CRLF,", sizeof tmp); if (mode & MODE_KEXTENDED) strlcat(tmp, "KEXTENDED,", sizeof tmp); - tmp[strlen (tmp) - 1] = '\0'; + tmp[strlen(tmp) - 1] = '\0'; return (tmp); } diff --git a/server-client.c b/server-client.c index 08584e7b..f957e0b1 100644 --- a/server-client.c +++ b/server-client.c @@ -2378,7 +2378,7 @@ server_client_set_flags(struct client *c, const char *flags) uint64_t flag; int not; - s = copy = xstrdup (flags); + s = copy = xstrdup(flags); while ((next = strsep(&s, ",")) != NULL) { not = (*next == '!'); if (not) diff --git a/status.c b/status.c index 853f46d3..b442e85d 100644 --- a/status.c +++ b/status.c @@ -1413,7 +1413,7 @@ process_key: break; if (c->prompt_buffer[0].size == 0) { prefix = '='; - free (c->prompt_buffer); + free(c->prompt_buffer); c->prompt_buffer = utf8_fromcstr(c->prompt_last); c->prompt_index = utf8_strlen(c->prompt_buffer); } else @@ -1424,7 +1424,7 @@ process_key: break; if (c->prompt_buffer[0].size == 0) { prefix = '='; - free (c->prompt_buffer); + free(c->prompt_buffer); c->prompt_buffer = utf8_fromcstr(c->prompt_last); c->prompt_index = utf8_strlen(c->prompt_buffer); } else diff --git a/window-copy.c b/window-copy.c index 951d7581..c3b1a66b 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2226,7 +2226,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->timeout = 0; - log_debug ("%s: %s", __func__, argument); + log_debug("%s: %s", __func__, argument); prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { @@ -2281,7 +2281,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->timeout = 0; - log_debug ("%s: %s", __func__, argument); + log_debug("%s: %s", __func__, argument); prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { @@ -2859,7 +2859,7 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, args->argc - 1 > window_copy_cmd_table[i].maxargs) break; clear = window_copy_cmd_table[i].clear; - action = window_copy_cmd_table[i].f (&cs); + action = window_copy_cmd_table[i].f(&cs); break; } } From 6cbc83c6a64f272575aac1ee32cc0a0b1cb75c7b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 17:53:54 +0000 Subject: [PATCH 0935/1006] Add a way to create an empty arguments set. --- arguments.c | 14 ++++++++++++-- cmd-find-window.c | 4 ++-- tmux.h | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/arguments.c b/arguments.c index dd92a952..91141b99 100644 --- a/arguments.c +++ b/arguments.c @@ -64,6 +64,17 @@ args_find(struct args *args, u_char flag) return (RB_FIND(args_tree, &args->tree, &entry)); } +/* Create an empty arguments set. */ +struct args * +args_create(void) +{ + struct args *args; + + args = xcalloc(1, sizeof *args); + RB_INIT(&args->tree); + return (args); +} + /* Parse an argv and argc into a new argument set. */ struct args * args_parse(const char *template, int argc, char **argv) @@ -71,12 +82,11 @@ args_parse(const char *template, int argc, char **argv) struct args *args; int opt; - args = xcalloc(1, sizeof *args); - optreset = 1; optind = 1; optarg = NULL; + args = args_create(); while ((opt = getopt(argc, argv, template)) != -1) { if (opt < 0) continue; diff --git a/cmd-find-window.c b/cmd-find-window.c index 4cd155e4..43f5f9be 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -48,7 +48,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *target = cmdq_get_target(item); struct window_pane *wp = target->wp; const char *s = args->argv[0], *suffix = ""; - char *filter, *argv = { NULL }; + char *filter; int C, N, T; C = args_has(args, 'C'); @@ -91,7 +91,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) else xasprintf(&filter, "#{m%s:*%s*,#{pane_title}}", suffix, s); - new_args = args_parse("", 1, &argv); + new_args = args_create(); if (args_has(args, 'Z')) args_set(new_args, 'Z', NULL); args_set(new_args, 'f', filter); diff --git a/tmux.h b/tmux.h index e6ba8e86..26f9dc3e 100644 --- a/tmux.h +++ b/tmux.h @@ -2182,6 +2182,7 @@ int tty_keys_next(struct tty *); /* arguments.c */ void args_set(struct args *, u_char, const char *); +struct args *args_create(void); struct args *args_parse(const char *, int, char **); void args_free(struct args *); char *args_print(struct args *); From 90dd474c3ea8bf10bde6ee15eaaf9f3af3fb46f6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 18:59:53 +0000 Subject: [PATCH 0936/1006] Expose args_value struct (will be needed soon) and add some missing frees. --- arguments.c | 25 ++++++------------------- cmd-new-session.c | 12 ++++++------ cmd-new-window.c | 13 +++++++------ cmd-queue.c | 16 ++++++++-------- cmd-refresh-client.c | 16 ++++++++-------- cmd-respawn-pane.c | 12 ++++++------ cmd-respawn-window.c | 12 ++++++------ cmd-split-window.c | 14 ++++++++------ tmux.h | 13 +++++++++---- 9 files changed, 64 insertions(+), 69 deletions(-) diff --git a/arguments.c b/arguments.c index 91141b99..142f86d8 100644 --- a/arguments.c +++ b/arguments.c @@ -29,10 +29,6 @@ * Manipulate command arguments. */ -struct args_value { - char *value; - TAILQ_ENTRY(args_value) entry; -}; TAILQ_HEAD(args_values, args_value); struct args_entry { @@ -335,30 +331,21 @@ args_next(struct args_entry **entry) } /* Get first value in argument. */ -const char * -args_first_value(struct args *args, u_char flag, struct args_value **value) +struct args_value * +args_first_value(struct args *args, u_char flag) { struct args_entry *entry; if ((entry = args_find(args, flag)) == NULL) return (NULL); - - *value = TAILQ_FIRST(&entry->values); - if (*value == NULL) - return (NULL); - return ((*value)->value); + return (TAILQ_FIRST(&entry->values)); } /* Get next value in argument. */ -const char * -args_next_value(struct args_value **value) +struct args_value * +args_next_value(struct args_value *value) { - if (*value == NULL) - return (NULL); - *value = TAILQ_NEXT(*value, entry); - if (*value == NULL) - return (NULL); - return ((*value)->value); + return (TAILQ_NEXT(value, entry)); } /* Convert an argument value to a number. */ diff --git a/cmd-new-session.c b/cmd-new-session.c index f3a5de26..79b756bf 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -75,7 +75,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct options *oo; struct termios tio, *tiop; struct session_group *sg = NULL; - const char *errstr, *template, *group, *tmp, *add; + const char *errstr, *template, *group, *tmp; char *cause, *cwd = NULL, *cp, *newname = NULL; char *name, *prefix = NULL; int detached, already_attached, is_control = 0; @@ -83,7 +83,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct spawn_context sc; enum cmd_retval retval; struct cmd_find_state fs; - struct args_value *value; + struct args_value *av; if (cmd_get_entry(self) == &cmd_has_session_entry) { /* @@ -269,10 +269,10 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) env = environ_create(); if (c != NULL && !args_has(args, 'E')) environ_update(global_s_options, c->environ, env); - add = args_first_value(args, 'e', &value); - while (add != NULL) { - environ_put(env, add, 0); - add = args_next_value(&value); + av = args_first_value(args, 'e'); + while (av != NULL) { + environ_put(env, av->value, 0); + av = args_next_value(av); } s = session_create(prefix, newname, cwd, env, oo, tiop); diff --git a/cmd-new-window.c b/cmd-new-window.c index 712e2a79..9393f6e4 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -62,9 +62,9 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) int idx = target->idx, before; struct winlink *new_wl = NULL; char *cause = NULL, *cp; - const char *template, *add, *name; + const char *template, *name; struct cmd_find_state fs; - struct args_value *value; + struct args_value *av; /* * If -S and -n are given and -t is not and a single window with this @@ -112,10 +112,10 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) sc.argv = args->argv; sc.environ = environ_create(); - add = args_first_value(args, 'e', &value); - while (add != NULL) { - environ_put(sc.environ, add, 0); - add = args_next_value(&value); + av = args_first_value(args, 'e'); + while (av != NULL) { + environ_put(sc.environ, av->value, 0); + av = args_next_value(av); } sc.idx = idx; @@ -130,6 +130,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if ((new_wl = spawn_window(&sc, &cause)) == NULL) { cmdq_error(item, "create window failed: %s", cause); free(cause); + environ_free(sc.environ); return (CMD_RETURN_ERROR); } if (!args_has(args, 'd') || new_wl == s->curw) { diff --git a/cmd-queue.c b/cmd-queue.c index 6dd5f9dd..198a2a43 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -352,8 +352,8 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, struct cmdq_state *state = item->state; struct cmd *cmd = item->cmd; struct args *args = cmd_get_args(cmd); - struct args_entry *entryp; - struct args_value *valuep; + struct args_entry *ae; + struct args_value *av; struct options *oo; va_list ap; char *name, tmp[32], flag, *arguments; @@ -398,7 +398,7 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i); cmdq_add_format(new_state, tmp, "%s", args->argv[i]); } - flag = args_first(args, &entryp); + flag = args_first(args, &ae); while (flag != 0) { value = args_get(args, flag); if (value == NULL) { @@ -410,15 +410,15 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, } i = 0; - value = args_first_value(args, flag, &valuep); - while (value != NULL) { + av = args_first_value(args, flag); + while (av != NULL) { xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i); - cmdq_add_format(new_state, tmp, "%s", value); + cmdq_add_format(new_state, tmp, "%s", av->value); i++; - value = args_next_value(&valuep); + av = args_next_value(av); } - flag = args_next(&entryp); + flag = args_next(&ae); } a = options_array_first(o); diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index e55ce907..2ade072f 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -184,20 +184,20 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'A')) { if (~tc->flags & CLIENT_CONTROL) goto not_control_client; - value = args_first_value(args, 'A', &av); - while (value != NULL) { - cmd_refresh_client_update_offset(tc, value); - value = args_next_value(&av); + av = args_first_value(args, 'A'); + while (av != NULL) { + cmd_refresh_client_update_offset(tc, av->value); + av = args_next_value(&av); } return (CMD_RETURN_NORMAL); } if (args_has(args, 'B')) { if (~tc->flags & CLIENT_CONTROL) goto not_control_client; - value = args_first_value(args, 'B', &av); - while (value != NULL) { - cmd_refresh_client_update_subscription(tc, value); - value = args_next_value(&av); + av = args_first_value(args, 'B'); + while (av != NULL) { + cmd_refresh_client_update_subscription(tc, av); + av = args_next_value(av); } return (CMD_RETURN_NORMAL); } diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 9db280b4..8d938c5e 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -54,8 +54,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) struct winlink *wl = target->wl; struct window_pane *wp = target->wp; char *cause = NULL; - const char *add; - struct args_value *value; + struct args_value *av; memset(&sc, 0, sizeof sc); sc.item = item; @@ -70,10 +69,10 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) sc.argv = args->argv; sc.environ = environ_create(); - add = args_first_value(args, 'e', &value); - while (add != NULL) { - environ_put(sc.environ, add, 0); - add = args_next_value(&value); + av = args_first_value(args, 'e'); + while (av != NULL) { + environ_put(sc.environ, av->value, 0); + av = args_next_value(av); } sc.idx = -1; @@ -86,6 +85,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) if (spawn_pane(&sc, &cause) == NULL) { cmdq_error(item, "respawn pane failed: %s", cause); free(cause); + environ_free(sc.environ); return (CMD_RETURN_ERROR); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 39d19ddb..da3ecee4 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -54,8 +54,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) struct session *s = target->s; struct winlink *wl = target->wl; char *cause = NULL; - const char *add; - struct args_value *value; + struct args_value *av; memset(&sc, 0, sizeof sc); sc.item = item; @@ -68,10 +67,10 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) sc.argv = args->argv; sc.environ = environ_create(); - add = args_first_value(args, 'e', &value); - while (add != NULL) { - environ_put(sc.environ, add, 0); - add = args_next_value(&value); + av = args_first_value(args, 'e'); + while (av != NULL) { + environ_put(sc.environ, av->value, 0); + av = args_next_value(av); } sc.idx = -1; @@ -84,6 +83,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) if (spawn_window(&sc, &cause) == NULL) { cmdq_error(item, "respawn window failed: %s", cause); free(cause); + environ_free(sc.environ); return (CMD_RETURN_ERROR); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 9e27bba1..ae36561f 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -65,10 +65,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct layout_cell *lc; struct cmd_find_state fs; int size, percentage, flags, input; - const char *template, *add, *errstr, *p; + const char *template, *errstr, *p; char *cause, *cp, *copy; size_t plen; - struct args_value *value; + struct args_value *av; if (args_has(args, 'h')) type = LAYOUT_LEFTRIGHT; @@ -141,10 +141,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) sc.argv = args->argv; sc.environ = environ_create(); - add = args_first_value(args, 'e', &value); - while (add != NULL) { - environ_put(sc.environ, add, 0); - add = args_next_value(&value); + av = args_first_value(args, 'e'); + while (av != NULL) { + environ_put(sc.environ, av->value, 0); + av = args_next_value(av); } sc.idx = -1; @@ -159,6 +159,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { cmdq_error(item, "create pane failed: %s", cause); free(cause); + environ_free(sc.environ); return (CMD_RETURN_ERROR); } if (input && window_pane_start_input(new_wp, item, &cause) != 0) { @@ -167,6 +168,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) window_remove_pane(wp->window, new_wp); cmdq_error(item, "%s", cause); free(cause); + environ_free(sc.environ); return (CMD_RETURN_ERROR); } if (!args_has(args, 'd')) diff --git a/tmux.h b/tmux.h index 26f9dc3e..63ad9e86 100644 --- a/tmux.h +++ b/tmux.h @@ -37,7 +37,6 @@ extern char **environ; struct args; -struct args_value; struct client; struct cmd; struct cmd_find_state; @@ -1356,7 +1355,13 @@ struct message_entry { }; TAILQ_HEAD(message_list, message_entry); -/* Parsed arguments structures. */ +/* Argument value. */ +struct args_value { + char *value; + TAILQ_ENTRY(args_value) entry; +}; + +/* Arguments set. */ struct args_entry; RB_HEAD(args_tree, args_entry); struct args { @@ -2191,8 +2196,8 @@ int args_has(struct args *, u_char); const char *args_get(struct args *, u_char); u_char args_first(struct args *, struct args_entry **); u_char args_next(struct args_entry **); -const char *args_first_value(struct args *, u_char, struct args_value **); -const char *args_next_value(struct args_value **); +struct args_value *args_first_value(struct args *, u_char); +struct args_value *args_next_value(struct args_value *); long long args_strtonum(struct args *, u_char, long long, long long, char **); long long args_percentage(struct args *, u_char, long long, From c76b28de242696a78a256dda3b42ba49d607a751 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 19:08:36 +0000 Subject: [PATCH 0937/1006] Remove some unnecessary blank lines. --- cmd-new-window.c | 1 - server-client.c | 2 -- window-copy.c | 1 - 3 files changed, 4 deletions(-) diff --git a/cmd-new-window.c b/cmd-new-window.c index 9393f6e4..a9156a6f 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -94,7 +94,6 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) } } - before = args_has(args, 'b'); if (args_has(args, 'a') || before) { idx = winlink_shuffle_up(s, wl, before); diff --git a/server-client.c b/server-client.c index f957e0b1..87c0f554 100644 --- a/server-client.c +++ b/server-client.c @@ -309,7 +309,6 @@ server_client_attached_lost(struct client *c) } } - /* Set client session. */ void server_client_set_session(struct client *c, struct session *s) @@ -1554,7 +1553,6 @@ server_client_check_pane_resize(struct window_pane *wp) evtimer_add(&wp->resize_timer, &tv); } - /* Check pane buffer size. */ static void server_client_check_pane_buffer(struct window_pane *wp) diff --git a/window-copy.c b/window-copy.c index c3b1a66b..7c342d90 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1885,7 +1885,6 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) struct window_copy_mode_data *data = wme->data; u_int px, py, nextx, nexty; - data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; data->selflag = SEL_WORD; From de94a344f61b0e4ef6459c11621be3c3d1683c9e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 19:34:51 +0000 Subject: [PATCH 0938/1006] Add a couple of const and fix some warnings. --- cmd-refresh-client.c | 6 +++--- cmd.c | 4 ++-- tmux.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 2ade072f..22efb38b 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -117,7 +117,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct tty *tty = &tc->tty; struct window *w; - const char *size, *errstr, *value; + const char *size, *errstr; u_int x, y, adjust; struct args_value *av; @@ -187,7 +187,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) av = args_first_value(args, 'A'); while (av != NULL) { cmd_refresh_client_update_offset(tc, av->value); - av = args_next_value(&av); + av = args_next_value(av); } return (CMD_RETURN_NORMAL); } @@ -196,7 +196,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) goto not_control_client; av = args_first_value(args, 'B'); while (av != NULL) { - cmd_refresh_client_update_subscription(tc, av); + cmd_refresh_client_update_subscription(tc, av->value); av = args_next_value(av); } return (CMD_RETURN_NORMAL); diff --git a/cmd.c b/cmd.c index 90fa221a..a26c523b 100644 --- a/cmd.c +++ b/cmd.c @@ -252,7 +252,7 @@ cmd_log_argv(int argc, char **argv, const char *fmt, ...) /* Prepend to an argument vector. */ void -cmd_prepend_argv(int *argc, char ***argv, char *arg) +cmd_prepend_argv(int *argc, char ***argv, const char *arg) { char **new_argv; int i; @@ -269,7 +269,7 @@ cmd_prepend_argv(int *argc, char ***argv, char *arg) /* Append to an argument vector. */ void -cmd_append_argv(int *argc, char ***argv, char *arg) +cmd_append_argv(int *argc, char ***argv, const char *arg) { *argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv); (*argv)[(*argc)++] = xstrdup(arg); diff --git a/tmux.h b/tmux.h index 63ad9e86..ae0ffc24 100644 --- a/tmux.h +++ b/tmux.h @@ -2236,8 +2236,8 @@ int cmd_find_from_nothing(struct cmd_find_state *, int); /* cmd.c */ extern const struct cmd_entry *cmd_table[]; void printflike(3, 4) cmd_log_argv(int, char **, const char *, ...); -void cmd_prepend_argv(int *, char ***, char *); -void cmd_append_argv(int *, char ***, char *); +void cmd_prepend_argv(int *, char ***, const char *); +void cmd_append_argv(int *, char ***, const char *); int cmd_pack_argv(int, char **, char *, size_t); int cmd_unpack_argv(char *, size_t, int, char ***); char **cmd_copy_argv(int, char **); From 5f32b7d9613e9ef3f8198302379a42630323da6a Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 19:50:16 +0000 Subject: [PATCH 0939/1006] Hide struct args behind a couple of accessor functions. --- arguments.c | 62 ++++++++++----- cmd-bind-key.c | 20 +++-- cmd-command-prompt.c | 33 ++++---- cmd-confirm-before.c | 12 +-- cmd-display-menu.c | 52 +++++++------ cmd-display-message.c | 13 ++-- cmd-display-panes.c | 4 +- cmd-find-window.c | 2 +- cmd-if-shell.c | 21 +++-- cmd-list-keys.c | 17 ++--- cmd-load-buffer.c | 2 +- cmd-new-session.c | 14 ++-- cmd-new-window.c | 13 ++-- cmd-pipe-pane.c | 4 +- cmd-queue.c | 6 +- cmd-refresh-client.c | 5 +- cmd-rename-session.c | 2 +- cmd-rename-window.c | 2 +- cmd-resize-pane.c | 5 +- cmd-resize-window.c | 5 +- cmd-respawn-pane.c | 10 ++- cmd-respawn-window.c | 10 ++- cmd-run-shell.c | 7 +- cmd-save-buffer.c | 2 +- cmd-select-layout.c | 18 ++--- cmd-send-keys.c | 10 +-- cmd-set-buffer.c | 6 +- cmd-set-environment.c | 19 ++--- cmd-set-option.c | 20 +++-- cmd-show-environment.c | 8 +- cmd-show-options.c | 4 +- cmd-source-file.c | 20 ++--- cmd-split-window.c | 17 +++-- cmd-unbind-key.c | 10 +-- cmd-wait-for.c | 8 +- cmd.c | 7 +- tmux.h | 10 +-- window-buffer.c | 4 +- window-client.c | 4 +- window-copy.c | 169 +++++++++++++++++++++-------------------- window-tree.c | 4 +- 41 files changed, 355 insertions(+), 306 deletions(-) diff --git a/arguments.c b/arguments.c index 142f86d8..0867f815 100644 --- a/arguments.c +++ b/arguments.c @@ -38,6 +38,12 @@ struct args_entry { RB_ENTRY(args_entry) entry; }; +struct args { + struct args_tree tree; + int argc; + char **argv; +}; + static struct args_entry *args_find(struct args *, u_char); static int args_cmp(struct args_entry *, struct args_entry *); @@ -73,7 +79,7 @@ args_create(void) /* Parse an argv and argc into a new argument set. */ struct args * -args_parse(const char *template, int argc, char **argv) +args_parse(const char *template, int argc, char **argv, int lower, int upper) { struct args *args; int opt; @@ -99,6 +105,10 @@ args_parse(const char *template, int argc, char **argv) args->argc = argc; args->argv = cmd_copy_argv(argc, argv); + if ((lower != -1 && argc < lower) || (upper != -1 && argc > upper)) { + args_free(args); + return (NULL); + } return (args); } @@ -126,6 +136,14 @@ args_free(struct args *args) free(args); } +/* Convert arguments to vector. */ +void +args_vector(struct args *args, int *argc, char ***argv) +{ + *argc = args->argc; + *argv = cmd_copy_argv(args->argc, args->argv); +} + /* Add to string. */ static void printflike(3, 4) args_print_add(char **buf, size_t *len, const char *fmt, ...) @@ -145,23 +163,6 @@ args_print_add(char **buf, size_t *len, const char *fmt, ...) free(s); } -/* Add value to string. */ -static void -args_print_add_value(char **buf, size_t *len, struct args_entry *entry, - struct args_value *value) -{ - char *escaped; - - if (**buf != '\0') - args_print_add(buf, len, " -%c ", entry->flag); - else - args_print_add(buf, len, "-%c ", entry->flag); - - escaped = args_escape(value->value); - args_print_add(buf, len, "%s", escaped); - free(escaped); -} - /* Add argument to string. */ static void args_print_add_argument(char **buf, size_t *len, const char *argument) @@ -203,8 +204,13 @@ args_print(struct args *args) /* Then the flags with arguments. */ RB_FOREACH(entry, args_tree, &args->tree) { - TAILQ_FOREACH(value, &entry->values, entry) - args_print_add_value(&buf, &len, entry, value); + TAILQ_FOREACH(value, &entry->values, entry) { + if (*buf != '\0') + args_print_add(&buf, &len, " -%c", entry->flag); + else + args_print_add(&buf, &len, "-%c", entry->flag); + args_print_add_argument(&buf, &len, value->value); + } } /* And finally the argument vector. */ @@ -330,6 +336,22 @@ args_next(struct args_entry **entry) return ((*entry)->flag); } +/* Get argument count. */ +u_int +args_count(struct args *args) +{ + return (args->argc); +} + +/* Return argument as string. */ +const char * +args_string(struct args *args, u_int idx) +{ + if (idx >= (u_int)args->argc) + return (NULL); + return (args->argv[idx]); +} + /* Get first value in argument. */ struct args_value * args_first_value(struct args *args, u_char flag) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index b4e4167c..87dd3cf7 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -48,12 +48,13 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) key_code key; const char *tablename, *note = args_get(args, 'N'); struct cmd_parse_result *pr; - char **argv = args->argv; - int argc = args->argc, repeat; + char **argv; + int argc, repeat; + u_int count = args_count(args); - key = key_string_lookup_string(argv[0]); + key = key_string_lookup_string(args_string(args, 0)); if (key == KEYC_NONE || key == KEYC_UNKNOWN) { - cmdq_error(item, "unknown key: %s", argv[0]); + cmdq_error(item, "unknown key: %s", args_string(args, 0)); return (CMD_RETURN_ERROR); } @@ -65,11 +66,14 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) tablename = "prefix"; repeat = args_has(args, 'r'); - if (argc != 1) { - if (argc == 2) - pr = cmd_parse_from_string(argv[1], NULL); - else + if (count != 1) { + if (count == 2) + pr = cmd_parse_from_string(args_string(args, 1), NULL); + else { + args_vector(args, &argc, &argv); pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL); + cmd_free_argv(argc, argv); + } switch (pr->status) { case CMD_PARSE_EMPTY: cmdq_error(item, "empty command"); diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 4943bc15..25ec6817 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -71,9 +71,10 @@ cmd_command_prompt_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_find_state *target = cmdq_get_target(item); - const char *inputs, *prompts, *type; + const char *inputs, *prompts, *type, *s; struct cmd_command_prompt_cdata *cdata; - char *prompt, *ptr, *input = NULL; + char *prompt, *comma, *input = NULL; + u_int count = args_count(args); size_t n; int wait = !args_has(args, 'b'); @@ -94,28 +95,30 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) if (wait) cdata->item = item; - if (args->argc != 0 && args_has(args, 'F')) - cdata->template = format_single_from_target(item, args->argv[0]); - else if (args->argc != 0) - cdata->template = xstrdup(args->argv[0]); - else + if (count != 0) { + s = args_string(args, 0); + if (args_has(args, 'F')) + cdata->template = format_single_from_target(item, s); + else + cdata->template = xstrdup(s); + } else cdata->template = xstrdup("%1"); if ((prompts = args_get(args, 'p')) != NULL) cdata->prompts = xstrdup(prompts); - else if (args->argc != 0) { + else if (count != 0) { n = strcspn(cdata->template, " ,"); - xasprintf(&cdata->prompts, "(%.*s) ", (int) n, cdata->template); + xasprintf(&cdata->prompts, "(%.*s) ", (int)n, cdata->template); } else cdata->prompts = xstrdup(":"); /* Get first prompt. */ cdata->next_prompt = cdata->prompts; - ptr = strsep(&cdata->next_prompt, ","); + comma = strsep(&cdata->next_prompt, ","); if (prompts == NULL) - prompt = xstrdup(ptr); + prompt = xstrdup(comma); else - xasprintf(&prompt, "%s ", ptr); + xasprintf(&prompt, "%s ", comma); /* Get initial prompt input. */ if ((inputs = args_get(args, 'I')) != NULL) { @@ -157,7 +160,7 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, int done) { struct cmd_command_prompt_cdata *cdata = data; - char *new_template, *prompt, *ptr, *error; + char *new_template, *prompt, *comma, *error; char *input = NULL; struct cmdq_item *item = cdata->item; enum cmd_parse_status status; @@ -177,8 +180,8 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, * Check if there are more prompts; if so, get its respective input * and update the prompt data. */ - if (done && (ptr = strsep(&cdata->next_prompt, ",")) != NULL) { - xasprintf(&prompt, "%s ", ptr); + if (done && (comma = strsep(&cdata->next_prompt, ",")) != NULL) { + xasprintf(&prompt, "%s ", comma); input = strsep(&cdata->next_input, ","); status_prompt_update(c, prompt, input); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 51c2fe8e..9f179aaf 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -59,22 +59,22 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) struct cmd_confirm_before_data *cdata; struct client *tc = cmdq_get_target_client(item); struct cmd_find_state *target = cmdq_get_target(item); - char *cmd, *copy, *new_prompt, *ptr; + char *cmd, *copy, *new_prompt, *tmp; const char *prompt; int wait = !args_has(args, 'b'); + cdata = xcalloc(1, sizeof *cdata); + cdata->cmd = xstrdup(args_string(args, 0)); + if ((prompt = args_get(args, 'p')) != NULL) xasprintf(&new_prompt, "%s ", prompt); else { - ptr = copy = xstrdup(args->argv[0]); - cmd = strsep(&ptr, " \t"); + tmp = copy = xstrdup(cdata->cmd); + cmd = strsep(&tmp, " \t"); xasprintf(&new_prompt, "Confirm '%s'? (y/n) ", cmd); free(copy); } - cdata = xcalloc(1, sizeof *cdata); - cdata->cmd = xstrdup(args->argv[0]); - cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); if (wait) cdata->pi.item = item; diff --git a/cmd-display-menu.c b/cmd-display-menu.c index c774284c..a8eda0d2 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -261,10 +261,10 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct menu *menu = NULL; struct menu_item menu_item; - const char *key; - char *title, *name; - int flags = 0, i; - u_int px, py; + const char *key, *name; + char *title; + int flags = 0; + u_int px, py, i, count = args_count(args); if (tc->overlay_draw != NULL) return (CMD_RETURN_NORMAL); @@ -275,24 +275,24 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) title = xstrdup(""); menu = menu_create(title); - for (i = 0; i != args->argc; /* nothing */) { - name = args->argv[i++]; + for (i = 0; i != count; /* nothing */) { + name = args_string(args, i++); if (*name == '\0') { menu_add_item(menu, NULL, item, tc, target); continue; } - if (args->argc - i < 2) { + if (count - i < 2) { cmdq_error(item, "not enough arguments"); free(title); menu_free(menu); return (CMD_RETURN_ERROR); } - key = args->argv[i++]; + key = args_string(args, i++); menu_item.name = name; menu_item.key = key_string_lookup_string(key); - menu_item.command = args->argv[i++]; + menu_item.command = args_string(args, i++); menu_add_item(menu, &menu_item, item, tc, target); } @@ -329,11 +329,10 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) struct session *s = target->s; struct client *tc = cmdq_get_target_client(item); struct tty *tty = &tc->tty; - const char *value, *shell[] = { NULL, NULL }; - const char *shellcmd = NULL; - char *cwd, *cause, **argv = args->argv; - int flags = 0, argc = args->argc; - u_int px, py, w, h; + const char *value, *shell, *shellcmd = NULL; + char *cwd, *cause, **argv = NULL; + int flags = 0, argc = 0; + u_int px, py, w, h, count = args_count(args); if (args_has(args, 'C')) { server_client_clear_overlay(tc); @@ -374,18 +373,18 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) cwd = format_single_from_target(item, value); else cwd = xstrdup(server_client_get_cwd(tc, s)); - if (argc == 0) + if (count == 0) shellcmd = options_get_string(s->options, "default-command"); - else if (argc == 1) - shellcmd = argv[0]; - if (argc <= 1 && (shellcmd == NULL || *shellcmd == '\0')) { + else if (count == 1) + shellcmd = args_string(args, 0); + if (count <= 1 && (shellcmd == NULL || *shellcmd == '\0')) { shellcmd = NULL; - shell[0] = options_get_string(s->options, "default-shell"); - if (!checkshell(shell[0])) - shell[0] = _PATH_BSHELL; - argc = 1; - argv = (char**)shell; - } + shell = options_get_string(s->options, "default-shell"); + if (!checkshell(shell)) + shell = _PATH_BSHELL; + cmd_append_argv(&argc, &argv, shell); + } else + args_vector(args, &argc, &argv); if (args_has(args, 'E') > 1) flags |= POPUP_CLOSEEXITZERO; @@ -394,7 +393,10 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'B')) flags |= POPUP_NOBORDER; if (popup_display(flags, item, px, py, w, h, shellcmd, argc, argv, cwd, - tc, s, NULL, NULL) != 0) + tc, s, NULL, NULL) != 0) { + cmd_free_argv(argc, argv); return (CMD_RETURN_NORMAL); + } + cmd_free_argv(argc, argv); return (CMD_RETURN_WAIT); } diff --git a/cmd-display-message.c b/cmd-display-message.c index 8fd6a8ff..f4d41e6c 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -68,9 +68,9 @@ 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; + int delay = -1, flags; struct format_tree *ft; - int flags; + u_int count = args_count(args); if (args_has(args, 'I')) { if (wp == NULL) @@ -83,7 +83,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_WAIT); } - if (args_has(args, 'F') && args->argc != 0) { + if (args_has(args, 'F') && count != 0) { cmdq_error(item, "only one of -F or argument must be given"); return (CMD_RETURN_ERROR); } @@ -97,9 +97,10 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) } } - template = args_get(args, 'F'); - if (args->argc != 0) - template = args->argv[0]; + if (count != 0) + template = args_string(args, 0); + else + template = args_get(args, 'F'); if (template == NULL) template = DISPLAY_MESSAGE_TEMPLATE; diff --git a/cmd-display-panes.c b/cmd-display-panes.c index beadae53..59484872 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -276,8 +276,8 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) delay = options_get_number(s->options, "display-panes-time"); cdata = xmalloc(sizeof *cdata); - if (args->argc != 0) - cdata->command = xstrdup(args->argv[0]); + if (args_count(args)) + cdata->command = xstrdup(args_string(args, 0)); else cdata->command = xstrdup("select-pane -t '%%'"); if (args_has(args, 'b')) diff --git a/cmd-find-window.c b/cmd-find-window.c index 43f5f9be..691baf85 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -47,7 +47,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self), *new_args; struct cmd_find_state *target = cmdq_get_target(item); struct window_pane *wp = target->wp; - const char *s = args->argv[0], *suffix = ""; + const char *s = args_string(args, 0), *suffix = ""; char *filter; int C, N, T; diff --git a/cmd-if-shell.c b/cmd-if-shell.c index f4c81074..df06a0b6 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -65,21 +65,20 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *target = cmdq_get_target(item); struct cmdq_state *state = cmdq_get_state(item); struct cmd_if_shell_data *cdata; - char *shellcmd, *cmd, *error; - const char *file; + char *shellcmd, *error; + const char *cmd = NULL, *file; struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct cmd_parse_input pi; enum cmd_parse_status status; + u_int count = args_count(args); - shellcmd = format_single_from_target(item, args->argv[0]); + shellcmd = format_single_from_target(item, args_string(args, 0)); if (args_has(args, 'F')) { if (*shellcmd != '0' && *shellcmd != '\0') - cmd = args->argv[1]; - else if (args->argc == 3) - cmd = args->argv[2]; - else - cmd = NULL; + cmd = args_string(args, 1); + else if (count == 3) + cmd = args_string(args, 2); free(shellcmd); if (cmd == NULL) return (CMD_RETURN_NORMAL); @@ -101,9 +100,9 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata = xcalloc(1, sizeof *cdata); - cdata->cmd_if = xstrdup(args->argv[1]); - if (args->argc == 3) - cdata->cmd_else = xstrdup(args->argv[2]); + cdata->cmd_if = xstrdup(args_string(args, 1)); + if (count == 3) + cdata->cmd_else = xstrdup(args_string(args, 2)); if (!args_has(args, 'b')) cdata->client = cmdq_get_client(item); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index dbb510fb..1484af6d 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -150,7 +150,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct key_table *table; struct key_binding *bd; - const char *tablename, *r; + const char *tablename, *r, *keystr; char *key, *cp, *tmp, *start, *empty; key_code prefix, only = KEYC_UNKNOWN; int repeat, width, tablewidth, keywidth, found = 0; @@ -159,10 +159,10 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) if (cmd_get_entry(self) == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, item)); - if (args->argc != 0) { - only = key_string_lookup_string(args->argv[0]); + if ((keystr = args_string(args, 0)) != NULL) { + only = key_string_lookup_string(keystr); if (only == KEYC_UNKNOWN) { - cmdq_error(item, "invalid key: %s", args->argv[0]); + cmdq_error(item, "invalid key: %s", keystr); return (CMD_RETURN_ERROR); } only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS); @@ -243,6 +243,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) tmpsize = 256; tmp = xmalloc(tmpsize); + table = key_bindings_first_table(); while (table != NULL) { if (tablename != NULL && strcmp(table->name, tablename) != 0) { @@ -307,7 +308,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) out: if (only != KEYC_UNKNOWN && !found) { - cmdq_error(item, "unknown key: %s", args->argv[0]); + cmdq_error(item, "unknown key: %s", args_string(args, 0)); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); @@ -320,12 +321,9 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) const struct cmd_entry **entryp; const struct cmd_entry *entry; struct format_tree *ft; - const char *template, *s, *command = NULL; + const char *template, *s, *command; char *line; - if (args->argc != 0) - command = args->argv[0]; - if ((template = args_get(args, 'F')) == NULL) { template = "#{command_list_name}" "#{?command_list_alias, (#{command_list_alias}),} " @@ -335,6 +333,7 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_defaults(ft, NULL, NULL, NULL, NULL); + command = args_string(args, 0); for (entryp = cmd_table; *entryp != NULL; entryp++) { entry = *entryp; if (command != NULL && diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index bca9a860..318a7467 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -105,7 +105,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) cdata->client->references++; } - path = format_single_from_target(item, args->argv[0]); + path = format_single_from_target(item, args_string(args, 0)); file_read(cmdq_get_client(item), path, cmd_load_buffer_done, cdata); free(path); diff --git a/cmd-new-session.c b/cmd-new-session.c index 79b756bf..0cc6b9da 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -79,8 +79,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) char *cause, *cwd = NULL, *cp, *newname = NULL; char *name, *prefix = NULL; int detached, already_attached, is_control = 0; - u_int sx, sy, dsx, dsy; - struct spawn_context sc; + u_int sx, sy, dsx, dsy, count = args_count(args); + struct spawn_context sc = { 0 }; enum cmd_retval retval; struct cmd_find_state fs; struct args_value *av; @@ -93,7 +93,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) { + if (args_has(args, 't') && (count != 0 || args_has(args, 'n'))) { cmdq_error(item, "command or window name given with target"); return (CMD_RETURN_ERROR); } @@ -277,15 +277,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) s = session_create(prefix, newname, cwd, env, oo, tiop); /* Spawn the initial window. */ - memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; if (!detached) sc.tc = c; sc.name = args_get(args, 'n'); - sc.argc = args->argc; - sc.argv = args->argv; + args_vector(args, &sc.argc, &sc.argv); sc.idx = -1; sc.cwd = args_get(args, 'c'); @@ -358,12 +356,16 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) cmd_find_from_session(&fs, s, 0); cmdq_insert_hook(s, item, &fs, "after-new-session"); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); free(cwd); free(newname); free(prefix); return (CMD_RETURN_NORMAL); fail: + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); free(cwd); free(newname); free(prefix); diff --git a/cmd-new-window.c b/cmd-new-window.c index a9156a6f..f24de8e9 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -55,12 +55,11 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) struct client *c = cmdq_get_client(item); struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); - struct spawn_context sc; + struct spawn_context sc = { 0 }; struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; - struct winlink *wl = target->wl; + struct winlink *wl = target->wl, *new_wl = NULL; int idx = target->idx, before; - struct winlink *new_wl = NULL; char *cause = NULL, *cp; const char *template, *name; struct cmd_find_state fs; @@ -101,14 +100,12 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) idx = target->idx; } - memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; sc.tc = tc; sc.name = args_get(args, 'n'); - sc.argc = args->argc; - sc.argv = args->argv; + args_vector(args, &sc.argc, &sc.argv); sc.environ = environ_create(); av = args_first_value(args, 'e'); @@ -129,6 +126,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if ((new_wl = spawn_window(&sc, &cause)) == NULL) { cmdq_error(item, "create window failed: %s", cause); free(cause); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); environ_free(sc.environ); return (CMD_RETURN_ERROR); } @@ -150,6 +149,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) cmd_find_from_winlink(&fs, new_wl, 0); cmdq_insert_hook(s, item, &fs, "after-new-window"); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); environ_free(sc.environ); return (CMD_RETURN_NORMAL); } diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 0a518e1b..d1917bc6 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -82,7 +82,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) } /* If no pipe command, that is enough. */ - if (args->argc == 0 || *args->argv[0] == '\0') + if (args_count(args) == 0 || *args_string(args, 0) == '\0') return (CMD_RETURN_NORMAL); /* @@ -112,7 +112,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) /* Expand the command. */ ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_defaults(ft, tc, s, wl, wp); - cmd = format_expand_time(ft, args->argv[0]); + cmd = format_expand_time(ft, args_string(args, 0)); format_free(ft); /* Fork the child. */ diff --git a/cmd-queue.c b/cmd-queue.c index 198a2a43..687d037f 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -357,7 +357,7 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, struct options *oo; va_list ap; char *name, tmp[32], flag, *arguments; - int i; + u_int i; const char *value; struct cmdq_item *new_item; struct cmdq_state *new_state; @@ -394,9 +394,9 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, cmdq_add_format(new_state, "hook_arguments", "%s", arguments); free(arguments); - for (i = 0; i < args->argc; i++) { + for (i = 0; i < args_count(args); i++) { xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i); - cmdq_add_format(new_state, tmp, "%s", args->argv[i]); + cmdq_add_format(new_state, tmp, "%s", args_string(args, i)); } flag = args_first(args, &ae); while (flag != 0) { diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 22efb38b..93845024 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -127,10 +127,11 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) args_has(args, 'U') || args_has(args, 'D')) { - if (args->argc == 0) + if (args_count(args) == 0) adjust = 1; else { - adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); + adjust = strtonum(args_string(args, 0), 1, INT_MAX, + &errstr); if (errstr != NULL) { cmdq_error(item, "adjustment %s", errstr); return (CMD_RETURN_ERROR); diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 49fafe33..8ec070bf 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -51,7 +51,7 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) struct session *s = target->s; char *newname, *tmp; - tmp = format_single_from_target(item, args->argv[0]); + tmp = format_single_from_target(item, args_string(args, 0)); newname = session_check_name(tmp); if (newname == NULL) { cmdq_error(item, "invalid session: %s", tmp); diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 593e0b9e..66c119f2 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -50,7 +50,7 @@ cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item) struct winlink *wl = target->wl; char *newname; - newname = format_single_from_target(item, args->argv[0]); + newname = format_single_from_target(item, args_string(args, 0)); window_set_name(wl->window, newname); options_set_number(wl->window->options, "automatic-rename", 0); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 94e060d0..98d500db 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -95,10 +95,11 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) } server_unzoom_window(w); - if (args->argc == 0) + if (args_count(args) == 0) adjust = 1; else { - adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); + adjust = strtonum(args_string(args, 0), 1, INT_MAX, + &errstr); if (errstr != NULL) { cmdq_error(item, "adjustment %s", errstr); return (CMD_RETURN_ERROR); diff --git a/cmd-resize-window.c b/cmd-resize-window.c index 1ebb7aca..3e33f771 100644 --- a/cmd-resize-window.c +++ b/cmd-resize-window.c @@ -56,10 +56,11 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) u_int adjust, sx, sy; int xpixel = -1, ypixel = -1; - if (args->argc == 0) + if (args_count(args) == 0) adjust = 1; else { - adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); + adjust = strtonum(args_string(args, 0), 1, INT_MAX, + &errstr); if (errstr != NULL) { cmdq_error(item, "adjustment %s", errstr); return (CMD_RETURN_ERROR); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 8d938c5e..14f9abf2 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -49,14 +49,13 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct spawn_context sc; + struct spawn_context sc = { 0 }; struct session *s = target->s; struct winlink *wl = target->wl; struct window_pane *wp = target->wp; char *cause = NULL; struct args_value *av; - memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; sc.wl = wl; @@ -65,8 +64,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) sc.lc = NULL; sc.name = NULL; - sc.argc = args->argc; - sc.argv = args->argv; + args_vector(args, &sc.argc, &sc.argv); sc.environ = environ_create(); av = args_first_value(args, 'e'); @@ -85,6 +83,8 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) if (spawn_pane(&sc, &cause) == NULL) { cmdq_error(item, "respawn pane failed: %s", cause); free(cause); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); environ_free(sc.environ); return (CMD_RETURN_ERROR); } @@ -93,6 +93,8 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) server_redraw_window_borders(wp->window); server_status_window(wp->window); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); environ_free(sc.environ); return (CMD_RETURN_NORMAL); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index da3ecee4..25288bad 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -49,22 +49,20 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct spawn_context sc; + struct spawn_context sc = { 0 }; struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct winlink *wl = target->wl; char *cause = NULL; struct args_value *av; - memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; sc.wl = wl; sc.tc = tc; sc.name = NULL; - sc.argc = args->argc; - sc.argv = args->argv; + args_vector(args, &sc.argc, &sc.argv); sc.environ = environ_create(); av = args_first_value(args, 'e'); @@ -83,12 +81,16 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) if (spawn_window(&sc, &cause) == NULL) { cmdq_error(item, "respawn window failed: %s", cause); free(cause); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); environ_free(sc.environ); return (CMD_RETURN_ERROR); } server_redraw_window(wl->window); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); environ_free(sc.environ); return (CMD_RETURN_NORMAL); } diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 7bc1d7cc..a0115f0a 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -104,6 +104,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) double d; struct timeval tv; char *end; + const char *cmd = args_string(args, 0); int wait = !args_has(args, 'b'); if ((delay = args_get(args, 'd')) != NULL) { @@ -112,12 +113,12 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "invalid delay time: %s", delay); return (CMD_RETURN_ERROR); } - } else if (args->argc == 0) + } else if (args_count(args) == 0) return (CMD_RETURN_NORMAL); cdata = xcalloc(1, sizeof *cdata); - if (args->argc != 0) - cdata->cmd = format_single_from_target(item, args->argv[0]); + if (cmd != NULL) + cdata->cmd = format_single_from_target(item, cmd); cdata->shell = !args_has(args, 'C'); if (!cdata->shell) { diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 7f161a91..f897af24 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -105,7 +105,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) } path = xstrdup("-"); } else - path = format_single_from_target(item, args->argv[0]); + path = format_single_from_target(item, args_string(args, 0)); if (args_has(args, 'a')) flags = O_APPEND; else diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 7069eccc..f9a29047 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -105,24 +105,24 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) goto changed; } + if (args_count(args) != 0) + layoutname = args_string(args, 0); + else if (args_has(args, 'o')) + layoutname = oldlayout; + else + layoutname = NULL; + if (!args_has(args, 'o')) { - if (args->argc == 0) + if (layoutname == NULL) layout = w->lastlayout; else - layout = layout_set_lookup(args->argv[0]); + layout = layout_set_lookup(layoutname); if (layout != -1) { layout_set_select(w, layout); goto changed; } } - if (args->argc != 0) - layoutname = args->argv[0]; - else if (args_has(args, 'o')) - layoutname = oldlayout; - else - layoutname = NULL; - if (layoutname != NULL) { if (layout_parse(w, layoutname) == -1) { cmdq_error(item, "can't set layout: %s", layoutname); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index c51d413b..351bd919 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -90,7 +90,7 @@ static struct cmdq_item * cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, struct args *args, int i) { - const char *s = args->argv[i]; + const char *s = args_string(args, i); struct utf8_data *ud, *loop; utf8_char uc; key_code key; @@ -145,9 +145,9 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) struct mouse_event *m = &event->m; struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); struct cmdq_item *after = item; - int i; key_code key; - u_int np = 1; + u_int i, np = 1; + u_int count = args_count(args); char *cause = NULL; if (args_has(args, 'N')) { @@ -157,7 +157,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } - if (wme != NULL && (args_has(args, 'X') || args->argc == 0)) { + if (wme != NULL && (args_has(args, 'X') || count == 0)) { if (wme->mode->command == NULL) { cmdq_error(item, "not in a mode"); return (CMD_RETURN_ERROR); @@ -203,7 +203,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) } for (; np != 0; np--) { - for (i = 0; i < args->argc; i++) { + for (i = 0; i < count; i++) { after = cmd_send_keys_inject_string(item, after, args, i); } diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 94d8cd52..3005e62d 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -94,11 +94,11 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args->argc != 1) { + if (args_count(args) != 1) { cmdq_error(item, "no data specified"); return (CMD_RETURN_ERROR); } - if ((newsize = strlen(args->argv[0])) == 0) + if ((newsize = strlen(args_string(args, 0))) == 0) return (CMD_RETURN_NORMAL); bufsize = 0; @@ -111,7 +111,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) } bufdata = xrealloc(bufdata, bufsize + newsize); - memcpy(bufdata + bufsize, args->argv[0], newsize); + memcpy(bufdata + bufsize, args_string(args, 0), newsize); bufsize += newsize; if (paste_set(bufdata, bufsize, bufname, &cause) != 0) { diff --git a/cmd-set-environment.c b/cmd-set-environment.c index f142df53..e60240a8 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -49,11 +49,11 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); struct environ *env; - const char *name, *value, *tflag; - char *expand = NULL; + const char *name = args_string(args, 0), *value; + const char *tflag; + char *expanded = NULL; enum cmd_retval retval = CMD_RETURN_NORMAL; - name = args->argv[0]; if (*name == '\0') { cmdq_error(item, "empty variable name"); return (CMD_RETURN_ERROR); @@ -63,13 +63,14 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } - if (args->argc < 2) + if (args_count(args) < 2) value = NULL; - else if (args_has(args, 'F')) - value = expand = format_single_from_target(item, args->argv[1]); else - value = args->argv[1]; - + value = args_string(args, 1); + if (value != NULL && args_has(args, 'F')) { + expanded = format_single_from_target(item, value); + value = expanded; + } if (args_has(args, 'g')) env = global_environ; else { @@ -113,6 +114,6 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) } out: - free(expand); + free(expanded); return (retval); } diff --git a/cmd-set-option.c b/cmd-set-option.c index 70e3c54d..48e04eed 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -77,14 +77,16 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *loop; struct options *oo; struct options_entry *parent, *o, *po; - char *name, *argument, *value = NULL, *cause; + char *name, *argument, *expanded = NULL; + char *cause; + const char *value; int window, idx, already, error, ambiguous; int scope; window = (cmd_get_entry(self) == &cmd_set_window_option_entry); /* Expand argument. */ - argument = format_single_from_target(item, args->argv[0]); + argument = format_single_from_target(item, args_string(args, 0)); /* If set-hook -R, fire the hook straight away. */ if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) { @@ -104,12 +106,14 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "invalid option: %s", argument); goto fail; } - if (args->argc < 2) + if (args_count(args) < 2) value = NULL; - else if (args_has(args, 'F')) - value = format_single_from_target(item, args->argv[1]); else - value = xstrdup(args->argv[1]); + value = args_string(args, 1); + if (value != NULL && args_has(args, 'F')) { + expanded = format_single_from_target(item, value); + value = expanded; + } /* Get the scope and table for the option .*/ scope = options_scope_from_name(args, window, name, target, &oo, @@ -211,13 +215,13 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) out: free(argument); - free(value); + free(expanded); free(name); return (CMD_RETURN_NORMAL); fail: free(argument); - free(value); + free(expanded); free(name); return (CMD_RETURN_ERROR); } diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 3ad31400..7ea1aeec 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -101,7 +101,7 @@ cmd_show_environment_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *target = cmdq_get_target(item); struct environ *env; struct environ_entry *envent; - const char *tflag; + const char *tflag, *name = args_string(args, 0); if ((tflag = args_get(args, 't')) != NULL) { if (target->s == NULL) { @@ -124,10 +124,10 @@ cmd_show_environment_exec(struct cmd *self, struct cmdq_item *item) env = target->s->environ; } - if (args->argc != 0) { - envent = environ_find(env, args->argv[0]); + if (name != NULL) { + envent = environ_find(env, name); if (envent == NULL) { - cmdq_error(item, "unknown variable: %s", args->argv[0]); + cmdq_error(item, "unknown variable: %s", name); return (CMD_RETURN_ERROR); } cmd_show_environment_print(self, item, envent); diff --git a/cmd-show-options.c b/cmd-show-options.c index a9c5bd2a..4d0acb42 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -86,7 +86,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) window = (cmd_get_entry(self) == &cmd_show_window_options_entry); - if (args->argc == 0) { + if (args_count(args) == 0) { scope = options_scope_from_flags(args, window, target, &oo, &cause); if (scope == OPTIONS_TABLE_NONE) { @@ -98,7 +98,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) } return (cmd_show_options_all(self, item, scope, oo)); } - argument = format_single_from_target(item, args->argv[0]); + argument = format_single_from_target(item, args_string(args, 0)); name = options_match(argument, &idx, &ambiguous); if (name == NULL) { diff --git a/cmd-source-file.c b/cmd-source-file.c index 5509259f..edc66c3f 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -129,11 +129,11 @@ 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, *expand = NULL; + char *pattern, *cwd, *expanded = NULL; const char *path, *error; glob_t g; - int i, result; - u_int j; + int result; + u_int i, j; cdata = xcalloc(1, sizeof *cdata); cdata->item = item; @@ -147,13 +147,13 @@ 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++) { + for (i = 0; i < args_count(args); i++) { + path = args_string(args, i); if (args_has(args, 'F')) { - free(expand); - expand = format_single_from_target(item, args->argv[i]); - path = expand; - } else - path = args->argv[i]; + free(expanded); + expanded = format_single_from_target(item, path); + path = expanded; + } if (strcmp(path, "-") == 0) { cmd_source_file_add(cdata, "-"); continue; @@ -180,7 +180,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) free(pattern); continue; } - free(expand); + free(expanded); free(pattern); for (j = 0; j < g.gl_pathc; j++) diff --git a/cmd-split-window.c b/cmd-split-window.c index ae36561f..baf75327 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -56,7 +56,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); - struct spawn_context sc; + struct spawn_context sc = { 0 }; struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct winlink *wl = target->wl; @@ -69,6 +69,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) char *cause, *cp, *copy; size_t plen; struct args_value *av; + u_int count = args_count(args); if (args_has(args, 'h')) type = LAYOUT_LEFTRIGHT; @@ -112,14 +113,14 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) size = -1; window_push_zoom(wp->window, 1, args_has(args, 'Z')); - input = (args_has(args, 'I') && args->argc == 0); + input = (args_has(args, 'I') && count == 0); flags = 0; if (args_has(args, 'b')) flags |= SPAWN_BEFORE; if (args_has(args, 'f')) flags |= SPAWN_FULLSIZE; - if (input || (args->argc == 1 && *args->argv[0] == '\0')) + if (input || (count == 1 && *args_string(args, 0) == '\0')) flags |= SPAWN_EMPTY; lc = layout_split_pane(wp, type, size, flags); @@ -128,7 +129,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } - memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; sc.wl = wl; @@ -137,8 +137,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) sc.lc = lc; sc.name = NULL; - sc.argc = args->argc; - sc.argv = args->argv; + args_vector(args, &sc.argc, &sc.argv); sc.environ = environ_create(); av = args_first_value(args, 'e'); @@ -159,6 +158,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { cmdq_error(item, "create pane failed: %s", cause); free(cause); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); environ_free(sc.environ); return (CMD_RETURN_ERROR); } @@ -168,6 +169,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) window_remove_pane(wp->window, new_wp); cmdq_error(item, "%s", cause); free(cause); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); environ_free(sc.environ); return (CMD_RETURN_ERROR); } @@ -188,6 +191,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) cmd_find_from_winlink_pane(&fs, wl, new_wp, 0); cmdq_insert_hook(s, item, &fs, "after-split-window"); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); environ_free(sc.environ); if (input) return (CMD_RETURN_WAIT); diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index a29831af..202a07f4 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -44,11 +44,11 @@ cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); key_code key; - const char *tablename; + const char *tablename, *keystr = args_string(args, 0); int quiet = args_has(args, 'q'); if (args_has(args, 'a')) { - if (args->argc != 0) { + if (keystr != NULL) { if (!quiet) cmdq_error(item, "key given with -a"); return (CMD_RETURN_ERROR); @@ -73,16 +73,16 @@ cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args->argc != 1) { + if (keystr == NULL) { if (!quiet) cmdq_error(item, "missing key"); return (CMD_RETURN_ERROR); } - key = key_string_lookup_string(args->argv[0]); + key = key_string_lookup_string(keystr); if (key == KEYC_NONE || key == KEYC_UNKNOWN) { if (!quiet) - cmdq_error(item, "unknown key: %s", args->argv[0]); + cmdq_error(item, "unknown key: %s", keystr); return (CMD_RETURN_ERROR); } diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 807a661a..027700b4 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -121,11 +121,11 @@ static enum cmd_retval cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); - const char *name = args->argv[0]; - struct wait_channel *wc, wc0; + const char *name = args_string(args, 0); + struct wait_channel *wc, find; - wc0.name = name; - wc = RB_FIND(wait_channels, &wait_channels, &wc0); + find.name = name; + wc = RB_FIND(wait_channels, &wait_channels, &find); if (args_has(args, 'S')) return (cmd_wait_for_signal(item, name, wc)); diff --git a/cmd.c b/cmd.c index a26c523b..4f13e90e 100644 --- a/cmd.c +++ b/cmd.c @@ -518,13 +518,10 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) return (NULL); cmd_log_argv(argc, argv, "%s: %s", __func__, entry->name); - args = args_parse(entry->args.template, argc, argv); + args = args_parse(entry->args.template, argc, argv, entry->args.lower, + entry->args.upper); if (args == NULL) goto usage; - if (entry->args.lower != -1 && args->argc < entry->args.lower) - goto usage; - if (entry->args.upper != -1 && args->argc > entry->args.upper) - goto usage; cmd = xcalloc(1, sizeof *cmd); cmd->entry = entry; diff --git a/tmux.h b/tmux.h index ae0ffc24..a28deb74 100644 --- a/tmux.h +++ b/tmux.h @@ -1364,11 +1364,6 @@ struct args_value { /* Arguments set. */ struct args_entry; RB_HEAD(args_tree, args_entry); -struct args { - struct args_tree tree; - int argc; - char **argv; -}; /* Command find structures. */ enum cmd_find_type { @@ -2188,7 +2183,8 @@ int tty_keys_next(struct tty *); /* arguments.c */ void args_set(struct args *, u_char, const char *); struct args *args_create(void); -struct args *args_parse(const char *, int, char **); +struct args *args_parse(const char *, int, char **, int, int); +void args_vector(struct args *, int *, char ***); void args_free(struct args *); char *args_print(struct args *); char *args_escape(const char *); @@ -2196,6 +2192,8 @@ int args_has(struct args *, u_char); const char *args_get(struct args *, u_char); u_char args_first(struct args *, struct args_entry **); u_char args_next(struct args_entry **); +u_int args_count(struct args *); +const char *args_string(struct args *, u_int); struct args_value *args_first_value(struct args *, u_char); struct args_value *args_next_value(struct args_value *); long long args_strtonum(struct args *, u_char, long long, long long, diff --git a/window-buffer.c b/window-buffer.c index 30bd5092..a2fa08ad 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -343,10 +343,10 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->key_format = xstrdup(WINDOW_BUFFER_DEFAULT_KEY_FORMAT); else data->key_format = xstrdup(args_get(args, 'K')); - if (args == NULL || args->argc == 0) + if (args == NULL || args_count(args) == 0) data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND); else - data->command = xstrdup(args->argv[0]); + data->command = xstrdup(args_string(args, 0)); data->data = mode_tree_start(wp, args, window_buffer_build, window_buffer_draw, window_buffer_search, window_buffer_menu, NULL, diff --git a/window-client.c b/window-client.c index db7c6dcc..00f36c7c 100644 --- a/window-client.c +++ b/window-client.c @@ -303,10 +303,10 @@ window_client_init(struct window_mode_entry *wme, data->key_format = xstrdup(WINDOW_CLIENT_DEFAULT_KEY_FORMAT); else data->key_format = xstrdup(args_get(args, 'K')); - if (args == NULL || args->argc == 0) + if (args == NULL || args_count(args) == 0) data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND); else - data->command = xstrdup(args->argv[0]); + data->command = xstrdup(args_string(args, 0)); data->data = mode_tree_start(wp, args, window_client_build, window_client_draw, NULL, window_client_menu, NULL, diff --git a/window-copy.c b/window-copy.c index 7c342d90..3e08c039 100644 --- a/window-copy.c +++ b/window-copy.c @@ -842,26 +842,23 @@ window_copy_expand_search_string(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; - const char *argument; + const char *ss = args_string(cs->args, 1); char *expanded; - if (cs->args->argc == 2) { - argument = cs->args->argv[1]; - if (*argument != '\0') { - if (args_has(cs->args, 'F')) { - expanded = format_single(NULL, argument, NULL, - NULL, NULL, wme->wp); - if (*expanded == '\0') { - free(expanded); - return (0); - } - free(data->searchstr); - data->searchstr = expanded; - } else { - free(data->searchstr); - data->searchstr = xstrdup(argument); - } + if (ss == NULL || *ss == '\0') + return (0); + + if (args_has(cs->args, 'F')) { + expanded = format_single(NULL, ss, NULL, NULL, NULL, wme->wp); + if (*expanded == '\0') { + free(expanded); + return (0); } + free(data->searchstr); + data->searchstr = expanded; + } else { + free(data->searchstr); + data->searchstr = xstrdup(ss); } return (1); } @@ -963,24 +960,25 @@ window_copy_do_copy_end_of_line(struct window_copy_cmd_state *cs, int pipe, int cancel) { struct window_mode_entry *wme = cs->wme; - int argc = cs->args->argc; - char **argv = cs->args->argv; struct client *c = cs->c; struct session *s = cs->s; struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; + u_int count = args_count(cs->args); u_int np = wme->prefix, ocx, ocy, ooy; struct window_copy_mode_data *data = wme->data; char *prefix = NULL, *command = NULL; + const char *arg1 = args_string(cs->args, 1); + const char *arg2 = args_string(cs->args, 2); if (pipe) { - if (argc == 3) - prefix = format_single(NULL, argv[2], c, s, wl, wp); - if (s != NULL && argc > 1 && *argv[1] != '\0') - command = format_single(NULL, argv[1], c, s, wl, wp); + if (count == 3) + prefix = format_single(NULL, arg2, c, s, wl, wp); + if (s != NULL && count > 1 && *arg1 != '\0') + command = format_single(NULL, arg1, c, s, wl, wp); } else { - if (argc == 2) - prefix = format_single(NULL, argv[1], c, s, wl, wp); + if (count == 2) + prefix = format_single(NULL, arg1, c, s, wl, wp); } ocx = data->cx; @@ -1044,24 +1042,25 @@ static enum window_copy_cmd_action window_copy_do_copy_line(struct window_copy_cmd_state *cs, int pipe, int cancel) { struct window_mode_entry *wme = cs->wme; - int argc = cs->args->argc; - char **argv = cs->args->argv; struct client *c = cs->c; struct session *s = cs->s; struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; + u_int count = args_count(cs->args); u_int np = wme->prefix, ocx, ocy, ooy; char *prefix = NULL, *command = NULL; + const char *arg1 = args_string(cs->args, 1); + const char *arg2 = args_string(cs->args, 2); if (pipe) { - if (argc == 3) - prefix = format_single(NULL, argv[2], c, s, wl, wp); - if (s != NULL && argc > 1 && *argv[1] != '\0') - command = format_single(NULL, argv[1], c, s, wl, wp); + if (count == 3) + prefix = format_single(NULL, arg2, c, s, wl, wp); + if (s != NULL && count > 1 && *arg1 != '\0') + command = format_single(NULL, arg1, c, s, wl, wp); } else { - if (argc == 2) - prefix = format_single(NULL, argv[1], c, s, wl, wp); + if (count == 2) + prefix = format_single(NULL, arg1, c, s, wl, wp); } ocx = data->cx; @@ -1131,9 +1130,10 @@ window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs) struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; char *prefix = NULL; + const char *arg1 = args_string(cs->args, 1); - if (cs->args->argc == 2) - prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + if (arg1 != NULL) + prefix = format_single(NULL, arg1, c, s, wl, wp); if (s != NULL) window_copy_copy_selection(wme, prefix); @@ -1968,14 +1968,15 @@ window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs) struct session *s = cs->s; struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; - char *command = NULL; - char *prefix = NULL; + char *command = NULL, *prefix = NULL; + const char *arg1 = args_string(cs->args, 1); + const char *arg2 = args_string(cs->args, 2); - if (cs->args->argc == 3) - prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp); + if (arg2 != NULL) + prefix = format_single(NULL, arg2, c, s, wl, wp); - if (s != NULL && cs->args->argc > 1 && *cs->args->argv[1] != '\0') - command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + if (s != NULL && arg1 != NULL && *arg1 != '\0') + command = format_single(NULL, arg1, c, s, wl, wp); window_copy_copy_pipe(wme, s, prefix, command); free(command); @@ -2012,9 +2013,10 @@ window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs) struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; char *command = NULL; + const char *arg1 = args_string(cs->args, 1); - if (s != NULL && cs->args->argc > 1 && *cs->args->argv[1] != '\0') - command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + if (s != NULL && arg1 != NULL && *arg1 != '\0') + command = format_single(NULL, arg1, c, s, wl, wp); window_copy_pipe(wme, s, command); free(command); @@ -2045,10 +2047,10 @@ static enum window_copy_cmd_action window_copy_cmd_goto_line(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - const char *argument = cs->args->argv[1]; + const char *arg1 = args_string(cs->args, 1); - if (*argument != '\0') - window_copy_goto_line(wme, argument); + if (*arg1 != '\0') + window_copy_goto_line(wme, arg1); return (WINDOW_COPY_CMD_NOTHING); } @@ -2058,12 +2060,12 @@ window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument = cs->args->argv[1]; + const char *arg1 = args_string(cs->args, 1); - if (*argument != '\0') { + if (*arg1 != '\0') { data->jumptype = WINDOW_COPY_JUMPBACKWARD; free(data->jumpchar); - data->jumpchar = utf8_fromcstr(argument); + data->jumpchar = utf8_fromcstr(arg1); for (; np != 0; np--) window_copy_cursor_jump_back(wme); } @@ -2076,12 +2078,12 @@ window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument = cs->args->argv[1]; + const char *arg1 = args_string(cs->args, 1); - if (*argument != '\0') { + if (*arg1 != '\0') { data->jumptype = WINDOW_COPY_JUMPFORWARD; free(data->jumpchar); - data->jumpchar = utf8_fromcstr(argument); + data->jumpchar = utf8_fromcstr(arg1); for (; np != 0; np--) window_copy_cursor_jump(wme); } @@ -2094,12 +2096,12 @@ window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument = cs->args->argv[1]; + const char *arg1 = args_string(cs->args, 1); - if (*argument != '\0') { + if (*arg1 != '\0') { data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; free(data->jumpchar); - data->jumpchar = utf8_fromcstr(argument); + data->jumpchar = utf8_fromcstr(arg1); for (; np != 0; np--) window_copy_cursor_jump_to_back(wme); } @@ -2112,12 +2114,12 @@ window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument = cs->args->argv[1]; + const char *arg1 = args_string(cs->args, 1); - if (*argument != '\0') { + if (*arg1 != '\0') { data->jumptype = WINDOW_COPY_JUMPTOFORWARD; free(data->jumpchar); - data->jumpchar = utf8_fromcstr(argument); + data->jumpchar = utf8_fromcstr(arg1); for (; np != 0; np--) window_copy_cursor_jump_to(wme); } @@ -2218,27 +2220,27 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; - const char *argument = cs->args->argv[1]; + const char *arg1 = args_string(cs->args, 1); const char *ss = data->searchstr; char prefix; enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; data->timeout = 0; - log_debug("%s: %s", __func__, argument); + log_debug("%s: %s", __func__, arg1); - prefix = *argument++; + prefix = *arg1++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; - } else if (ss != NULL && strcmp(argument, ss) != 0) { + } else if (ss != NULL && strcmp(arg1, ss) != 0) { data->cx = data->searchx; data->cy = data->searchy; data->oy = data->searcho; action = WINDOW_COPY_CMD_REDRAW; } - if (*argument == '\0') { + if (*arg1 == '\0') { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2248,7 +2250,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->searchtype = WINDOW_COPY_SEARCHUP; data->searchregex = 0; free(data->searchstr); - data->searchstr = xstrdup(argument); + data->searchstr = xstrdup(arg1); if (!window_copy_search_up(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); @@ -2258,7 +2260,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->searchtype = WINDOW_COPY_SEARCHDOWN; data->searchregex = 0; free(data->searchstr); - data->searchstr = xstrdup(argument); + data->searchstr = xstrdup(arg1); if (!window_copy_search_down(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); @@ -2273,27 +2275,27 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; - const char *argument = cs->args->argv[1]; + const char *arg1 = args_string(cs->args, 1); const char *ss = data->searchstr; char prefix; enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; data->timeout = 0; - log_debug("%s: %s", __func__, argument); + log_debug("%s: %s", __func__, arg1); - prefix = *argument++; + prefix = *arg1++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; - } else if (ss != NULL && strcmp(argument, ss) != 0) { + } else if (ss != NULL && strcmp(arg1, ss) != 0) { data->cx = data->searchx; data->cy = data->searchy; data->oy = data->searcho; action = WINDOW_COPY_CMD_REDRAW; } - if (*argument == '\0') { + if (*arg1 == '\0') { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2303,7 +2305,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->searchtype = WINDOW_COPY_SEARCHDOWN; data->searchregex = 0; free(data->searchstr); - data->searchstr = xstrdup(argument); + data->searchstr = xstrdup(arg1); if (!window_copy_search_down(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); @@ -2313,7 +2315,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->searchtype = WINDOW_COPY_SEARCHUP; data->searchregex = 0; free(data->searchstr); - data->searchstr = xstrdup(argument); + data->searchstr = xstrdup(arg1); if (!window_copy_search_up(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); @@ -2342,8 +2344,8 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) static const struct { const char *command; - int minargs; - int maxargs; + u_int minargs; + u_int maxargs; enum window_copy_cmd_clear clear; enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); } window_copy_cmd_table[] = { @@ -2833,13 +2835,12 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, enum window_copy_cmd_action action; enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER; const char *command; - u_int i; + u_int i, count = args_count(args); int keys; - if (args->argc == 0) + if (count == 0) return; - command = args->argv[0]; - + command = args_string(args, 0); if (m != NULL && m->valid && !MOUSE_WHEEL(m->b)) window_copy_move_mouse(m); @@ -2854,8 +2855,8 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, action = WINDOW_COPY_CMD_NOTHING; for (i = 0; i < nitems(window_copy_cmd_table); i++) { if (strcmp(window_copy_cmd_table[i].command, command) == 0) { - if (args->argc - 1 < window_copy_cmd_table[i].minargs || - args->argc - 1 > window_copy_cmd_table[i].maxargs) + if (count - 1 < window_copy_cmd_table[i].minargs || + count - 1 > window_copy_cmd_table[i].maxargs) break; clear = window_copy_cmd_table[i].clear; action = window_copy_cmd_table[i].f(&cs); @@ -5188,14 +5189,16 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, const char *separators, int already) { struct window_copy_mode_data *data = wme->data; + struct window *w = wme->wp->window; struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, oldy, hsize; int stop_at_eol; - stop_at_eol = - options_get_number(wme->wp->window->options, "mode-keys") - == MODEKEY_EMACS; + if (options_get_number(w->options, "mode-keys") == MODEKEY_EMACS) + stop_at_eol = 1; + else + stop_at_eol = 0; px = data->cx; hsize = screen_hsize(back_s); diff --git a/window-tree.c b/window-tree.c index f8e38399..e7029d33 100644 --- a/window-tree.c +++ b/window-tree.c @@ -925,10 +925,10 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT); else data->key_format = xstrdup(args_get(args, 'K')); - if (args == NULL || args->argc == 0) + if (args == NULL || args_count(args) == 0) data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); else - data->command = xstrdup(args->argv[0]); + data->command = xstrdup(args_string(args, 0)); data->squash_groups = !args_has(args, 'G'); data->data = mode_tree_start(wp, args, window_tree_build, From caa8703a23358e95beeec2867b05b8372eb523b1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 20:04:22 +0000 Subject: [PATCH 0940/1006] Spacing tweaks. --- format.c | 2 +- window-copy.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 1cf852c5..ebfe628c 100644 --- a/format.c +++ b/format.c @@ -3626,7 +3626,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, break; cp++; - argv = xreallocarray (argv, argc + 1, sizeof *argv); + argv = xreallocarray(argv, argc + 1, sizeof *argv); value = xstrndup(cp, end - cp); argv[argc++] = format_expand1(es, value); free(value); diff --git a/window-copy.c b/window-copy.c index 3e08c039..c1a31b48 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2841,6 +2841,7 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, if (count == 0) return; command = args_string(args, 0); + if (m != NULL && m->valid && !MOUSE_WHEEL(m->b)) window_copy_move_mouse(m); From d589be6c65c7295880bb84cd875baab2f77d5067 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Aug 2021 20:08:30 +0000 Subject: [PATCH 0941/1006] A couple more spacing fixes. --- arguments.c | 8 ++++---- cmd-parse.y | 2 +- format.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arguments.c b/arguments.c index 0867f815..ad3e20b3 100644 --- a/arguments.c +++ b/arguments.c @@ -148,7 +148,7 @@ args_vector(struct args *args, int *argc, char ***argv) static void printflike(3, 4) args_print_add(char **buf, size_t *len, const char *fmt, ...) { - va_list ap; + va_list ap; char *s; size_t slen; @@ -181,7 +181,7 @@ args_print_add_argument(char **buf, size_t *len, const char *argument) char * args_print(struct args *args) { - size_t len; + size_t len; char *buf; int i; u_int j; @@ -376,7 +376,7 @@ args_strtonum(struct args *args, u_char flag, long long minval, long long maxval, char **cause) { const char *errstr; - long long ll; + long long ll; struct args_entry *entry; struct args_value *value; @@ -418,7 +418,7 @@ args_string_percentage(const char *value, long long minval, long long maxval, long long curval, char **cause) { const char *errstr; - long long ll; + long long ll; size_t valuelen = strlen(value); char *copy; diff --git a/cmd-parse.y b/cmd-parse.y index 86cb357e..6d1fec7e 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -579,7 +579,7 @@ cmd_parse_get_error(const char *file, u_int line, const char *error) if (file == NULL) s = xstrdup(error); else - xasprintf (&s, "%s:%u: %s", file, line, error); + xasprintf(&s, "%s:%u: %s", file, line, error); return (s); } diff --git a/format.c b/format.c index ebfe628c..8202a256 100644 --- a/format.c +++ b/format.c @@ -2085,7 +2085,7 @@ static void * format_cb_session_windows(struct format_tree *ft) { if (ft->s != NULL) - return (format_printf ("%u", winlink_count(&ft->s->windows))); + return (format_printf("%u", winlink_count(&ft->s->windows))); return (NULL); } From d371764d022b6c074f967faee815c3117203451e Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Aug 2021 08:44:59 +0000 Subject: [PATCH 0942/1006] Wrap command argument definitions in their own struct. --- arguments.c | 9 +++++---- cmd.c | 3 +-- tmux.h | 15 +++++++++------ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/arguments.c b/arguments.c index ad3e20b3..77ca8e73 100644 --- a/arguments.c +++ b/arguments.c @@ -79,7 +79,7 @@ args_create(void) /* Parse an argv and argc into a new argument set. */ struct args * -args_parse(const char *template, int argc, char **argv, int lower, int upper) +args_parse(const struct args_parse *parse, int argc, char **argv) { struct args *args; int opt; @@ -89,10 +89,10 @@ args_parse(const char *template, int argc, char **argv, int lower, int upper) optarg = NULL; args = args_create(); - while ((opt = getopt(argc, argv, template)) != -1) { + while ((opt = getopt(argc, argv, parse->template)) != -1) { if (opt < 0) continue; - if (opt == '?' || strchr(template, opt) == NULL) { + if (opt == '?' || strchr(parse->template, opt) == NULL) { args_free(args); return (NULL); } @@ -105,7 +105,8 @@ args_parse(const char *template, int argc, char **argv, int lower, int upper) args->argc = argc; args->argv = cmd_copy_argv(argc, argv); - if ((lower != -1 && argc < lower) || (upper != -1 && argc > upper)) { + if ((parse->lower != -1 && argc < parse->lower) || + (parse->upper != -1 && argc > parse->upper)) { args_free(args); return (NULL); } diff --git a/cmd.c b/cmd.c index 4f13e90e..29f2d130 100644 --- a/cmd.c +++ b/cmd.c @@ -518,8 +518,7 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) return (NULL); cmd_log_argv(argc, argv, "%s: %s", __func__, entry->name); - args = args_parse(entry->args.template, argc, argv, entry->args.lower, - entry->args.upper); + args = args_parse(&entry->args, argc, argv); if (args == NULL) goto usage; diff --git a/tmux.h b/tmux.h index a28deb74..43881c1d 100644 --- a/tmux.h +++ b/tmux.h @@ -1365,6 +1365,13 @@ struct args_value { struct args_entry; RB_HEAD(args_tree, args_entry); +/* Arguments parsing state. */ +struct args_parse { + const char *template; + int lower; + int upper; +}; + /* Command find structures. */ enum cmd_find_type { CMD_FIND_PANE, @@ -1453,11 +1460,7 @@ struct cmd_entry { const char *name; const char *alias; - struct { - const char *template; - int lower; - int upper; - } args; + struct args_parse args; const char *usage; struct cmd_entry_flag source; @@ -2183,7 +2186,7 @@ int tty_keys_next(struct tty *); /* arguments.c */ void args_set(struct args *, u_char, const char *); struct args *args_create(void); -struct args *args_parse(const char *, int, char **, int, int); +struct args *args_parse(const struct args_parse *, int, char **); void args_vector(struct args *, int *, char ***); void args_free(struct args *); char *args_print(struct args *); From 08e6360f23284c9e2e521cb466002bdd9350a63d Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Aug 2021 10:22:38 +0000 Subject: [PATCH 0943/1006] Add args parsing callback for some future work, currently unused. --- cmd-attach-session.c | 2 +- cmd-bind-key.c | 2 +- cmd-break-pane.c | 2 +- cmd-capture-pane.c | 4 ++-- cmd-choose-tree.c | 8 ++++---- cmd-command-prompt.c | 2 +- cmd-confirm-before.c | 2 +- cmd-copy-mode.c | 4 ++-- cmd-detach-client.c | 4 ++-- cmd-display-menu.c | 4 ++-- cmd-display-message.c | 2 +- cmd-display-panes.c | 2 +- cmd-find-window.c | 2 +- cmd-if-shell.c | 2 +- cmd-join-pane.c | 4 ++-- cmd-kill-pane.c | 2 +- cmd-kill-server.c | 4 ++-- cmd-kill-session.c | 2 +- cmd-kill-window.c | 4 ++-- cmd-list-buffers.c | 2 +- cmd-list-clients.c | 2 +- cmd-list-keys.c | 4 ++-- cmd-list-panes.c | 2 +- cmd-list-sessions.c | 2 +- cmd-list-windows.c | 2 +- cmd-load-buffer.c | 2 +- cmd-lock-server.c | 6 +++--- cmd-move-window.c | 4 ++-- cmd-new-session.c | 4 ++-- cmd-new-window.c | 2 +- cmd-paste-buffer.c | 2 +- cmd-pipe-pane.c | 2 +- cmd-refresh-client.c | 2 +- cmd-rename-session.c | 2 +- cmd-rename-window.c | 2 +- cmd-resize-pane.c | 2 +- cmd-resize-window.c | 2 +- cmd-respawn-pane.c | 2 +- cmd-respawn-window.c | 2 +- cmd-rotate-window.c | 2 +- cmd-run-shell.c | 2 +- cmd-save-buffer.c | 4 ++-- cmd-select-layout.c | 6 +++--- cmd-select-pane.c | 4 ++-- cmd-select-window.c | 8 ++++---- cmd-send-keys.c | 4 ++-- cmd-set-buffer.c | 4 ++-- cmd-set-environment.c | 2 +- cmd-set-option.c | 6 +++--- cmd-show-environment.c | 2 +- cmd-show-messages.c | 2 +- cmd-show-options.c | 6 +++--- cmd-show-prompt-history.c | 4 ++-- cmd-source-file.c | 2 +- cmd-split-window.c | 2 +- cmd-swap-pane.c | 2 +- cmd-swap-window.c | 2 +- cmd-switch-client.c | 2 +- cmd-unbind-key.c | 2 +- cmd-wait-for.c | 2 +- tmux.h | 2 ++ 61 files changed, 91 insertions(+), 89 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index c2074f4f..cc795b22 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_attach_session_entry = { .name = "attach-session", .alias = "attach", - .args = { "c:dEf:rt:x", 0, 0 }, + .args = { "c:dEf:rt:x", 0, 0, NULL }, .usage = "[-dErx] [-c working-directory] [-f flags] " CMD_TARGET_SESSION_USAGE, diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 87dd3cf7..a16d054e 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_bind_key_entry = { .name = "bind-key", .alias = "bind", - .args = { "nrN:T:", 1, -1 }, + .args = { "nrN:T:", 1, -1, NULL }, .usage = "[-nr] [-T key-table] [-N note] key " "[command [arguments]]", diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 4c436405..4f38d4bd 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_break_pane_entry = { .name = "break-pane", .alias = "breakp", - .args = { "abdPF:n:s:t:", 0, 0 }, + .args = { "abdPF:n:s:t:", 0, 0, NULL }, .usage = "[-abdP] [-F format] [-n window-name] [-s src-pane] " "[-t dst-window]", diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 6f37bc8f..964f831e 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_capture_pane_entry = { .name = "capture-pane", .alias = "capturep", - .args = { "ab:CeE:JNpPqS:t:", 0, 0 }, + .args = { "ab:CeE:JNpPqS:t:", 0, 0, NULL }, .usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] " "[-S start-line] " CMD_TARGET_PANE_USAGE, @@ -53,7 +53,7 @@ const struct cmd_entry cmd_clear_history_entry = { .name = "clear-history", .alias = "clearhist", - .args = { "t:", 0, 0 }, + .args = { "t:", 0, 0, NULL }, .usage = CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 81209ee3..9258f366 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -30,7 +30,7 @@ const struct cmd_entry cmd_choose_tree_entry = { .name = "choose-tree", .alias = NULL, - .args = { "F:f:GK:NO:rst:wZ", 0, 1 }, + .args = { "F:f:GK:NO:rst:wZ", 0, 1, NULL }, .usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", @@ -44,7 +44,7 @@ const struct cmd_entry cmd_choose_client_entry = { .name = "choose-client", .alias = NULL, - .args = { "F:f:K:NO:rt:Z", 0, 1 }, + .args = { "F:f:K:NO:rt:Z", 0, 1, NULL }, .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", @@ -58,7 +58,7 @@ const struct cmd_entry cmd_choose_buffer_entry = { .name = "choose-buffer", .alias = NULL, - .args = { "F:f:K:NO:rt:Z", 0, 1 }, + .args = { "F:f:K:NO:rt:Z", 0, 1, NULL }, .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", @@ -72,7 +72,7 @@ const struct cmd_entry cmd_customize_mode_entry = { .name = "customize-mode", .alias = NULL, - .args = { "F:f:Nt:Z", 0, 0 }, + .args = { "F:f:Nt:Z", 0, 0, NULL }, .usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 25ec6817..f80760ce 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,7 +40,7 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1bFkiI:Np:t:T:", 0, 1 }, + .args = { "1bFkiI:Np:t:T:", 0, 1, NULL }, .usage = "[-1bFkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [-T type] [template]", diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 9f179aaf..580c379a 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_confirm_before_entry = { .name = "confirm-before", .alias = "confirm", - .args = { "bp:t:", 1, 1 }, + .args = { "bp:t:", 1, 1, NULL }, .usage = "[-b] [-p prompt] " CMD_TARGET_CLIENT_USAGE " command", .flags = CMD_CLIENT_TFLAG, diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index d8b4fd3e..8f698ce8 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -30,7 +30,7 @@ const struct cmd_entry cmd_copy_mode_entry = { .name = "copy-mode", .alias = NULL, - .args = { "eHMs:t:uq", 0, 0 }, + .args = { "eHMs:t:uq", 0, 0, NULL }, .usage = "[-eHMuq] [-s src-pane] " CMD_TARGET_PANE_USAGE, .source = { 's', CMD_FIND_PANE, 0 }, @@ -44,7 +44,7 @@ const struct cmd_entry cmd_clock_mode_entry = { .name = "clock-mode", .alias = NULL, - .args = { "t:", 0, 0 }, + .args = { "t:", 0, 0, NULL }, .usage = CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 02a43f4e..661293ae 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_detach_client_entry = { .name = "detach-client", .alias = "detach", - .args = { "aE:s:t:P", 0, 0 }, + .args = { "aE:s:t:P", 0, 0, NULL }, .usage = "[-aP] [-E shell-command] " "[-s target-session] " CMD_TARGET_CLIENT_USAGE, @@ -47,7 +47,7 @@ const struct cmd_entry cmd_suspend_client_entry = { .name = "suspend-client", .alias = "suspendc", - .args = { "t:", 0, 0 }, + .args = { "t:", 0, 0, NULL }, .usage = CMD_TARGET_CLIENT_USAGE, .flags = CMD_CLIENT_TFLAG, diff --git a/cmd-display-menu.c b/cmd-display-menu.c index a8eda0d2..b80f8d46 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_display_menu_entry = { .name = "display-menu", .alias = "menu", - .args = { "c:t:OT:x:y:", 1, -1 }, + .args = { "c:t:OT:x:y:", 1, -1, NULL }, .usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] " "[-x position] [-y position] name key command ...", @@ -51,7 +51,7 @@ const struct cmd_entry cmd_display_popup_entry = { .name = "display-popup", .alias = "popup", - .args = { "BCc:d:Eh:t:w:x:y:", 0, -1 }, + .args = { "BCc:d:Eh:t:w:x:y:", 0, -1, NULL }, .usage = "[-BCE] [-c target-client] [-d start-directory] [-h height] " CMD_TARGET_PANE_USAGE " [-w width] " "[-x position] [-y position] [command]", diff --git a/cmd-display-message.c b/cmd-display-message.c index f4d41e6c..596f0b5c 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_display_message_entry = { .name = "display-message", .alias = "display", - .args = { "ac:d:INpt:F:v", 0, 1 }, + .args = { "ac:d:INpt:F:v", 0, 1, NULL }, .usage = "[-aINpv] [-c target-client] [-d delay] [-F format] " CMD_TARGET_PANE_USAGE " [message]", diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 59484872..bc171638 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_display_panes_entry = { .name = "display-panes", .alias = "displayp", - .args = { "bd:Nt:", 0, 1 }, + .args = { "bd:Nt:", 0, 1, NULL }, .usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, diff --git a/cmd-find-window.c b/cmd-find-window.c index 691baf85..804e8fe4 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_find_window_entry = { .name = "find-window", .alias = "findw", - .args = { "CiNrt:TZ", 1, 1 }, + .args = { "CiNrt:TZ", 1, 1, NULL }, .usage = "[-CiNrTZ] " CMD_TARGET_PANE_USAGE " match-string", .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-if-shell.c b/cmd-if-shell.c index df06a0b6..28e9b5d1 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_if_shell_entry = { .name = "if-shell", .alias = "if", - .args = { "bFt:", 2, 3 }, + .args = { "bFt:", 2, 3, NULL }, .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " "[command]", diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 3d3f0ac8..f7f16a4e 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_join_pane_entry = { .name = "join-pane", .alias = "joinp", - .args = { "bdfhvp:l:s:t:", 0, 0 }, + .args = { "bdfhvp:l:s:t:", 0, 0, NULL }, .usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, @@ -50,7 +50,7 @@ const struct cmd_entry cmd_move_pane_entry = { .name = "move-pane", .alias = "movep", - .args = { "bdfhvp:l:s:t:", 0, 0 }, + .args = { "bdfhvp:l:s:t:", 0, 0, NULL }, .usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 3bf6e26e..e1134a1e 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_kill_pane_entry = { .name = "kill-pane", .alias = "killp", - .args = { "at:", 0, 0 }, + .args = { "at:", 0, 0, NULL }, .usage = "[-a] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-kill-server.c b/cmd-kill-server.c index 76bcf267..7bb79e3d 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_kill_server_entry = { .name = "kill-server", .alias = NULL, - .args = { "", 0, 0 }, + .args = { "", 0, 0, NULL }, .usage = "", .flags = 0, @@ -44,7 +44,7 @@ const struct cmd_entry cmd_start_server_entry = { .name = "start-server", .alias = "start", - .args = { "", 0, 0 }, + .args = { "", 0, 0, NULL }, .usage = "", .flags = CMD_STARTSERVER, diff --git a/cmd-kill-session.c b/cmd-kill-session.c index c10efba6..19a8d495 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_kill_session_entry = { .name = "kill-session", .alias = NULL, - .args = { "aCt:", 0, 0 }, + .args = { "aCt:", 0, 0, NULL }, .usage = "[-aC] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 430f667e..f5ff05f8 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -30,7 +30,7 @@ const struct cmd_entry cmd_kill_window_entry = { .name = "kill-window", .alias = "killw", - .args = { "at:", 0, 0 }, + .args = { "at:", 0, 0, NULL }, .usage = "[-a] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -43,7 +43,7 @@ const struct cmd_entry cmd_unlink_window_entry = { .name = "unlink-window", .alias = "unlinkw", - .args = { "kt:", 0, 0 }, + .args = { "kt:", 0, 0, NULL }, .usage = "[-k] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 45d5a4ee..8b12f0b3 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_list_buffers_entry = { .name = "list-buffers", .alias = "lsb", - .args = { "F:f:", 0, 0 }, + .args = { "F:f:", 0, 0, NULL }, .usage = "[-F format] [-f filter]", .flags = CMD_AFTERHOOK, diff --git a/cmd-list-clients.c b/cmd-list-clients.c index d450f017..a5b7d147 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_list_clients_entry = { .name = "list-clients", .alias = "lsc", - .args = { "F:t:", 0, 0 }, + .args = { "F:t:", 0, 0, NULL }, .usage = "[-F format] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 1484af6d..ae9f995c 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_list_keys_entry = { .name = "list-keys", .alias = "lsk", - .args = { "1aNP:T:", 0, 1 }, + .args = { "1aNP:T:", 0, 1, NULL }, .usage = "[-1aN] [-P prefix-string] [-T key-table] [key]", .flags = CMD_STARTSERVER|CMD_AFTERHOOK, @@ -47,7 +47,7 @@ const struct cmd_entry cmd_list_commands_entry = { .name = "list-commands", .alias = "lscm", - .args = { "F:", 0, 1 }, + .args = { "F:", 0, 1, NULL }, .usage = "[-F format] [command]", .flags = CMD_STARTSERVER|CMD_AFTERHOOK, diff --git a/cmd-list-panes.c b/cmd-list-panes.c index c6dcff23..a29a4032 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_list_panes_entry = { .name = "list-panes", .alias = "lsp", - .args = { "asF:f:t:", 0, 0 }, + .args = { "asF:f:t:", 0, 0, NULL }, .usage = "[-as] [-F format] [-f filter] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index fbc3db1d..e448524e 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -42,7 +42,7 @@ const struct cmd_entry cmd_list_sessions_entry = { .name = "list-sessions", .alias = "ls", - .args = { "F:f:", 0, 0 }, + .args = { "F:f:", 0, 0, NULL }, .usage = "[-F format] [-f filter]", .flags = CMD_AFTERHOOK, diff --git a/cmd-list-windows.c b/cmd-list-windows.c index d6cc0b7a..035471d4 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -49,7 +49,7 @@ const struct cmd_entry cmd_list_windows_entry = { .name = "list-windows", .alias = "lsw", - .args = { "F:f:at:", 0, 0 }, + .args = { "F:f:at:", 0, 0, NULL }, .usage = "[-a] [-F format] [-f filter] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 318a7467..59810dea 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_load_buffer_entry = { .name = "load-buffer", .alias = "loadb", - .args = { "b:t:w", 1, 1 }, + .args = { "b:t:w", 1, 1, NULL }, .usage = CMD_BUFFER_USAGE " " CMD_TARGET_CLIENT_USAGE " path", .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL, diff --git a/cmd-lock-server.c b/cmd-lock-server.c index a0df95b0..bd61dcff 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -30,7 +30,7 @@ const struct cmd_entry cmd_lock_server_entry = { .name = "lock-server", .alias = "lock", - .args = { "", 0, 0 }, + .args = { "", 0, 0, NULL }, .usage = "", .flags = CMD_AFTERHOOK, @@ -41,7 +41,7 @@ const struct cmd_entry cmd_lock_session_entry = { .name = "lock-session", .alias = "locks", - .args = { "t:", 0, 0 }, + .args = { "t:", 0, 0, NULL }, .usage = CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, @@ -54,7 +54,7 @@ const struct cmd_entry cmd_lock_client_entry = { .name = "lock-client", .alias = "lockc", - .args = { "t:", 0, 0 }, + .args = { "t:", 0, 0, NULL }, .usage = CMD_TARGET_CLIENT_USAGE, .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, diff --git a/cmd-move-window.c b/cmd-move-window.c index 61128771..4b90e70f 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_move_window_entry = { .name = "move-window", .alias = "movew", - .args = { "abdkrs:t:", 0, 0 }, + .args = { "abdkrs:t:", 0, 0, NULL }, .usage = "[-abdkr] " CMD_SRCDST_WINDOW_USAGE, .source = { 's', CMD_FIND_WINDOW, 0 }, @@ -46,7 +46,7 @@ const struct cmd_entry cmd_link_window_entry = { .name = "link-window", .alias = "linkw", - .args = { "abdks:t:", 0, 0 }, + .args = { "abdks:t:", 0, 0, NULL }, .usage = "[-abdk] " CMD_SRCDST_WINDOW_USAGE, .source = { 's', CMD_FIND_WINDOW, 0 }, diff --git a/cmd-new-session.c b/cmd-new-session.c index 0cc6b9da..83e3f299 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_new_session_entry = { .name = "new-session", .alias = "new", - .args = { "Ac:dDe:EF:f:n:Ps:t:x:Xy:", 0, -1 }, + .args = { "Ac:dDe:EF:f:n:Ps:t:x:Xy:", 0, -1, NULL }, .usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] " "[-f flags] [-n window-name] [-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", @@ -54,7 +54,7 @@ const struct cmd_entry cmd_has_session_entry = { .name = "has-session", .alias = "has", - .args = { "t:", 0, 0 }, + .args = { "t:", 0, 0, NULL }, .usage = CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, diff --git a/cmd-new-window.c b/cmd-new-window.c index f24de8e9..208cd880 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_new_window_entry = { .name = "new-window", .alias = "neww", - .args = { "abc:de:F:kn:PSt:", 0, -1 }, + .args = { "abc:de:F:kn:PSt:", 0, -1, NULL }, .usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] " "[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 22fa8bd5..28d4a8cc 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_paste_buffer_entry = { .name = "paste-buffer", .alias = "pasteb", - .args = { "db:prs:t:", 0, 0 }, + .args = { "db:prs:t:", 0, 0, NULL }, .usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index d1917bc6..ed08e618 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -44,7 +44,7 @@ const struct cmd_entry cmd_pipe_pane_entry = { .name = "pipe-pane", .alias = "pipep", - .args = { "IOot:", 0, 1 }, + .args = { "IOot:", 0, 1, NULL }, .usage = "[-IOo] " CMD_TARGET_PANE_USAGE " [shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 93845024..026bee02 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "A:B:cC:Df:F:lLRSt:U", 0, 1 }, + .args = { "A:B:cC:Df:F:lLRSt:U", 0, 1, NULL }, .usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] " "[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 8ec070bf..694f3c97 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_rename_session_entry = { .name = "rename-session", .alias = "rename", - .args = { "t:", 1, 1 }, + .args = { "t:", 1, 1, NULL }, .usage = CMD_TARGET_SESSION_USAGE " new-name", .target = { 't', CMD_FIND_SESSION, 0 }, diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 66c119f2..472b571b 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_rename_window_entry = { .name = "rename-window", .alias = "renamew", - .args = { "t:", 1, 1 }, + .args = { "t:", 1, 1, NULL }, .usage = CMD_TARGET_WINDOW_USAGE " new-name", .target = { 't', CMD_FIND_WINDOW, 0 }, diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 98d500db..9395b379 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_resize_pane_entry = { .name = "resize-pane", .alias = "resizep", - .args = { "DLMRTt:Ux:y:Z", 0, 1 }, + .args = { "DLMRTt:Ux:y:Z", 0, 1, NULL }, .usage = "[-DLMRTUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " "[adjustment]", diff --git a/cmd-resize-window.c b/cmd-resize-window.c index 3e33f771..8389230b 100644 --- a/cmd-resize-window.c +++ b/cmd-resize-window.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_resize_window_entry = { .name = "resize-window", .alias = "resizew", - .args = { "aADLRt:Ux:y:", 0, 1 }, + .args = { "aADLRt:Ux:y:", 0, 1, NULL }, .usage = "[-aADLRU] [-x width] [-y height] " CMD_TARGET_WINDOW_USAGE " " "[adjustment]", diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 14f9abf2..3d8d1c03 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_respawn_pane_entry = { .name = "respawn-pane", .alias = "respawnp", - .args = { "c:e:kt:", 0, -1 }, + .args = { "c:e:kt:", 0, -1, NULL }, .usage = "[-k] [-c start-directory] [-e environment] " CMD_TARGET_PANE_USAGE " [command]", diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 25288bad..51ef209e 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_respawn_window_entry = { .name = "respawn-window", .alias = "respawnw", - .args = { "c:e:kt:", 0, -1 }, + .args = { "c:e:kt:", 0, -1, NULL }, .usage = "[-k] [-c start-directory] [-e environment] " CMD_TARGET_WINDOW_USAGE " [command]", diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 450ffc17..0e2ed852 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -31,7 +31,7 @@ const struct cmd_entry cmd_rotate_window_entry = { .name = "rotate-window", .alias = "rotatew", - .args = { "Dt:UZ", 0, 0 }, + .args = { "Dt:UZ", 0, 0, NULL }, .usage = "[-DUZ] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, diff --git a/cmd-run-shell.c b/cmd-run-shell.c index a0115f0a..207e4710 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_run_shell_entry = { .name = "run-shell", .alias = "run", - .args = { "bd:Ct:", 0, 1 }, + .args = { "bd:Ct:", 0, 1, NULL }, .usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index f897af24..513181e1 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_save_buffer_entry = { .name = "save-buffer", .alias = "saveb", - .args = { "ab:", 1, 1 }, + .args = { "ab:", 1, 1, NULL }, .usage = "[-a] " CMD_BUFFER_USAGE " path", .flags = CMD_AFTERHOOK, @@ -49,7 +49,7 @@ const struct cmd_entry cmd_show_buffer_entry = { .name = "show-buffer", .alias = "showb", - .args = { "b:", 0, 0 }, + .args = { "b:", 0, 0, NULL }, .usage = CMD_BUFFER_USAGE, .flags = CMD_AFTERHOOK, diff --git a/cmd-select-layout.c b/cmd-select-layout.c index f9a29047..c857a0e1 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_select_layout_entry = { .name = "select-layout", .alias = "selectl", - .args = { "Enopt:", 0, 1 }, + .args = { "Enopt:", 0, 1, NULL }, .usage = "[-Enop] " CMD_TARGET_PANE_USAGE " [layout-name]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -46,7 +46,7 @@ const struct cmd_entry cmd_next_layout_entry = { .name = "next-layout", .alias = "nextl", - .args = { "t:", 0, 0 }, + .args = { "t:", 0, 0, NULL }, .usage = CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -59,7 +59,7 @@ const struct cmd_entry cmd_previous_layout_entry = { .name = "previous-layout", .alias = "prevl", - .args = { "t:", 0, 0 }, + .args = { "t:", 0, 0, NULL }, .usage = CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, diff --git a/cmd-select-pane.c b/cmd-select-pane.c index c5b4ee13..ae21d4ce 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_select_pane_entry = { .name = "select-pane", .alias = "selectp", - .args = { "DdegLlMmP:RT:t:UZ", 0, 0 }, /* -P and -g deprecated */ + .args = { "DdegLlMmP:RT:t:UZ", 0, 0, NULL }, /* -P and -g deprecated */ .usage = "[-DdeLlMmRUZ] [-T title] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -46,7 +46,7 @@ const struct cmd_entry cmd_last_pane_entry = { .name = "last-pane", .alias = "lastp", - .args = { "det:Z", 0, 0 }, + .args = { "det:Z", 0, 0, NULL }, .usage = "[-deZ] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, diff --git a/cmd-select-window.c b/cmd-select-window.c index 8dd358b0..4aca3e60 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_select_window_entry = { .name = "select-window", .alias = "selectw", - .args = { "lnpTt:", 0, 0 }, + .args = { "lnpTt:", 0, 0, NULL }, .usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -46,7 +46,7 @@ const struct cmd_entry cmd_next_window_entry = { .name = "next-window", .alias = "next", - .args = { "at:", 0, 0 }, + .args = { "at:", 0, 0, NULL }, .usage = "[-a] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, @@ -59,7 +59,7 @@ const struct cmd_entry cmd_previous_window_entry = { .name = "previous-window", .alias = "prev", - .args = { "at:", 0, 0 }, + .args = { "at:", 0, 0, NULL }, .usage = "[-a] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, @@ -72,7 +72,7 @@ const struct cmd_entry cmd_last_window_entry = { .name = "last-window", .alias = "last", - .args = { "t:", 0, 0 }, + .args = { "t:", 0, 0, NULL }, .usage = CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 351bd919..44b796ba 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_send_keys_entry = { .name = "send-keys", .alias = "send", - .args = { "FHlMN:Rt:X", 0, -1 }, + .args = { "FHlMN:Rt:X", 0, -1, NULL }, .usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...", @@ -47,7 +47,7 @@ const struct cmd_entry cmd_send_prefix_entry = { .name = "send-prefix", .alias = NULL, - .args = { "2t:", 0, 0 }, + .args = { "2t:", 0, 0, NULL }, .usage = "[-2] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 3005e62d..9112683f 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_set_buffer_entry = { .name = "set-buffer", .alias = "setb", - .args = { "ab:t:n:w", 0, 1 }, + .args = { "ab:t:n:w", 0, 1, NULL }, .usage = "[-aw] " CMD_BUFFER_USAGE " [-n new-buffer-name] " CMD_TARGET_CLIENT_USAGE " data", @@ -45,7 +45,7 @@ const struct cmd_entry cmd_delete_buffer_entry = { .name = "delete-buffer", .alias = "deleteb", - .args = { "b:", 0, 0 }, + .args = { "b:", 0, 0, NULL }, .usage = CMD_BUFFER_USAGE, .flags = CMD_AFTERHOOK, diff --git a/cmd-set-environment.c b/cmd-set-environment.c index e60240a8..cec1f3e3 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_set_environment_entry = { .name = "set-environment", .alias = "setenv", - .args = { "Fhgrt:u", 1, 2 }, + .args = { "Fhgrt:u", 1, 2, NULL }, .usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " name [value]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, diff --git a/cmd-set-option.c b/cmd-set-option.c index 48e04eed..8839dc0d 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_set_option_entry = { .name = "set-option", .alias = "set", - .args = { "aFgopqst:uUw", 1, 2 }, + .args = { "aFgopqst:uUw", 1, 2, NULL }, .usage = "[-aFgopqsuUw] " CMD_TARGET_PANE_USAGE " option [value]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -46,7 +46,7 @@ const struct cmd_entry cmd_set_window_option_entry = { .name = "set-window-option", .alias = "setw", - .args = { "aFgoqt:u", 1, 2 }, + .args = { "aFgoqt:u", 1, 2, NULL }, .usage = "[-aFgoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, @@ -59,7 +59,7 @@ const struct cmd_entry cmd_set_hook_entry = { .name = "set-hook", .alias = NULL, - .args = { "agpRt:uw", 1, 2 }, + .args = { "agpRt:uw", 1, 2, NULL }, .usage = "[-agpRuw] " CMD_TARGET_PANE_USAGE " hook [command]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 7ea1aeec..b52db366 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_show_environment_entry = { .name = "show-environment", .alias = "showenv", - .args = { "hgst:", 0, 1 }, + .args = { "hgst:", 0, 1, NULL }, .usage = "[-hgs] " CMD_TARGET_SESSION_USAGE " [name]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, diff --git a/cmd-show-messages.c b/cmd-show-messages.c index ef5acf44..64a4670e 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -40,7 +40,7 @@ const struct cmd_entry cmd_show_messages_entry = { .name = "show-messages", .alias = "showmsgs", - .args = { "JTt:", 0, 0 }, + .args = { "JTt:", 0, 0, NULL }, .usage = "[-JT] " CMD_TARGET_CLIENT_USAGE, .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, diff --git a/cmd-show-options.c b/cmd-show-options.c index 4d0acb42..bdcd3e78 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_show_options_entry = { .name = "show-options", .alias = "show", - .args = { "AgHpqst:vw", 0, 1 }, + .args = { "AgHpqst:vw", 0, 1, NULL }, .usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -52,7 +52,7 @@ const struct cmd_entry cmd_show_window_options_entry = { .name = "show-window-options", .alias = "showw", - .args = { "gvt:", 0, 1 }, + .args = { "gvt:", 0, 1, NULL }, .usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, @@ -65,7 +65,7 @@ const struct cmd_entry cmd_show_hooks_entry = { .name = "show-hooks", .alias = NULL, - .args = { "gpt:w", 0, 1 }, + .args = { "gpt:w", 0, 1, NULL }, .usage = "[-gpw] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, diff --git a/cmd-show-prompt-history.c b/cmd-show-prompt-history.c index 2091ac9d..c85950b0 100644 --- a/cmd-show-prompt-history.c +++ b/cmd-show-prompt-history.c @@ -31,7 +31,7 @@ const struct cmd_entry cmd_show_prompt_history_entry = { .name = "show-prompt-history", .alias = "showphist", - .args = { "T:", 0, 0 }, + .args = { "T:", 0, 0, NULL }, .usage = "[-T type]", .flags = CMD_AFTERHOOK, @@ -42,7 +42,7 @@ const struct cmd_entry cmd_clear_prompt_history_entry = { .name = "clear-prompt-history", .alias = "clearphist", - .args = { "T:", 0, 0 }, + .args = { "T:", 0, 0, NULL }, .usage = "[-T type]", .flags = CMD_AFTERHOOK, diff --git a/cmd-source-file.c b/cmd-source-file.c index edc66c3f..63e1b28c 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_source_file_entry = { .name = "source-file", .alias = "source", - .args = { "Fnqv", 1, -1 }, + .args = { "Fnqv", 1, -1, NULL }, .usage = "[-Fnqv] path ...", .flags = 0, diff --git a/cmd-split-window.c b/cmd-split-window.c index baf75327..30a361df 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -40,7 +40,7 @@ const struct cmd_entry cmd_split_window_entry = { .name = "split-window", .alias = "splitw", - .args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1 }, + .args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL }, .usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] " "[-F format] [-l size] " CMD_TARGET_PANE_USAGE " [command]", diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 12bc20b4..7d477739 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_swap_pane_entry = { .name = "swap-pane", .alias = "swapp", - .args = { "dDs:t:UZ", 0, 0 }, + .args = { "dDs:t:UZ", 0, 0, NULL }, .usage = "[-dDUZ] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 651a44da..b765112b 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_swap_window_entry = { .name = "swap-window", .alias = "swapw", - .args = { "ds:t:", 0, 0 }, + .args = { "ds:t:", 0, 0, NULL }, .usage = "[-d] " CMD_SRCDST_WINDOW_USAGE, .source = { 's', CMD_FIND_WINDOW, CMD_FIND_DEFAULT_MARKED }, diff --git a/cmd-switch-client.c b/cmd-switch-client.c index bc6baa6a..dc1b621a 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_switch_client_entry = { .name = "switch-client", .alias = "switchc", - .args = { "lc:EFnpt:rT:Z", 0, 0 }, + .args = { "lc:EFnpt:rT:Z", 0, 0, NULL }, .usage = "[-ElnprZ] [-c target-client] [-t target-session] " "[-T key-table]", diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 202a07f4..6d91d7cc 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_unbind_key_entry = { .name = "unbind-key", .alias = "unbind", - .args = { "anqT:", 0, 1 }, + .args = { "anqT:", 0, 1, NULL }, .usage = "[-anq] [-T key-table] key", .flags = CMD_AFTERHOOK, diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 027700b4..8a6aa259 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_wait_for_entry = { .name = "wait-for", .alias = "wait", - .args = { "LSU", 1, 1 }, + .args = { "LSU", 1, 1, NULL }, .usage = "[-L|-S|-U] channel", .flags = 0, diff --git a/tmux.h b/tmux.h index 43881c1d..af3e3a84 100644 --- a/tmux.h +++ b/tmux.h @@ -1366,10 +1366,12 @@ struct args_entry; RB_HEAD(args_tree, args_entry); /* Arguments parsing state. */ +typedef enum args_type (*args_parse_cb)(struct args *, u_int); struct args_parse { const char *template; int lower; int upper; + args_parse_cb cb; }; /* Command find structures. */ From 110ba767e591946d6784acef87737850f2ad3ae9 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Aug 2021 10:28:05 +0000 Subject: [PATCH 0944/1006] Rename a member to match what it will be in future. --- arguments.c | 12 ++++++------ cmd-new-session.c | 2 +- cmd-new-window.c | 2 +- cmd-queue.c | 2 +- cmd-refresh-client.c | 4 ++-- cmd-resize-pane.c | 3 +-- cmd-resize-window.c | 3 +-- cmd-respawn-pane.c | 2 +- cmd-respawn-window.c | 2 +- cmd-split-window.c | 2 +- tmux.h | 2 +- 11 files changed, 17 insertions(+), 19 deletions(-) diff --git a/arguments.c b/arguments.c index 77ca8e73..2f9c0c31 100644 --- a/arguments.c +++ b/arguments.c @@ -128,7 +128,7 @@ args_free(struct args *args) RB_REMOVE(args_tree, &args->tree, entry); TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) { TAILQ_REMOVE(&entry->values, value, entry); - free(value->value); + free(value->string); free(value); } free(entry); @@ -210,7 +210,7 @@ args_print(struct args *args) args_print_add(&buf, &len, " -%c", entry->flag); else args_print_add(&buf, &len, "-%c", entry->flag); - args_print_add_argument(&buf, &len, value->value); + args_print_add_argument(&buf, &len, value->string); } } @@ -299,7 +299,7 @@ args_set(struct args *args, u_char flag, const char *s) if (s != NULL) { value = xcalloc(1, sizeof *value); - value->value = xstrdup(s); + value->string = xstrdup(s); TAILQ_INSERT_TAIL(&entry->values, value, entry); } } @@ -314,7 +314,7 @@ args_get(struct args *args, u_char flag) return (NULL); if (TAILQ_EMPTY(&entry->values)) return (NULL); - return (TAILQ_LAST(&entry->values, args_values)->value); + return (TAILQ_LAST(&entry->values, args_values)->string); } /* Get first argument. */ @@ -387,7 +387,7 @@ args_strtonum(struct args *args, u_char flag, long long minval, } value = TAILQ_LAST(&entry->values, args_values); - ll = strtonum(value->value, minval, maxval, &errstr); + ll = strtonum(value->string, minval, maxval, &errstr); if (errstr != NULL) { *cause = xstrdup(errstr); return (0); @@ -409,7 +409,7 @@ args_percentage(struct args *args, u_char flag, long long minval, *cause = xstrdup("missing"); return (0); } - value = TAILQ_LAST(&entry->values, args_values)->value; + value = TAILQ_LAST(&entry->values, args_values)->string; return (args_string_percentage(value, minval, maxval, curval, cause)); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 83e3f299..93c7e7b4 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -271,7 +271,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) environ_update(global_s_options, c->environ, env); av = args_first_value(args, 'e'); while (av != NULL) { - environ_put(env, av->value, 0); + environ_put(env, av->string, 0); av = args_next_value(av); } s = session_create(prefix, newname, cwd, env, oo, tiop); diff --git a/cmd-new-window.c b/cmd-new-window.c index 208cd880..e88795c2 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -110,7 +110,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) av = args_first_value(args, 'e'); while (av != NULL) { - environ_put(sc.environ, av->value, 0); + environ_put(sc.environ, av->string, 0); av = args_next_value(av); } diff --git a/cmd-queue.c b/cmd-queue.c index 687d037f..806f1cbb 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -413,7 +413,7 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, av = args_first_value(args, flag); while (av != NULL) { xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i); - cmdq_add_format(new_state, tmp, "%s", av->value); + cmdq_add_format(new_state, tmp, "%s", av->string); i++; av = args_next_value(av); } diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 026bee02..24a49dcb 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -187,7 +187,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) goto not_control_client; av = args_first_value(args, 'A'); while (av != NULL) { - cmd_refresh_client_update_offset(tc, av->value); + cmd_refresh_client_update_offset(tc, av->string); av = args_next_value(av); } return (CMD_RETURN_NORMAL); @@ -197,7 +197,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) goto not_control_client; av = args_first_value(args, 'B'); while (av != NULL) { - cmd_refresh_client_update_subscription(tc, av->value); + cmd_refresh_client_update_subscription(tc, av->string); av = args_next_value(av); } return (CMD_RETURN_NORMAL); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 9395b379..81744f72 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -98,8 +98,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_count(args) == 0) adjust = 1; else { - adjust = strtonum(args_string(args, 0), 1, INT_MAX, - &errstr); + adjust = strtonum(args_string(args, 0), 1, INT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "adjustment %s", errstr); return (CMD_RETURN_ERROR); diff --git a/cmd-resize-window.c b/cmd-resize-window.c index 8389230b..24d73c87 100644 --- a/cmd-resize-window.c +++ b/cmd-resize-window.c @@ -59,8 +59,7 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) if (args_count(args) == 0) adjust = 1; else { - adjust = strtonum(args_string(args, 0), 1, INT_MAX, - &errstr); + adjust = strtonum(args_string(args, 0), 1, INT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "adjustment %s", errstr); return (CMD_RETURN_ERROR); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 3d8d1c03..652ef755 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -69,7 +69,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) av = args_first_value(args, 'e'); while (av != NULL) { - environ_put(sc.environ, av->value, 0); + environ_put(sc.environ, av->string, 0); av = args_next_value(av); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 51ef209e..4e6dc2a9 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -67,7 +67,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) av = args_first_value(args, 'e'); while (av != NULL) { - environ_put(sc.environ, av->value, 0); + environ_put(sc.environ, av->string, 0); av = args_next_value(av); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 30a361df..8b078db1 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -142,7 +142,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) av = args_first_value(args, 'e'); while (av != NULL) { - environ_put(sc.environ, av->value, 0); + environ_put(sc.environ, av->string, 0); av = args_next_value(av); } diff --git a/tmux.h b/tmux.h index af3e3a84..fb1631f9 100644 --- a/tmux.h +++ b/tmux.h @@ -1357,7 +1357,7 @@ TAILQ_HEAD(message_list, message_entry); /* Argument value. */ struct args_value { - char *value; + char *string; TAILQ_ENTRY(args_value) entry; }; From c286fbdcd778c0d3d6b60a9f8682b413078e4639 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Aug 2021 14:06:17 +0000 Subject: [PATCH 0945/1006] Preserve command group when moving temporary list to current list being buit. --- cmd-parse.y | 2 +- cmd.c | 13 ++++++++++++- tmux.h | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 6d1fec7e..458092b4 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -871,7 +871,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, cmd_list_free(current); return; } - cmd_list_move(current, add); + cmd_list_append_all(current, add); cmd_list_free(add); } if (current != NULL) { diff --git a/cmd.c b/cmd.c index 29f2d130..52c5ad74 100644 --- a/cmd.c +++ b/cmd.c @@ -594,7 +594,18 @@ cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); } -/* Move all commands from one command list to another */ +/* Append all commands from one list to another. */ +void +cmd_list_append_all(struct cmd_list *cmdlist, struct cmd_list *from) +{ + struct cmd *cmd; + + TAILQ_FOREACH(cmd, from->list, qentry) + cmd->group = cmdlist->group; + TAILQ_CONCAT(cmdlist->list, from->list, qentry); +} + +/* Move all commands from one command list to another. */ void cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) { diff --git a/tmux.h b/tmux.h index fb1631f9..3a825526 100644 --- a/tmux.h +++ b/tmux.h @@ -2256,6 +2256,7 @@ void cmd_free(struct cmd *); char *cmd_print(struct cmd *); struct cmd_list *cmd_list_new(void); void cmd_list_append(struct cmd_list *, struct cmd *); +void cmd_list_append_all(struct cmd_list *, struct cmd_list *); void cmd_list_move(struct cmd_list *, struct cmd_list *); void cmd_list_free(struct cmd_list *); char *cmd_list_print(struct cmd_list *, int); From 68cacaec68ca8300e0ea439abdf9db16e74241bb Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Aug 2021 14:10:08 +0000 Subject: [PATCH 0946/1006] Remove some members of struct cmd which are no longer used. --- cmd.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/cmd.c b/cmd.c index 52c5ad74..ce04ce2a 100644 --- a/cmd.c +++ b/cmd.c @@ -222,10 +222,6 @@ struct cmd { char *file; u_int line; - char *alias; - int argc; - char **argv; - TAILQ_ENTRY(cmd) qentry; }; TAILQ_HEAD(cmds, cmd); @@ -530,10 +526,6 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) cmd->file = xstrdup(file); cmd->line = line; - cmd->alias = NULL; - cmd->argc = argc; - cmd->argv = cmd_copy_argv(argc, argv); - return (cmd); usage: @@ -547,9 +539,6 @@ usage: void cmd_free(struct cmd *cmd) { - free(cmd->alias); - cmd_free_argv(cmd->argc, cmd->argv); - free(cmd->file); args_free(cmd->args); From 5241dae87de88906dc5c1dc271a4f25522a22d4c Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Aug 2021 17:25:32 +0000 Subject: [PATCH 0947/1006] Stop caring about empty commands, just treat as a null command. --- cfg.c | 4 ---- cmd-bind-key.c | 3 --- cmd-if-shell.c | 2 -- cmd-parse.y | 15 ++++++--------- cmd-queue.c | 11 ++++++++++- options.c | 4 ---- server-client.c | 3 --- tmux.h | 1 - window-customize.c | 3 --- 9 files changed, 16 insertions(+), 30 deletions(-) diff --git a/cfg.c b/cfg.c index cf6117f4..3130e323 100644 --- a/cfg.c +++ b/cfg.c @@ -125,8 +125,6 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, pr = cmd_parse_from_file(f, &pi); fclose(f); - if (pr->status == CMD_PARSE_EMPTY) - return (0); if (pr->status == CMD_PARSE_ERROR) { cfg_add_cause("%s", pr->error); free(pr->error); @@ -179,8 +177,6 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path, pi.c = c; pr = cmd_parse_from_buffer(buf, len, &pi); - if (pr->status == CMD_PARSE_EMPTY) - return (0); if (pr->status == CMD_PARSE_ERROR) { cfg_add_cause("%s", pr->error); free(pr->error); diff --git a/cmd-bind-key.c b/cmd-bind-key.c index a16d054e..97057b3a 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -75,9 +75,6 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) cmd_free_argv(argc, argv); } 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); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 28e9b5d1..a04bdddd 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -159,8 +159,6 @@ cmd_if_shell_callback(struct job *job) pr = cmd_parse_from_string(cmd, &cdata->input); switch (pr->status) { - case CMD_PARSE_EMPTY: - break; case CMD_PARSE_ERROR: if (cdata->item != NULL) cmdq_error(cdata->item, "%s", pr->error); diff --git a/cmd-parse.y b/cmd-parse.y index 458092b4..6be5d8a0 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -744,7 +744,8 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd, first = TAILQ_FIRST(&cmd->arguments); if (first == NULL || first->type != CMD_PARSE_STRING) { - pr->status = CMD_PARSE_EMPTY; + pr->status = CMD_PARSE_SUCCESS; + pr->cmdlist = cmd_list_new(); return (1); } name = first->string; @@ -840,7 +841,8 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, /* Check for an empty list. */ if (TAILQ_EMPTY(cmds)) { - pr->status = CMD_PARSE_EMPTY; + pr->status = CMD_PARSE_SUCCESS; + pr->cmdlist = cmd_list_new(); return; } cmd_parse_log_commands(cmds, __func__); @@ -942,8 +944,6 @@ cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi, pr = cmd_parse_from_string(s, pi); switch (pr->status) { - case CMD_PARSE_EMPTY: - break; case CMD_PARSE_ERROR: if (error != NULL) *error = pr->error; @@ -968,8 +968,6 @@ cmd_parse_and_append(const char *s, struct cmd_parse_input *pi, pr = cmd_parse_from_string(s, pi); switch (pr->status) { - case CMD_PARSE_EMPTY: - break; case CMD_PARSE_ERROR: if (error != NULL) *error = pr->error; @@ -1000,9 +998,8 @@ cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi) memset(&pr, 0, sizeof pr); if (len == 0) { - pr.status = CMD_PARSE_EMPTY; - pr.cmdlist = NULL; - pr.error = NULL; + pr.status = CMD_PARSE_SUCCESS; + pr.cmdlist = cmd_list_new(); return (&pr); } diff --git a/cmd-queue.c b/cmd-queue.c index 806f1cbb..4fbdc4e7 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -478,6 +478,13 @@ cmdq_remove_group(struct cmdq_item *item) } } +/* Empty command callback. */ +static enum cmd_retval +cmdq_empty_command(__unused struct cmdq_item *item, __unused void *data) +{ + return (CMD_RETURN_NORMAL); +} + /* Get a command for the command queue. */ struct cmdq_item * cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state) @@ -487,12 +494,14 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state) const struct cmd_entry *entry; int created = 0; + if ((cmd = cmd_list_first(cmdlist)) == NULL) + return (cmdq_get_callback(cmdq_empty_command, NULL)); + if (state == NULL) { state = cmdq_new_state(NULL, NULL, 0); created = 1; } - cmd = cmd_list_first(cmdlist); while (cmd != NULL) { entry = cmd_get_entry(cmd); diff --git a/options.c b/options.c index 23c83c07..e32db774 100644 --- a/options.c +++ b/options.c @@ -443,10 +443,6 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, if (OPTIONS_IS_COMMAND(o)) { pr = cmd_parse_from_string(value, NULL); switch (pr->status) { - case CMD_PARSE_EMPTY: - if (cause != NULL) - *cause = xstrdup("empty command"); - return (-1); case CMD_PARSE_ERROR: if (cause != NULL) *cause = pr->error; diff --git a/server-client.c b/server-client.c index 87c0f554..8821ba57 100644 --- a/server-client.c +++ b/server-client.c @@ -2151,9 +2151,6 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) pr = cmd_parse_from_arguments(argc, argv, NULL); switch (pr->status) { - case CMD_PARSE_EMPTY: - cause = xstrdup("empty command"); - goto error; case CMD_PARSE_ERROR: cause = pr->error; goto error; diff --git a/tmux.h b/tmux.h index 3a825526..f8dcac49 100644 --- a/tmux.h +++ b/tmux.h @@ -1417,7 +1417,6 @@ enum cmd_retval { /* Command parse result. */ enum cmd_parse_status { - CMD_PARSE_EMPTY, CMD_PARSE_ERROR, CMD_PARSE_SUCCESS }; diff --git a/window-customize.c b/window-customize.c index 782542f7..0f09eba8 100644 --- a/window-customize.c +++ b/window-customize.c @@ -1185,9 +1185,6 @@ window_customize_set_command_callback(struct client *c, void *itemdata, pr = cmd_parse_from_string(s, NULL); switch (pr->status) { - case CMD_PARSE_EMPTY: - error = xstrdup("empty command"); - goto fail; case CMD_PARSE_ERROR: error = pr->error; goto fail; From 63b6eec27889e4c38b4a051a1bb3463f153cc2c8 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Aug 2021 17:41:19 +0000 Subject: [PATCH 0948/1006] Use new syntax for default key bindings. --- key-bindings.c | 504 ++++++++++++++++++++++++------------------------- 1 file changed, 252 insertions(+), 252 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index 1bdbee98..c0a959e2 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -340,291 +340,291 @@ key_bindings_init(void) { static const char *defaults[] = { /* Prefix keys. */ - "bind -N 'Send the prefix key' C-b send-prefix", - "bind -N 'Rotate through the panes' C-o rotate-window", - "bind -N 'Suspend the current client' C-z suspend-client", - "bind -N 'Select next layout' Space next-layout", - "bind -N 'Break pane to a new window' ! break-pane", - "bind -N 'Split window vertically' '\"' split-window", - "bind -N 'List all paste buffers' '#' list-buffers", - "bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"", - "bind -N 'Split window horizontally' % split-window -h", - "bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window", - "bind -N 'Prompt for window index to select' \"'\" command-prompt -T window-target -pindex \"select-window -t ':%%'\"", - "bind -N 'Switch to previous client' ( switch-client -p", - "bind -N 'Switch to next client' ) switch-client -n", - "bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"", - "bind -N 'Delete the most recent paste buffer' - delete-buffer", - "bind -N 'Move the current window' . command-prompt -T target \"move-window -t '%%'\"", - "bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'", - "bind -N 'Select window 0' 0 select-window -t:=0", - "bind -N 'Select window 1' 1 select-window -t:=1", - "bind -N 'Select window 2' 2 select-window -t:=2", - "bind -N 'Select window 3' 3 select-window -t:=3", - "bind -N 'Select window 4' 4 select-window -t:=4", - "bind -N 'Select window 5' 5 select-window -t:=5", - "bind -N 'Select window 6' 6 select-window -t:=6", - "bind -N 'Select window 7' 7 select-window -t:=7", - "bind -N 'Select window 8' 8 select-window -t:=8", - "bind -N 'Select window 9' 9 select-window -t:=9", - "bind -N 'Prompt for a command' : command-prompt", - "bind -N 'Move to the previously active pane' \\; last-pane", - "bind -N 'Choose a paste buffer from a list' = choose-buffer -Z", - "bind -N 'List key bindings' ? list-keys -N", - "bind -N 'Choose a client from a list' D choose-client -Z", - "bind -N 'Spread panes out evenly' E select-layout -E", - "bind -N 'Switch to the last client' L switch-client -l", - "bind -N 'Clear the marked pane' M select-pane -M", - "bind -N 'Enter copy mode' [ copy-mode", - "bind -N 'Paste the most recent paste buffer' ] paste-buffer -p", - "bind -N 'Create a new window' c new-window", - "bind -N 'Detach the current client' d detach-client", - "bind -N 'Search for a pane' f command-prompt \"find-window -Z -- '%%'\"", - "bind -N 'Display window information' i display-message", - "bind -N 'Select the previously current window' l last-window", - "bind -N 'Toggle the marked pane' m select-pane -m", - "bind -N 'Select the next window' n next-window", - "bind -N 'Select the next pane' o select-pane -t:.+", - "bind -N 'Customize options' C customize-mode -Z", - "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", - "bind -N 'Show a clock' t clock-mode", - "bind -N 'Choose a window from a list' w choose-tree -Zw", - "bind -N 'Kill the active pane' x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane", - "bind -N 'Zoom the active pane' z resize-pane -Z", - "bind -N 'Swap the active pane with the pane above' '{' swap-pane -U", - "bind -N 'Swap the active pane with the pane below' '}' swap-pane -D", - "bind -N 'Show messages' '~' show-messages", - "bind -N 'Enter copy mode and scroll up' PPage copy-mode -u", - "bind -N 'Select the pane above the active pane' -r Up select-pane -U", - "bind -N 'Select the pane below the active pane' -r Down select-pane -D", - "bind -N 'Select the pane to the left of the active pane' -r Left select-pane -L", - "bind -N 'Select the pane to the right of the active pane' -r Right select-pane -R", - "bind -N 'Set the even-horizontal layout' M-1 select-layout even-horizontal", - "bind -N 'Set the even-vertical layout' M-2 select-layout even-vertical", - "bind -N 'Set the main-horizontal layout' M-3 select-layout main-horizontal", - "bind -N 'Set the main-vertical layout' M-4 select-layout main-vertical", - "bind -N 'Select the tiled layout' M-5 select-layout tiled", - "bind -N 'Select the next window with an alert' M-n next-window -a", - "bind -N 'Rotate through the panes in reverse' M-o rotate-window -D", - "bind -N 'Select the previous window with an alert' M-p previous-window -a", - "bind -N 'Move the visible part of the window up' -r S-Up refresh-client -U 10", - "bind -N 'Move the visible part of the window down' -r S-Down refresh-client -D 10", - "bind -N 'Move the visible part of the window left' -r S-Left refresh-client -L 10", - "bind -N 'Move the visible part of the window right' -r S-Right refresh-client -R 10", - "bind -N 'Reset so the visible part of the window follows the cursor' -r DC refresh-client -c", - "bind -N 'Resize the pane up by 5' -r M-Up resize-pane -U 5", - "bind -N 'Resize the pane down by 5' -r M-Down resize-pane -D 5", - "bind -N 'Resize the pane left by 5' -r M-Left resize-pane -L 5", - "bind -N 'Resize the pane right by 5' -r M-Right resize-pane -R 5", - "bind -N 'Resize the pane up' -r C-Up resize-pane -U", - "bind -N 'Resize the pane down' -r C-Down resize-pane -D", - "bind -N 'Resize the pane left' -r C-Left resize-pane -L", - "bind -N 'Resize the pane right' -r C-Right resize-pane -R", + "bind -N 'Send the prefix key' C-b { send-prefix }", + "bind -N 'Rotate through the panes' C-o { rotate-window }", + "bind -N 'Suspend the current client' C-z { suspend-client }", + "bind -N 'Select next layout' Space { next-layout }", + "bind -N 'Break pane to a new window' ! { break-pane }", + "bind -N 'Split window vertically' '\"' { split-window }", + "bind -N 'List all paste buffers' '#' { list-buffers }", + "bind -N 'Rename current session' '$' { command-prompt -I'#S' \"rename-session -- '%%'\" }", + "bind -N 'Split window horizontally' % { split-window -h }", + "bind -N 'Kill current window' & { confirm-before -p\"kill-window #W? (y/n)\" kill-window }", + "bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex \"select-window -t ':%%'\" }", + "bind -N 'Switch to previous client' ( { switch-client -p }", + "bind -N 'Switch to next client' ) { switch-client -n }", + "bind -N 'Rename current window' , { command-prompt -I'#W' \"rename-window -- '%%'\" }", + "bind -N 'Delete the most recent paste buffer' - { delete-buffer }", + "bind -N 'Move the current window' . { command-prompt -T target \"move-window -t '%%'\" }", + "bind -N 'Describe key binding' '/' { command-prompt -kpkey 'list-keys -1N \"%%%\"' }", + "bind -N 'Select window 0' 0 { select-window -t:=0 }", + "bind -N 'Select window 1' 1 { select-window -t:=1 }", + "bind -N 'Select window 2' 2 { select-window -t:=2 }", + "bind -N 'Select window 3' 3 { select-window -t:=3 }", + "bind -N 'Select window 4' 4 { select-window -t:=4 }", + "bind -N 'Select window 5' 5 { select-window -t:=5 }", + "bind -N 'Select window 6' 6 { select-window -t:=6 }", + "bind -N 'Select window 7' 7 { select-window -t:=7 }", + "bind -N 'Select window 8' 8 { select-window -t:=8 }", + "bind -N 'Select window 9' 9 { select-window -t:=9 }", + "bind -N 'Prompt for a command' : { command-prompt }", + "bind -N 'Move to the previously active pane' \\; { last-pane }", + "bind -N 'Choose a paste buffer from a list' = { choose-buffer -Z }", + "bind -N 'List key bindings' ? { list-keys -N }", + "bind -N 'Choose a client from a list' D { choose-client -Z }", + "bind -N 'Spread panes out evenly' E { select-layout -E }", + "bind -N 'Switch to the last client' L { switch-client -l }", + "bind -N 'Clear the marked pane' M { select-pane -M }", + "bind -N 'Enter copy mode' [ { copy-mode }", + "bind -N 'Paste the most recent paste buffer' ] { paste-buffer -p }", + "bind -N 'Create a new window' c { new-window }", + "bind -N 'Detach the current client' d { detach-client }", + "bind -N 'Search for a pane' f { command-prompt \"find-window -Z -- '%%'\" }", + "bind -N 'Display window information' i { display-message }", + "bind -N 'Select the previously current window' l { last-window }", + "bind -N 'Toggle the marked pane' m { select-pane -m }", + "bind -N 'Select the next window' n { next-window }", + "bind -N 'Select the next pane' o { select-pane -t:.+ }", + "bind -N 'Customize options' C { customize-mode -Z }", + "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 }", + "bind -N 'Show a clock' t { clock-mode }", + "bind -N 'Choose a window from a list' w { choose-tree -Zw }", + "bind -N 'Kill the active pane' x { confirm-before -p\"kill-pane #P? (y/n)\" kill-pane }", + "bind -N 'Zoom the active pane' z { resize-pane -Z }", + "bind -N 'Swap the active pane with the pane above' '{' { swap-pane -U }", + "bind -N 'Swap the active pane with the pane below' '}' { swap-pane -D }", + "bind -N 'Show messages' '~' { show-messages }", + "bind -N 'Enter copy mode and scroll up' PPage { copy-mode -u }", + "bind -N 'Select the pane above the active pane' -r Up { select-pane -U }", + "bind -N 'Select the pane below the active pane' -r Down { select-pane -D }", + "bind -N 'Select the pane to the left of the active pane' -r Left { select-pane -L }", + "bind -N 'Select the pane to the right of the active pane' -r Right { select-pane -R }", + "bind -N 'Set the even-horizontal layout' M-1 { select-layout even-horizontal }", + "bind -N 'Set the even-vertical layout' M-2 { select-layout even-vertical }", + "bind -N 'Set the main-horizontal layout' M-3 { select-layout main-horizontal }", + "bind -N 'Set the main-vertical layout' M-4 { select-layout main-vertical }", + "bind -N 'Select the tiled layout' M-5 { select-layout tiled }", + "bind -N 'Select the next window with an alert' M-n { next-window -a }", + "bind -N 'Rotate through the panes in reverse' M-o { rotate-window -D }", + "bind -N 'Select the previous window with an alert' M-p { previous-window -a }", + "bind -N 'Move the visible part of the window up' -r S-Up { refresh-client -U 10 }", + "bind -N 'Move the visible part of the window down' -r S-Down { refresh-client -D 10 }", + "bind -N 'Move the visible part of the window left' -r S-Left { refresh-client -L 10 }", + "bind -N 'Move the visible part of the window right' -r S-Right { refresh-client -R 10 }", + "bind -N 'Reset so the visible part of the window follows the cursor' -r DC { refresh-client -c }", + "bind -N 'Resize the pane up by 5' -r M-Up { resize-pane -U 5 }", + "bind -N 'Resize the pane down by 5' -r M-Down { resize-pane -D 5 }", + "bind -N 'Resize the pane left by 5' -r M-Left { resize-pane -L 5 }", + "bind -N 'Resize the pane right by 5' -r M-Right { resize-pane -R 5 }", + "bind -N 'Resize the pane up' -r C-Up { resize-pane -U }", + "bind -N 'Resize the pane down' -r C-Down { resize-pane -D }", + "bind -N 'Resize the pane left' -r C-Left { resize-pane -L }", + "bind -N 'Resize the pane right' -r C-Right { resize-pane -R }", /* Menu keys */ - "bind < display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, - "bind > display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU, + "bind < { display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU " }", + "bind > { display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", /* Mouse button 1 down on pane. */ - "bind -n MouseDown1Pane select-pane -t=\\; send -M", + "bind -n MouseDown1Pane { select-pane -t=; send -M }", /* Mouse button 1 drag on pane. */ - "bind -n MouseDrag1Pane if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M }", + "bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }", /* Mouse wheel up on pane. */ - "bind -n WheelUpPane if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -e }", + "bind -n WheelUpPane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -e } }", /* Mouse button 2 down on pane. */ - "bind -n MouseDown2Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { paste -p }", + "bind -n MouseDown2Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { paste -p } }", /* Mouse button 1 double click on pane. */ - "bind -n DoubleClick1Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-word; run -d0.3; send -X copy-pipe-and-cancel }", + "bind -n DoubleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-word; run -d0.3; send -X copy-pipe-and-cancel } }", /* Mouse button 1 triple click on pane. */ - "bind -n TripleClick1Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel }", + "bind -n TripleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel } }", /* Mouse button 1 drag on border. */ - "bind -n MouseDrag1Border resize-pane -M", + "bind -n MouseDrag1Border { resize-pane -M }", /* Mouse button 1 down on status line. */ - "bind -n MouseDown1Status select-window -t=", + "bind -n MouseDown1Status { select-window -t= }", /* Mouse wheel down on status line. */ - "bind -n WheelDownStatus next-window", + "bind -n WheelDownStatus { next-window }", /* Mouse wheel up on status line. */ - "bind -n WheelUpStatus previous-window", + "bind -n WheelUpStatus { previous-window }", /* Mouse button 3 down on status left. */ - "bind -n MouseDown3StatusLeft display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU, + "bind -n MouseDown3StatusLeft { display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU " }", /* Mouse button 3 down on status line. */ - "bind -n MouseDown3Status display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, + "bind -n MouseDown3Status { display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU "}", /* Mouse button 3 down on pane. */ - "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", - "bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU, + "bind -n MouseDown3Pane { if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " } }", + "bind -n M-MouseDown3Pane { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", /* Copy mode (emacs) keys. */ - "bind -Tcopy-mode C-Space send -X begin-selection", - "bind -Tcopy-mode C-a send -X start-of-line", - "bind -Tcopy-mode C-c send -X cancel", - "bind -Tcopy-mode C-e send -X end-of-line", - "bind -Tcopy-mode C-f send -X cursor-right", - "bind -Tcopy-mode C-b send -X cursor-left", - "bind -Tcopy-mode C-g send -X clear-selection", - "bind -Tcopy-mode C-k send -X copy-pipe-end-of-line-and-cancel", - "bind -Tcopy-mode C-n send -X cursor-down", - "bind -Tcopy-mode C-p send -X cursor-up", - "bind -Tcopy-mode C-r command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", - "bind -Tcopy-mode C-s command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'", - "bind -Tcopy-mode C-v send -X page-down", - "bind -Tcopy-mode C-w send -X copy-pipe-and-cancel", - "bind -Tcopy-mode Escape send -X cancel", - "bind -Tcopy-mode Space send -X page-down", - "bind -Tcopy-mode , send -X jump-reverse", - "bind -Tcopy-mode \\; send -X jump-again", - "bind -Tcopy-mode F command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"'", - "bind -Tcopy-mode N send -X search-reverse", - "bind -Tcopy-mode R send -X rectangle-toggle", - "bind -Tcopy-mode T command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"'", - "bind -Tcopy-mode X send -X set-mark", - "bind -Tcopy-mode f command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"'", - "bind -Tcopy-mode g command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", - "bind -Tcopy-mode n send -X search-again", - "bind -Tcopy-mode q send -X cancel", - "bind -Tcopy-mode r send -X refresh-from-pane", - "bind -Tcopy-mode t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'", - "bind -Tcopy-mode Home send -X start-of-line", - "bind -Tcopy-mode End send -X end-of-line", + "bind -Tcopy-mode C-Space { send -X begin-selection }", + "bind -Tcopy-mode C-a { send -X start-of-line }", + "bind -Tcopy-mode C-c { send -X cancel }", + "bind -Tcopy-mode C-e { send -X end-of-line }", + "bind -Tcopy-mode C-f { send -X cursor-right }", + "bind -Tcopy-mode C-b { send -X cursor-left }", + "bind -Tcopy-mode C-g { send -X clear-selection }", + "bind -Tcopy-mode C-k { send -X copy-pipe-end-of-line-and-cancel }", + "bind -Tcopy-mode C-n { send -X cursor-down }", + "bind -Tcopy-mode C-p { send -X cursor-up }", + "bind -Tcopy-mode C-r { command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"' }", + "bind -Tcopy-mode C-s { command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"' }", + "bind -Tcopy-mode C-v { send -X page-down }", + "bind -Tcopy-mode C-w { send -X copy-pipe-and-cancel }", + "bind -Tcopy-mode Escape { send -X cancel }", + "bind -Tcopy-mode Space { send -X page-down }", + "bind -Tcopy-mode , { send -X jump-reverse }", + "bind -Tcopy-mode \\; { send -X jump-again }", + "bind -Tcopy-mode F { command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"' }", + "bind -Tcopy-mode N { send -X search-reverse }", + "bind -Tcopy-mode R { send -X rectangle-toggle }", + "bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"' }", + "bind -Tcopy-mode X { send -X set-mark }", + "bind -Tcopy-mode f { command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"' }", + "bind -Tcopy-mode g { command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"' }", + "bind -Tcopy-mode n { send -X search-again }", + "bind -Tcopy-mode q { send -X cancel }", + "bind -Tcopy-mode r { send -X refresh-from-pane }", + "bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"' }", + "bind -Tcopy-mode Home { send -X start-of-line }", + "bind -Tcopy-mode End { send -X end-of-line }", "bind -Tcopy-mode MouseDown1Pane select-pane", - "bind -Tcopy-mode MouseDrag1Pane select-pane\\; send -X begin-selection", - "bind -Tcopy-mode MouseDragEnd1Pane send -X copy-pipe-and-cancel", - "bind -Tcopy-mode WheelUpPane select-pane\\; send -N5 -X scroll-up", - "bind -Tcopy-mode WheelDownPane select-pane\\; send -N5 -X scroll-down", - "bind -Tcopy-mode DoubleClick1Pane select-pane\\; send -X select-word\\; run -d0.3\\; send -X copy-pipe-and-cancel", - "bind -Tcopy-mode TripleClick1Pane select-pane\\; send -X select-line\\; run -d0.3\\; send -X copy-pipe-and-cancel", - "bind -Tcopy-mode NPage send -X page-down", - "bind -Tcopy-mode PPage send -X page-up", - "bind -Tcopy-mode Up send -X cursor-up", - "bind -Tcopy-mode Down send -X cursor-down", - "bind -Tcopy-mode Left send -X cursor-left", - "bind -Tcopy-mode Right send -X cursor-right", - "bind -Tcopy-mode M-1 command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"'", - "bind -Tcopy-mode M-2 command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"'", - "bind -Tcopy-mode M-3 command-prompt -Np'(repeat)' -I3 'send -N \"%%%\"'", - "bind -Tcopy-mode M-4 command-prompt -Np'(repeat)' -I4 'send -N \"%%%\"'", - "bind -Tcopy-mode M-5 command-prompt -Np'(repeat)' -I5 'send -N \"%%%\"'", - "bind -Tcopy-mode M-6 command-prompt -Np'(repeat)' -I6 'send -N \"%%%\"'", - "bind -Tcopy-mode M-7 command-prompt -Np'(repeat)' -I7 'send -N \"%%%\"'", - "bind -Tcopy-mode M-8 command-prompt -Np'(repeat)' -I8 'send -N \"%%%\"'", - "bind -Tcopy-mode M-9 command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"'", - "bind -Tcopy-mode M-< send -X history-top", - "bind -Tcopy-mode M-> send -X history-bottom", - "bind -Tcopy-mode M-R send -X top-line", - "bind -Tcopy-mode M-b send -X previous-word", - "bind -Tcopy-mode C-M-b send -X previous-matching-bracket", - "bind -Tcopy-mode M-f send -X next-word-end", - "bind -Tcopy-mode C-M-f send -X next-matching-bracket", - "bind -Tcopy-mode M-m send -X back-to-indentation", - "bind -Tcopy-mode M-r send -X middle-line", - "bind -Tcopy-mode M-v send -X page-up", - "bind -Tcopy-mode M-w send -X copy-pipe-and-cancel", - "bind -Tcopy-mode M-x send -X jump-to-mark", - "bind -Tcopy-mode 'M-{' send -X previous-paragraph", - "bind -Tcopy-mode 'M-}' send -X next-paragraph", - "bind -Tcopy-mode M-Up send -X halfpage-up", - "bind -Tcopy-mode M-Down send -X halfpage-down", - "bind -Tcopy-mode C-Up send -X scroll-up", - "bind -Tcopy-mode C-Down send -X scroll-down", + "bind -Tcopy-mode MouseDrag1Pane { select-pane; send -X begin-selection }", + "bind -Tcopy-mode MouseDragEnd1Pane { send -X copy-pipe-and-cancel }", + "bind -Tcopy-mode WheelUpPane { select-pane; send -N5 -X scroll-up }", + "bind -Tcopy-mode WheelDownPane { select-pane; send -N5 -X scroll-down }", + "bind -Tcopy-mode DoubleClick1Pane { select-pane; send -X select-word; run -d0.3; send -X copy-pipe-and-cancel }", + "bind -Tcopy-mode TripleClick1Pane { select-pane; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel }", + "bind -Tcopy-mode NPage { send -X page-down }", + "bind -Tcopy-mode PPage { send -X page-up }", + "bind -Tcopy-mode Up { send -X cursor-up }", + "bind -Tcopy-mode Down { send -X cursor-down }", + "bind -Tcopy-mode Left { send -X cursor-left }", + "bind -Tcopy-mode Right { send -X cursor-right }", + "bind -Tcopy-mode M-1 { command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"' }", + "bind -Tcopy-mode M-2 { command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"' }", + "bind -Tcopy-mode M-3 { command-prompt -Np'(repeat)' -I3 'send -N \"%%%\"' }", + "bind -Tcopy-mode M-4 { command-prompt -Np'(repeat)' -I4 'send -N \"%%%\"' }", + "bind -Tcopy-mode M-5 { command-prompt -Np'(repeat)' -I5 'send -N \"%%%\"' }", + "bind -Tcopy-mode M-6 { command-prompt -Np'(repeat)' -I6 'send -N \"%%%\"' }", + "bind -Tcopy-mode M-7 { command-prompt -Np'(repeat)' -I7 'send -N \"%%%\"' }", + "bind -Tcopy-mode M-8 { command-prompt -Np'(repeat)' -I8 'send -N \"%%%\"' }", + "bind -Tcopy-mode M-9 { command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"' }", + "bind -Tcopy-mode M-< { send -X history-top }", + "bind -Tcopy-mode M-> { send -X history-bottom }", + "bind -Tcopy-mode M-R { send -X top-line }", + "bind -Tcopy-mode M-b { send -X previous-word }", + "bind -Tcopy-mode C-M-b { send -X previous-matching-bracket }", + "bind -Tcopy-mode M-f { send -X next-word-end }", + "bind -Tcopy-mode C-M-f { send -X next-matching-bracket }", + "bind -Tcopy-mode M-m { send -X back-to-indentation }", + "bind -Tcopy-mode M-r { send -X middle-line }", + "bind -Tcopy-mode M-v { send -X page-up }", + "bind -Tcopy-mode M-w { send -X copy-pipe-and-cancel }", + "bind -Tcopy-mode M-x { send -X jump-to-mark }", + "bind -Tcopy-mode 'M-{' { send -X previous-paragraph }", + "bind -Tcopy-mode 'M-}' { send -X next-paragraph }", + "bind -Tcopy-mode M-Up { send -X halfpage-up }", + "bind -Tcopy-mode M-Down { send -X halfpage-down }", + "bind -Tcopy-mode C-Up { send -X scroll-up }", + "bind -Tcopy-mode C-Down { send -X scroll-down }", /* Copy mode (vi) keys. */ - "bind -Tcopy-mode-vi '#' send -FX search-backward '#{copy_cursor_word}'", - "bind -Tcopy-mode-vi * send -FX search-forward '#{copy_cursor_word}'", - "bind -Tcopy-mode-vi C-c send -X cancel", - "bind -Tcopy-mode-vi C-d send -X halfpage-down", - "bind -Tcopy-mode-vi C-e send -X scroll-down", - "bind -Tcopy-mode-vi C-b send -X page-up", - "bind -Tcopy-mode-vi C-f send -X page-down", - "bind -Tcopy-mode-vi C-h send -X cursor-left", - "bind -Tcopy-mode-vi C-j send -X copy-pipe-and-cancel", - "bind -Tcopy-mode-vi Enter send -X copy-pipe-and-cancel", - "bind -Tcopy-mode-vi C-u send -X halfpage-up", - "bind -Tcopy-mode-vi C-v send -X rectangle-toggle", - "bind -Tcopy-mode-vi C-y send -X scroll-up", - "bind -Tcopy-mode-vi Escape send -X clear-selection", - "bind -Tcopy-mode-vi Space send -X begin-selection", - "bind -Tcopy-mode-vi '$' send -X end-of-line", - "bind -Tcopy-mode-vi , send -X jump-reverse", - "bind -Tcopy-mode-vi / command-prompt -T search -p'(search down)' 'send -X search-forward \"%%%\"'", - "bind -Tcopy-mode-vi 0 send -X start-of-line", - "bind -Tcopy-mode-vi 1 command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"'", - "bind -Tcopy-mode-vi 2 command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"'", - "bind -Tcopy-mode-vi 3 command-prompt -Np'(repeat)' -I3 'send -N \"%%%\"'", - "bind -Tcopy-mode-vi 4 command-prompt -Np'(repeat)' -I4 'send -N \"%%%\"'", - "bind -Tcopy-mode-vi 5 command-prompt -Np'(repeat)' -I5 'send -N \"%%%\"'", - "bind -Tcopy-mode-vi 6 command-prompt -Np'(repeat)' -I6 'send -N \"%%%\"'", - "bind -Tcopy-mode-vi 7 command-prompt -Np'(repeat)' -I7 'send -N \"%%%\"'", - "bind -Tcopy-mode-vi 8 command-prompt -Np'(repeat)' -I8 'send -N \"%%%\"'", - "bind -Tcopy-mode-vi 9 command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"'", - "bind -Tcopy-mode-vi : command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", - "bind -Tcopy-mode-vi \\; send -X jump-again", - "bind -Tcopy-mode-vi ? command-prompt -T search -p'(search up)' 'send -X search-backward \"%%%\"'", - "bind -Tcopy-mode-vi A send -X append-selection-and-cancel", - "bind -Tcopy-mode-vi B send -X previous-space", - "bind -Tcopy-mode-vi D send -X copy-pipe-end-of-line-and-cancel", - "bind -Tcopy-mode-vi E send -X next-space-end", - "bind -Tcopy-mode-vi F command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"'", - "bind -Tcopy-mode-vi G send -X history-bottom", - "bind -Tcopy-mode-vi H send -X top-line", - "bind -Tcopy-mode-vi J send -X scroll-down", - "bind -Tcopy-mode-vi K send -X scroll-up", - "bind -Tcopy-mode-vi L send -X bottom-line", - "bind -Tcopy-mode-vi M send -X middle-line", - "bind -Tcopy-mode-vi N send -X search-reverse", - "bind -Tcopy-mode-vi T command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"'", - "bind -Tcopy-mode-vi V send -X select-line", - "bind -Tcopy-mode-vi W send -X next-space", - "bind -Tcopy-mode-vi X send -X set-mark", - "bind -Tcopy-mode-vi ^ send -X back-to-indentation", - "bind -Tcopy-mode-vi b send -X previous-word", - "bind -Tcopy-mode-vi e send -X next-word-end", - "bind -Tcopy-mode-vi f command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"'", - "bind -Tcopy-mode-vi g send -X history-top", - "bind -Tcopy-mode-vi h send -X cursor-left", - "bind -Tcopy-mode-vi j send -X cursor-down", - "bind -Tcopy-mode-vi k send -X cursor-up", - "bind -Tcopy-mode-vi l send -X cursor-right", - "bind -Tcopy-mode-vi n send -X search-again", - "bind -Tcopy-mode-vi o send -X other-end", - "bind -Tcopy-mode-vi q send -X cancel", - "bind -Tcopy-mode-vi r send -X refresh-from-pane", - "bind -Tcopy-mode-vi t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'", - "bind -Tcopy-mode-vi v send -X rectangle-toggle", - "bind -Tcopy-mode-vi w send -X next-word", - "bind -Tcopy-mode-vi '{' send -X previous-paragraph", - "bind -Tcopy-mode-vi '}' send -X next-paragraph", - "bind -Tcopy-mode-vi % send -X next-matching-bracket", - "bind -Tcopy-mode-vi MouseDown1Pane select-pane", - "bind -Tcopy-mode-vi MouseDrag1Pane select-pane\\; send -X begin-selection", - "bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-pipe-and-cancel", - "bind -Tcopy-mode-vi WheelUpPane select-pane\\; send -N5 -X scroll-up", - "bind -Tcopy-mode-vi WheelDownPane select-pane\\; send -N5 -X scroll-down", - "bind -Tcopy-mode-vi DoubleClick1Pane select-pane\\; send -X select-word\\; run -d0.3\\; send -X copy-pipe-and-cancel", - "bind -Tcopy-mode-vi TripleClick1Pane select-pane\\; send -X select-line\\; run -d0.3\\; send -X copy-pipe-and-cancel", - "bind -Tcopy-mode-vi BSpace send -X cursor-left", - "bind -Tcopy-mode-vi NPage send -X page-down", - "bind -Tcopy-mode-vi PPage send -X page-up", - "bind -Tcopy-mode-vi Up send -X cursor-up", - "bind -Tcopy-mode-vi Down send -X cursor-down", - "bind -Tcopy-mode-vi Left send -X cursor-left", - "bind -Tcopy-mode-vi Right send -X cursor-right", - "bind -Tcopy-mode-vi M-x send -X jump-to-mark", - "bind -Tcopy-mode-vi C-Up send -X scroll-up", - "bind -Tcopy-mode-vi C-Down send -X scroll-down", + "bind -Tcopy-mode-vi '#' { send -FX search-backward '#{copy_cursor_word}' }", + "bind -Tcopy-mode-vi * { send -FX search-forward '#{copy_cursor_word}' }", + "bind -Tcopy-mode-vi C-c { send -X cancel }", + "bind -Tcopy-mode-vi C-d { send -X halfpage-down }", + "bind -Tcopy-mode-vi C-e { send -X scroll-down }", + "bind -Tcopy-mode-vi C-b { send -X page-up }", + "bind -Tcopy-mode-vi C-f { send -X page-down }", + "bind -Tcopy-mode-vi C-h { send -X cursor-left }", + "bind -Tcopy-mode-vi C-j { send -X copy-pipe-and-cancel }", + "bind -Tcopy-mode-vi Enter { send -X copy-pipe-and-cancel }", + "bind -Tcopy-mode-vi C-u { send -X halfpage-up }", + "bind -Tcopy-mode-vi C-v { send -X rectangle-toggle }", + "bind -Tcopy-mode-vi C-y { send -X scroll-up }", + "bind -Tcopy-mode-vi Escape { send -X clear-selection }", + "bind -Tcopy-mode-vi Space { send -X begin-selection }", + "bind -Tcopy-mode-vi '$' { send -X end-of-line }", + "bind -Tcopy-mode-vi , { send -X jump-reverse }", + "bind -Tcopy-mode-vi / { command-prompt -T search -p'(search down)' 'send -X search-forward \"%%%\"' }", + "bind -Tcopy-mode-vi 0 { send -X start-of-line }", + "bind -Tcopy-mode-vi 1 { command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"' }", + "bind -Tcopy-mode-vi 2 { command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"' }", + "bind -Tcopy-mode-vi 3 { command-prompt -Np'(repeat)' -I3 'send -N \"%%%\"' }", + "bind -Tcopy-mode-vi 4 { command-prompt -Np'(repeat)' -I4 'send -N \"%%%\"' }", + "bind -Tcopy-mode-vi 5 { command-prompt -Np'(repeat)' -I5 'send -N \"%%%\"' }", + "bind -Tcopy-mode-vi 6 { command-prompt -Np'(repeat)' -I6 'send -N \"%%%\"' }", + "bind -Tcopy-mode-vi 7 { command-prompt -Np'(repeat)' -I7 'send -N \"%%%\"' }", + "bind -Tcopy-mode-vi 8 { command-prompt -Np'(repeat)' -I8 'send -N \"%%%\"' }", + "bind -Tcopy-mode-vi 9 { command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"' }", + "bind -Tcopy-mode-vi : { command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"' }", + "bind -Tcopy-mode-vi \\; { send -X jump-again }", + "bind -Tcopy-mode-vi ? { command-prompt -T search -p'(search up)' 'send -X search-backward \"%%%\"' }", + "bind -Tcopy-mode-vi A { send -X append-selection-and-cancel }", + "bind -Tcopy-mode-vi B { send -X previous-space }", + "bind -Tcopy-mode-vi D { send -X copy-pipe-end-of-line-and-cancel }", + "bind -Tcopy-mode-vi E { send -X next-space-end }", + "bind -Tcopy-mode-vi F { command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"' }", + "bind -Tcopy-mode-vi G { send -X history-bottom }", + "bind -Tcopy-mode-vi H { send -X top-line }", + "bind -Tcopy-mode-vi J { send -X scroll-down }", + "bind -Tcopy-mode-vi K { send -X scroll-up }", + "bind -Tcopy-mode-vi L { send -X bottom-line }", + "bind -Tcopy-mode-vi M { send -X middle-line }", + "bind -Tcopy-mode-vi N { send -X search-reverse }", + "bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"' }", + "bind -Tcopy-mode-vi V { send -X select-line }", + "bind -Tcopy-mode-vi W { send -X next-space }", + "bind -Tcopy-mode-vi X { send -X set-mark }", + "bind -Tcopy-mode-vi ^ { send -X back-to-indentation }", + "bind -Tcopy-mode-vi b { send -X previous-word }", + "bind -Tcopy-mode-vi e { send -X next-word-end }", + "bind -Tcopy-mode-vi f { command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"' }", + "bind -Tcopy-mode-vi g { send -X history-top }", + "bind -Tcopy-mode-vi h { send -X cursor-left }", + "bind -Tcopy-mode-vi j { send -X cursor-down }", + "bind -Tcopy-mode-vi k { send -X cursor-up }", + "bind -Tcopy-mode-vi l { send -X cursor-right }", + "bind -Tcopy-mode-vi n { send -X search-again }", + "bind -Tcopy-mode-vi o { send -X other-end }", + "bind -Tcopy-mode-vi q { send -X cancel }", + "bind -Tcopy-mode-vi r { send -X refresh-from-pane }", + "bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"' }", + "bind -Tcopy-mode-vi v { send -X rectangle-toggle }", + "bind -Tcopy-mode-vi w { send -X next-word }", + "bind -Tcopy-mode-vi '{' { send -X previous-paragraph }", + "bind -Tcopy-mode-vi '}' { send -X next-paragraph }", + "bind -Tcopy-mode-vi % { send -X next-matching-bracket }", + "bind -Tcopy-mode-vi MouseDown1Pane { select-pane }", + "bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }", + "bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }", + "bind -Tcopy-mode-vi WheelUpPane { select-pane; send -N5 -X scroll-up }", + "bind -Tcopy-mode-vi WheelDownPane { select-pane; send -N5 -X scroll-down }", + "bind -Tcopy-mode-vi DoubleClick1Pane { select-pane; send -X select-word; run -d0.3; send -X copy-pipe-and-cancel }", + "bind -Tcopy-mode-vi TripleClick1Pane { select-pane; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel }", + "bind -Tcopy-mode-vi BSpace { send -X cursor-left }", + "bind -Tcopy-mode-vi NPage { send -X page-down }", + "bind -Tcopy-mode-vi PPage { send -X page-up }", + "bind -Tcopy-mode-vi Up { send -X cursor-up }", + "bind -Tcopy-mode-vi Down { send -X cursor-down }", + "bind -Tcopy-mode-vi Left { send -X cursor-left }", + "bind -Tcopy-mode-vi Right { send -X cursor-right }", + "bind -Tcopy-mode-vi M-x { send -X jump-to-mark }", + "bind -Tcopy-mode-vi C-Up { send -X scroll-up }", + "bind -Tcopy-mode-vi C-Down { send -X scroll-down }", }; u_int i; struct cmd_parse_result *pr; From 326d2ef234cd8838700e914a0d780f46be50904c Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Aug 2021 18:39:07 +0000 Subject: [PATCH 0949/1006] Pass typed arguments out of the parser into the arguments list and let it convert them into strings. --- arguments.c | 125 +++++++++++++++++++++++++++++++++++++++++++--------- cmd-parse.y | 29 +++++++----- cmd.c | 25 ++++------- tmux.h | 20 +++++++-- 4 files changed, 148 insertions(+), 51 deletions(-) diff --git a/arguments.c b/arguments.c index 2f9c0c31..4fc83a0a 100644 --- a/arguments.c +++ b/arguments.c @@ -18,9 +18,9 @@ #include +#include #include #include -#include #include #include "tmux.h" @@ -66,6 +66,20 @@ args_find(struct args *args, u_char flag) return (RB_FIND(args_tree, &args->tree, &entry)); } +/* Get value as string. */ +static char * +args_value_as_string(struct args_value *value) +{ + switch (value->type) { + case ARGS_NONE: + return (xstrdup("")); + case ARGS_COMMANDS: + return (cmd_list_print(value->cmdlist, 0)); + case ARGS_STRING: + return (xstrdup(value->string)); + } +} + /* Create an empty arguments set. */ struct args * args_create(void) @@ -77,42 +91,109 @@ args_create(void) return (args); } -/* Parse an argv and argc into a new argument set. */ +/* Parse arguments into a new argument set. */ struct args * -args_parse(const struct args_parse *parse, int argc, char **argv) +args_parse(const struct args_parse *parse, struct args_value *values, + u_int count) { - struct args *args; - int opt; + struct args *args; + u_int i; + struct args_value *value; + u_char flag, argument; + const char *found, *string; + char *s; - optreset = 1; - optind = 1; - optarg = NULL; + if (count == 0) + return (args_create()); args = args_create(); - while ((opt = getopt(argc, argv, parse->template)) != -1) { - if (opt < 0) - continue; - if (opt == '?' || strchr(parse->template, opt) == NULL) { - args_free(args); - return (NULL); + for (i = 1; i < count; /* nothing */) { + value = &values[i]; + + s = args_value_as_string(value); + log_debug("%s: %u = %s", __func__, i, s); + free(s); + + if (value->type != ARGS_STRING) + break; + + string = value->string; + if (*string++ != '-' || *string == '\0') + break; + i++; + if (string[0] == '-' && string[1] == '\0') + break; + + for (;;) { + flag = *string++; + if (flag == '\0') + break; + if (!isalnum(flag)) { + args_free(args); + return (NULL); + } + found = strchr(parse->template, flag); + if (found == NULL) { + args_free(args); + return (NULL); + } + argument = *++found; + if (argument != ':') { + log_debug("%s: add -%c", __func__, flag); + args_set(args, flag, NULL); + continue; + } + if (*string != '\0') + s = xstrdup(string); + else { + if (i == count) { + args_free(args); + return (NULL); + } + s = args_value_as_string(&values[i++]); + } + log_debug("%s: add -%c = %s", __func__, flag, s); + args_set(args, flag, s); + free(s); + break; } - args_set(args, opt, optarg); - optarg = NULL; } - argc -= optind; - argv += optind; + log_debug("%s: flags end at %u of %u", __func__, i, count); + if (i != count) { + for (/* nothing */; i < count; i++) { + value = &values[i]; - args->argc = argc; - args->argv = cmd_copy_argv(argc, argv); + s = args_value_as_string(value); + log_debug("%s: %u = %s", __func__, i, s); + cmd_append_argv(&args->argc, &args->argv, s); + free(s); + } + } - if ((parse->lower != -1 && argc < parse->lower) || - (parse->upper != -1 && argc > parse->upper)) { + if ((parse->lower != -1 && args->argc < parse->lower) || + (parse->upper != -1 && args->argc > parse->upper)) { args_free(args); return (NULL); } return (args); } +/* Free a value. */ +void +args_free_value(struct args_value *value) +{ + switch (value->type) { + case ARGS_NONE: + break; + case ARGS_STRING: + free(value->string); + break; + case ARGS_COMMANDS: + cmd_list_free(value->cmdlist); + break; + } +} + /* Free an arguments set. */ void args_free(struct args *args) diff --git a/cmd-parse.y b/cmd-parse.y index 6be5d8a0..57d0e84e 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -794,39 +794,48 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, struct cmd_parse_input *pi, struct cmd_parse_result *pr) { struct cmd_parse_argument *arg; - struct cmd_list *cmdlist; + struct cmd_list *cmdlist = NULL; struct cmd *add; - char *s, **argv = NULL, *cause; - int argc = 0; + char *cause; + struct args_value *values = NULL; + u_int count = 0, idx; if (cmd_parse_expand_alias(cmd, pi, pr, &cmdlist)) return (cmdlist); TAILQ_FOREACH(arg, &cmd->arguments, entry) { + values = xreallocarray(values, count + 1, sizeof *values); switch (arg->type) { case CMD_PARSE_STRING: - cmd_append_argv(&argc, &argv, arg->string); + values[count].type = ARGS_STRING; + values[count].string = xstrdup(arg->string); break; case CMD_PARSE_COMMANDS: cmd_parse_build_commands(arg->commands, pi, pr); if (pr->status != CMD_PARSE_SUCCESS) - return (NULL); - s = cmd_list_print(pr->cmdlist, 0); - cmd_append_argv(&argc, &argv, s); - free(s); + goto out; + values[count].type = ARGS_COMMANDS; + values[count].cmdlist = pr->cmdlist; + values[count].cmdlist->references++; break; } + count++; } - add = cmd_parse(argc, argv, pi->file, pi->line, &cause); + add = cmd_parse(values, count, pi->file, pi->line, &cause); if (add == NULL) { pr->status = CMD_PARSE_ERROR; pr->error = cmd_parse_get_error(pi->file, pi->line, cause); free(cause); - return (NULL); + goto out; } cmdlist = cmd_list_new(); cmd_list_append(cmdlist, add); + +out: + for (idx = 0; idx < count; idx++) + args_free_value(&values[idx]); + free(values); return (cmdlist); } diff --git a/cmd.c b/cmd.c index ce04ce2a..930ee56c 100644 --- a/cmd.c +++ b/cmd.c @@ -496,27 +496,26 @@ ambiguous: /* Parse a single command from an argument vector. */ struct cmd * -cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) +cmd_parse(struct args_value *values, u_int count, const char *file, u_int line, + char **cause) { const struct cmd_entry *entry; - const char *name; struct cmd *cmd; struct args *args; - if (argc == 0) { + if (count == 0 || values[0].type != ARGS_STRING) { xasprintf(cause, "no command"); return (NULL); } - name = argv[0]; - - entry = cmd_find(name, cause); + entry = cmd_find(values[0].string, cause); if (entry == NULL) return (NULL); - cmd_log_argv(argc, argv, "%s: %s", __func__, entry->name); - args = args_parse(&entry->args, argc, argv); - if (args == NULL) - goto usage; + args = args_parse(&entry->args, values, count); + if (args == NULL) { + xasprintf(cause, "usage: %s %s", entry->name, entry->usage); + return (NULL); + } cmd = xcalloc(1, sizeof *cmd); cmd->entry = entry; @@ -527,12 +526,6 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) cmd->line = line; return (cmd); - -usage: - if (args != NULL) - args_free(args); - xasprintf(cause, "usage: %s %s", entry->name, entry->usage); - return (NULL); } /* Free a command. */ diff --git a/tmux.h b/tmux.h index f8dcac49..34c66493 100644 --- a/tmux.h +++ b/tmux.h @@ -1355,9 +1355,20 @@ struct message_entry { }; TAILQ_HEAD(message_list, message_entry); +/* Argument type. */ +enum args_type { + ARGS_NONE, + ARGS_STRING, + ARGS_COMMANDS +}; + /* Argument value. */ struct args_value { - char *string; + enum args_type type; + union { + char *string; + struct cmd_list *cmdlist; + }; TAILQ_ENTRY(args_value) entry; }; @@ -2187,8 +2198,10 @@ int tty_keys_next(struct tty *); /* arguments.c */ void args_set(struct args *, u_char, const char *); struct args *args_create(void); -struct args *args_parse(const struct args_parse *, int, char **); +struct args *args_parse(const struct args_parse *, struct args_value *, + u_int); void args_vector(struct args *, int *, char ***); +void args_free_value(struct args_value *); void args_free(struct args *); char *args_print(struct args *); char *args_escape(const char *); @@ -2250,7 +2263,8 @@ const struct cmd_entry *cmd_get_entry(struct cmd *); struct args *cmd_get_args(struct cmd *); u_int cmd_get_group(struct cmd *); void cmd_get_source(struct cmd *, const char **, u_int *); -struct cmd *cmd_parse(int, char **, const char *, u_int, char **); +struct cmd *cmd_parse(struct args_value *, u_int, const char *, u_int, + char **); void cmd_free(struct cmd *); char *cmd_print(struct cmd *); struct cmd_list *cmd_list_new(void); From 069f5925af8bf70a99eec7f4baf5772707e62def Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Aug 2021 20:46:43 +0000 Subject: [PATCH 0950/1006] Preserve argument type in command and convert to string on demand. --- cmd-bind-key.c | 45 +++++++++++++++++++++++++++------------------ cmd-find-window.c | 34 +++++++++++++++++++++------------- cmd-parse.y | 3 ++- tmux.h | 4 +++- 4 files changed, 53 insertions(+), 33 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 97057b3a..4a6c8541 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -50,6 +50,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) struct cmd_parse_result *pr; char **argv; int argc, repeat; + struct args_value *value; u_int count = args_count(args); key = key_string_lookup_string(args_string(args, 0)); @@ -66,24 +67,32 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) tablename = "prefix"; repeat = args_has(args, 'r'); - if (count != 1) { - if (count == 2) - pr = cmd_parse_from_string(args_string(args, 1), NULL); - else { - args_vector(args, &argc, &argv); - pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL); - cmd_free_argv(argc, argv); - } - switch (pr->status) { - 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 + if (count == 1) { key_bindings_add(tablename, key, note, repeat, NULL); + return (CMD_RETURN_NORMAL); + } + + value = args_value(args, 1); + if (count == 2 && value->type == ARGS_COMMANDS) { + key_bindings_add(tablename, key, note, repeat, value->cmdlist); + return (CMD_RETURN_NORMAL); + } + + if (count == 2) + pr = cmd_parse_from_string(args_string(args, 1), NULL); + else { + args_vector(args, &argc, &argv); + pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL); + cmd_free_argv(argc, argv); + } + switch (pr->status) { + 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); return (CMD_RETURN_NORMAL); } diff --git a/cmd-find-window.c b/cmd-find-window.c index 804e8fe4..6e07537c 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -48,7 +48,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *target = cmdq_get_target(item); struct window_pane *wp = target->wp; const char *s = args_string(args, 0), *suffix = ""; - char *filter; + struct args_value *filter; int C, N, T; C = args_has(args, 'C'); @@ -65,31 +65,41 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) if (!C && !N && !T) C = N = T = 1; + filter = xcalloc(1, sizeof *filter); + filter->type = ARGS_STRING; + if (C && N && T) { - xasprintf(&filter, + xasprintf(&filter->string, "#{||:" "#{C%s:%s},#{||:#{m%s:*%s*,#{window_name}}," "#{m%s:*%s*,#{pane_title}}}}", suffix, s, suffix, s, suffix, s); } else if (C && N) { - xasprintf(&filter, + xasprintf(&filter->string, "#{||:#{C%s:%s},#{m%s:*%s*,#{window_name}}}", suffix, s, suffix, s); } else if (C && T) { - xasprintf(&filter, + xasprintf(&filter->string, "#{||:#{C%s:%s},#{m%s:*%s*,#{pane_title}}}", suffix, s, suffix, s); } else if (N && T) { - xasprintf(&filter, + xasprintf(&filter->string, "#{||:#{m%s:*%s*,#{window_name}}," "#{m%s:*%s*,#{pane_title}}}", suffix, s, suffix, s); - } else if (C) - xasprintf(&filter, "#{C%s:%s}", suffix, s); - else if (N) - xasprintf(&filter, "#{m%s:*%s*,#{window_name}}", suffix, s); - else - xasprintf(&filter, "#{m%s:*%s*,#{pane_title}}", suffix, s); + } else if (C) { + xasprintf(&filter->string, + "#{C%s:%s}", + suffix, s); + } else if (N) { + xasprintf(&filter->string, + "#{m%s:*%s*,#{window_name}}", + suffix, s); + } else { + xasprintf(&filter->string, + "#{m%s:*%s*,#{pane_title}}", + suffix, s); + } new_args = args_create(); if (args_has(args, 'Z')) @@ -97,9 +107,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) args_set(new_args, 'f', filter); window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args); - args_free(new_args); - free(filter); return (CMD_RETURN_NORMAL); } diff --git a/cmd-parse.y b/cmd-parse.y index 57d0e84e..86917941 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -804,7 +804,8 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, return (cmdlist); TAILQ_FOREACH(arg, &cmd->arguments, entry) { - values = xreallocarray(values, count + 1, sizeof *values); + values = xrecallocarray(values, count, count + 1, + sizeof *values); switch (arg->type) { case CMD_PARSE_STRING: values[count].type = ARGS_STRING; diff --git a/tmux.h b/tmux.h index 34c66493..fa0268a2 100644 --- a/tmux.h +++ b/tmux.h @@ -1369,6 +1369,7 @@ struct args_value { char *string; struct cmd_list *cmdlist; }; + char *cached; TAILQ_ENTRY(args_value) entry; }; @@ -2196,7 +2197,7 @@ void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); /* arguments.c */ -void args_set(struct args *, u_char, const char *); +void args_set(struct args *, u_char, struct args_value *); struct args *args_create(void); struct args *args_parse(const struct args_parse *, struct args_value *, u_int); @@ -2210,6 +2211,7 @@ const char *args_get(struct args *, u_char); u_char args_first(struct args *, struct args_entry **); u_char args_next(struct args_entry **); u_int args_count(struct args *); +struct args_value *args_value(struct args *, u_int); const char *args_string(struct args *, u_int); struct args_value *args_first_value(struct args *, u_char); struct args_value *args_next_value(struct args_value *); From 0084cbef5ad9f1f860b50fb7c485ac841817b3a8 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 21 Aug 2021 20:57:52 +0000 Subject: [PATCH 0951/1006] Free value properly. --- arguments.c | 143 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 93 insertions(+), 50 deletions(-) diff --git a/arguments.c b/arguments.c index 4fc83a0a..49bc8b2a 100644 --- a/arguments.c +++ b/arguments.c @@ -39,9 +39,9 @@ struct args_entry { }; struct args { - struct args_tree tree; - int argc; - char **argv; + struct args_tree tree; + u_int count; + struct args_value *values; }; static struct args_entry *args_find(struct args *, u_char); @@ -66,17 +66,37 @@ args_find(struct args *args, u_char flag) return (RB_FIND(args_tree, &args->tree, &entry)); } +/* Copy value. */ +static void +args_copy_value(struct args_value *to, struct args_value *from) +{ + to->type = from->type; + switch (from->type) { + case ARGS_NONE: + break; + case ARGS_COMMANDS: + to->cmdlist = from->cmdlist; + to->cmdlist->references++; + break; + case ARGS_STRING: + to->string = xstrdup(from->string); + break; + } +} + /* Get value as string. */ -static char * +static const char * args_value_as_string(struct args_value *value) { switch (value->type) { case ARGS_NONE: - return (xstrdup("")); + return (""); case ARGS_COMMANDS: - return (cmd_list_print(value->cmdlist, 0)); + if (value->cached == NULL) + value->cached = cmd_list_print(value->cmdlist, 0); + return (value->cached); case ARGS_STRING: - return (xstrdup(value->string)); + return (value->string); } } @@ -98,10 +118,9 @@ args_parse(const struct args_parse *parse, struct args_value *values, { struct args *args; u_int i; - struct args_value *value; + struct args_value *value, *new; u_char flag, argument; - const char *found, *string; - char *s; + const char *found, *string, *s; if (count == 0) return (args_create()); @@ -109,11 +128,6 @@ args_parse(const struct args_parse *parse, struct args_value *values, args = args_create(); for (i = 1; i < count; /* nothing */) { value = &values[i]; - - s = args_value_as_string(value); - log_debug("%s: %u = %s", __func__, i, s); - free(s); - if (value->type != ARGS_STRING) break; @@ -143,18 +157,20 @@ args_parse(const struct args_parse *parse, struct args_value *values, args_set(args, flag, NULL); continue; } - if (*string != '\0') - s = xstrdup(string); - else { + new = xcalloc(1, sizeof *value); + if (*string != '\0') { + new->type = ARGS_STRING; + new->string = xstrdup(string); + } else { if (i == count) { args_free(args); return (NULL); } - s = args_value_as_string(&values[i++]); + args_copy_value(new, &values[i++]); } + s = args_value_as_string(new); log_debug("%s: add -%c = %s", __func__, flag, s); - args_set(args, flag, s); - free(s); + args_set(args, flag, new); break; } } @@ -165,13 +181,15 @@ args_parse(const struct args_parse *parse, struct args_value *values, s = args_value_as_string(value); log_debug("%s: %u = %s", __func__, i, s); - cmd_append_argv(&args->argc, &args->argv, s); - free(s); + + args->values = xrecallocarray(args->values, + args->count, args->count + 1, sizeof *args->values); + args_copy_value(&args->values[args->count++], value); } } - if ((parse->lower != -1 && args->argc < parse->lower) || - (parse->upper != -1 && args->argc > parse->upper)) { + if ((parse->lower != -1 && args->count < (u_int)parse->lower) || + (parse->upper != -1 && args->count > (u_int)parse->upper)) { args_free(args); return (NULL); } @@ -192,6 +210,7 @@ args_free_value(struct args_value *value) cmd_list_free(value->cmdlist); break; } + free(value->cached); } /* Free an arguments set. */ @@ -202,14 +221,17 @@ args_free(struct args *args) struct args_entry *entry1; struct args_value *value; struct args_value *value1; + u_int i; - cmd_free_argv(args->argc, args->argv); + for (i = 0; i < args->count; i++) + args_free_value(&args->values[i]); + free(args->values); RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { RB_REMOVE(args_tree, &args->tree, entry); TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) { TAILQ_REMOVE(&entry->values, value, entry); - free(value->string); + args_free_value(value); free(value); } free(entry); @@ -222,8 +244,16 @@ args_free(struct args *args) void args_vector(struct args *args, int *argc, char ***argv) { - *argc = args->argc; - *argv = cmd_copy_argv(args->argc, args->argv); + struct args_value *value; + u_int i; + + *argc = 0; + *argv = NULL; + + for (i = 0; i < args->count; i++) { + value = &args->values[i]; + cmd_append_argv(argc, argv, args_value_as_string(value)); + } } /* Add to string. */ @@ -245,18 +275,28 @@ args_print_add(char **buf, size_t *len, const char *fmt, ...) free(s); } -/* Add argument to string. */ +/* Add value to string. */ static void -args_print_add_argument(char **buf, size_t *len, const char *argument) +args_print_add_value(char **buf, size_t *len, struct args_value *value) { - char *escaped; + char *expanded = NULL; if (**buf != '\0') args_print_add(buf, len, " "); - escaped = args_escape(argument); - args_print_add(buf, len, "%s", escaped); - free(escaped); + switch (value->type) { + case ARGS_NONE: + break; + case ARGS_COMMANDS: + expanded = cmd_list_print(value->cmdlist, 0); + args_print_add(buf, len, "{ %s }", expanded); + break; + case ARGS_STRING: + expanded = args_escape(value->string); + args_print_add(buf, len, "%s", expanded); + break; + } + free(expanded); } /* Print a set of arguments. */ @@ -265,8 +305,7 @@ args_print(struct args *args) { size_t len; char *buf; - int i; - u_int j; + u_int i, j; struct args_entry *entry; struct args_value *value; @@ -291,13 +330,13 @@ args_print(struct args *args) args_print_add(&buf, &len, " -%c", entry->flag); else args_print_add(&buf, &len, "-%c", entry->flag); - args_print_add_argument(&buf, &len, value->string); + args_print_add_value(&buf, &len, value); } } /* And finally the argument vector. */ - for (i = 0; i < args->argc; i++) - args_print_add_argument(&buf, &len, args->argv[i]); + for (i = 0; i < args->count; i++) + args_print_add_value(&buf, &len, &args->values[i]); return (buf); } @@ -363,10 +402,9 @@ args_has(struct args *args, u_char flag) /* Set argument value in the arguments tree. */ void -args_set(struct args *args, u_char flag, const char *s) +args_set(struct args *args, u_char flag, struct args_value *value) { struct args_entry *entry; - struct args_value *value; entry = args_find(args, flag); if (entry == NULL) { @@ -377,12 +415,8 @@ args_set(struct args *args, u_char flag, const char *s) RB_INSERT(args_tree, &args->tree, entry); } else entry->count++; - - if (s != NULL) { - value = xcalloc(1, sizeof *value); - value->string = xstrdup(s); + if (value != NULL && value->type != ARGS_NONE) TAILQ_INSERT_TAIL(&entry->values, value, entry); - } } /* Get argument value. Will be NULL if it isn't present. */ @@ -422,16 +456,25 @@ args_next(struct args_entry **entry) u_int args_count(struct args *args) { - return (args->argc); + return (args->count); +} + +/* Get argument value. */ +struct args_value * +args_value(struct args *args, u_int idx) +{ + if (idx >= args->count) + return (NULL); + return (&args->values[idx]); } /* Return argument as string. */ const char * args_string(struct args *args, u_int idx) { - if (idx >= (u_int)args->argc) + if (idx >= args->count) return (NULL); - return (args->argv[idx]); + return (args_value_as_string(&args->values[idx])); } /* Get first value in argument. */ From c0048d6d20a056464a6b1b63e8bdc1793a29b029 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 22 Aug 2021 13:00:28 +0000 Subject: [PATCH 0952/1006] Insert alias in the right place, GitHub issue 2842. --- cmd-parse.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-parse.y b/cmd-parse.y index 86917941..c27d530e 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -772,7 +772,7 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd, TAILQ_REMOVE(&cmd->arguments, first, entry); cmd_parse_free_argument(first); - after = TAILQ_NEXT(TAILQ_FIRST(&last->arguments), entry); + after = TAILQ_FIRST(&last->arguments); TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) { TAILQ_REMOVE(&cmd->arguments, arg, entry); if (after == NULL) From 72d905f32c53ea1304b4b3206383502a23cfc0fd Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 22 Aug 2021 13:48:29 +0000 Subject: [PATCH 0953/1006] Do not double free expanded path in source-file, also remove some unnecessary assignments. --- cmd-display-menu.c | 2 -- cmd-source-file.c | 2 +- file.c | 4 ++-- server-client.c | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index b80f8d46..347a7090 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -121,8 +121,6 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, if (sr != NULL) break; } - if (line == lines) - ranges = &tc->status.entries[0].ranges; if (sr != NULL) { format_add(ft, "popup_window_status_line_x", "%u", diff --git a/cmd-source-file.c b/cmd-source-file.c index 63e1b28c..fbe871ca 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -180,12 +180,12 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) free(pattern); continue; } - free(expanded); free(pattern); for (j = 0; j < g.gl_pathc; j++) cmd_source_file_add(cdata, g.gl_pathv[j]); } + free(expanded); cdata->after = item; cdata->retval = retval; diff --git a/file.c b/file.c index e527f121..7b956120 100644 --- a/file.c +++ b/file.c @@ -558,7 +558,7 @@ file_write_open(struct client_files *files, struct tmuxpeer *peer, log_debug("open write file %d %s", msg->stream, path); find.stream = msg->stream; - if ((cf = RB_FIND(client_files, files, &find)) != NULL) { + if (RB_FIND(client_files, files, &find) != NULL) { error = EBADF; goto reply; } @@ -717,7 +717,7 @@ file_read_open(struct client_files *files, struct tmuxpeer *peer, log_debug("open read file %d %s", msg->stream, path); find.stream = msg->stream; - if ((cf = RB_FIND(client_files, files, &find)) != NULL) { + if (RB_FIND(client_files, files, &find) != NULL) { error = EBADF; goto reply; } diff --git a/server-client.c b/server-client.c index 8821ba57..ac9a7475 100644 --- a/server-client.c +++ b/server-client.c @@ -280,7 +280,7 @@ server_client_open(struct client *c, char **cause) static void server_client_attached_lost(struct client *c) { - struct session *s = c->session; + struct session *s; struct window *w; struct client *loop; struct client *found; From 2e9bafaf14ed255eb3f3ef36202f3f4d2c9eca39 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 22 Aug 2021 15:33:14 +0000 Subject: [PATCH 0954/1006] Fix handling of leading #s when working out width. --- format-draw.c | 199 +++++++++++++++++++++----------------------------- 1 file changed, 83 insertions(+), 116 deletions(-) diff --git a/format-draw.c b/format-draw.c index cf0bdf5f..6164cc44 100644 --- a/format-draw.c +++ b/format-draw.c @@ -632,6 +632,36 @@ format_draw_absolute_centre(struct screen_write_ctx *octx, u_int available, width_after); } +/* Get width and count of any leading #s. */ +static const char * +format_leading_hashes(const char *cp, u_int *n, u_int *width) +{ + for (*n = 0; cp[*n] == '#'; (*n)++) + /* nothing */; + if (*n == 0) { + *width = 0; + return (cp); + } + if (cp[*n] != '[') { + if ((*n % 2) == 0) + *width = (*n / 2); + else + *width = (*n / 2) + 1; + return (cp + *n); + } + *width = (*n / 2); + if ((*n % 2) == 0) { + /* + * An even number of #s means that all #s are escaped, so not a + * style. The caller should not skip this. Return pointing to + * the [. + */ + return (cp + *n); + } + /* This is a style, so return pointing to the #. */ + return (cp + *n - 1); +} + /* Draw multiple characters. */ static void format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch, @@ -1002,37 +1032,22 @@ u_int format_width(const char *expanded) { const char *cp, *end; - u_int n, width = 0; + u_int n, leading_width, width = 0; struct utf8_data ud; enum utf8_state more; cp = expanded; while (*cp != '\0') { if (*cp == '#') { - for (n = 1; cp[n] == '#'; n++) - /* nothing */; - if (cp[n] != '[') { - width += n; - cp += n; - continue; + end = format_leading_hashes(cp, &n, &leading_width); + width += leading_width; + cp = end; + if (*cp == '#') { + end = format_skip(cp + 2, "]"); + if (end == NULL) + return (0); + cp = end + 1; } - width += (n / 2); /* one for each ## */ - - if ((n % 2) == 0) { - /* - * An even number of #s means that all #s are - * escaped, so not a style. - */ - width++; /* one for the [ */ - cp += (n + 1); - continue; - } - cp += (n - 1); /* point to the [ */ - - end = format_skip(cp + 2, "]"); - if (end == NULL) - return (0); - cp = end + 1; } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *cp); @@ -1059,7 +1074,7 @@ format_trim_left(const char *expanded, u_int limit) { char *copy, *out; const char *cp = expanded, *end; - u_int even, n, width = 0; + u_int n, width = 0, leading_width; struct utf8_data ud; enum utf8_state more; @@ -1068,44 +1083,27 @@ format_trim_left(const char *expanded, u_int limit) if (width >= limit) break; if (*cp == '#') { - for (end = cp + 1; *end == '#'; end++) - /* nothing */; - n = end - cp; - if (*end != '[') { - if (n > limit - width) - n = limit - width; - memcpy(out, cp, n); - out += n; - width += n; - cp = end; - continue; - } - even = ((n % 2) == 0); - - n /= 2; - if (n > limit - width) - n = limit - width; - width += n; - n *= 2; - memcpy(out, cp, n); - out += n; - - if (even) { - if (width + 1 <= limit) { - *out++ = '['; - width++; + end = format_leading_hashes(cp, &n, &leading_width); + if (leading_width > limit - width) + leading_width = limit - width; + if (leading_width != 0) { + if (n == 1) + *out++ = '#'; + else { + memset(out, '#', 2 * leading_width); + out += 2 * leading_width; } - cp = end + 1; - continue; + width += leading_width; + } + cp = end; + if (*cp == '#') { + end = format_skip(cp + 2, "]"); + if (end == NULL) + break; + memcpy(out, cp, end + 1 - cp); + out += (end + 1 - cp); + cp = end + 1; } - cp = end - 1; - - end = format_skip(cp + 2, "]"); - if (end == NULL) - break; - memcpy(out, cp, end + 1 - cp); - out += (end + 1 - cp); - cp = end + 1; } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *cp); @@ -1137,7 +1135,8 @@ format_trim_right(const char *expanded, u_int limit) { char *copy, *out; const char *cp = expanded, *end; - u_int width = 0, total_width, skip, old_n, even, n; + u_int width = 0, total_width, skip, n; + u_int leading_width, copy_width; struct utf8_data ud; enum utf8_state more; @@ -1149,64 +1148,32 @@ format_trim_right(const char *expanded, u_int limit) out = copy = xcalloc(1, strlen(expanded) + 1); while (*cp != '\0') { if (*cp == '#') { - for (end = cp + 1; *end == '#'; end++) - /* nothing */; - old_n = n = end - cp; - if (*end != '[') { - if (width <= skip) { - if (skip - width >= n) - n = 0; - else - n -= (skip - width); - } - if (n != 0) { - memcpy(out, cp, n); - out += n; - } - - /* - * The width always increases by the full - * amount even if we can't copy anything yet. - */ - width += old_n; - cp = end; - continue; - } - even = ((n % 2) == 0); - - n /= 2; + end = format_leading_hashes(cp, &n, &leading_width); if (width <= skip) { - if (skip - width >= n) - n = 0; + if (skip - width >= leading_width) + copy_width = 0; else - n -= (skip - width); + copy_width -= (skip - width); + } else + copy_width = leading_width; + if (copy_width != 0) { + if (n == 1) + *out++ = '#'; + else { + memset(out, '#', 2 * copy_width); + out += 2 * copy_width; + } } - if (n != 0) { - /* - * Copy the full amount because it hasn't been - * escaped yet. - */ - memcpy(out, cp, old_n); - out += old_n; + width += leading_width; + cp = end; + if (*cp == '#') { + end = format_skip(cp + 2, "]"); + if (end == NULL) + break; + memcpy(out, cp, end + 1 - cp); + out += (end + 1 - cp); + cp = end + 1; } - cp += old_n; - width += (old_n / 2) - even; - - if (even) { - if (width > skip) - *out++ = '['; - width++; - continue; - } - cp = end - 1; - - end = format_skip(cp + 2, "]"); - if (end == NULL) { - break; - } - memcpy(out, cp, end + 1 - cp); - out += (end + 1 - cp); - cp = end + 1; } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *cp); From bc71e233d95378f927c8d68a686c0fb362a67ddb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 22 Aug 2021 16:33:40 +0100 Subject: [PATCH 0955/1006] Fix style regress test. --- regress/style-trim.sh | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/regress/style-trim.sh b/regress/style-trim.sh index 56f93656..720d99fd 100644 --- a/regress/style-trim.sh +++ b/regress/style-trim.sh @@ -19,11 +19,9 @@ check() { 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" + if [ "$v" != "$2" -o "$r" != "$3" ]; then + printf "$1 = [$v = $2] [$r = $3]" + printf " \033[31mbad\033[0m\n" exit 1 fi } @@ -46,16 +44,16 @@ check '#{V} #{w:V}' '#0123456 8' '#0123456 8' check '#{=3:V}' '#01' '#01' check '#{=-3:V}' '456' '456' -# drawn as ##0123456 +# drawn as #0123456 $TMUX setenv -g V '##0123456' -check '#{V} #{w:V}' '##0123456 9' '##0123456 9' -check '#{=3:V}' '##0' '##0' +check '#{V} #{w:V}' '##0123456 8' '#0123456 8' +check '#{=3:V}' '##01' '#01' check '#{=-3:V}' '456' '456' -# drawn as ###0123456 +# drawn as ##0123456 $TMUX setenv -g V '###0123456' -check '#{V} #{w:V}' '###0123456 10' '###0123456 10' -check '#{=3:V}' '###' '###' +check '#{V} #{w:V}' '###0123456 9' '##0123456 9' +check '#{=3:V}' '####0' '##0' check '#{=-3:V}' '456' '456' # drawn as 0123456 From 3ed37a207988bc6e96dc673ae4564a4efd682ea6 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 08:17:41 +0000 Subject: [PATCH 0956/1006] Limit width and height to tty correctly, GitHub issue 2843. --- cmd-display-menu.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 347a7090..378c62e1 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -218,7 +218,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, else if (n < 0) n = 0; *px = n; - log_debug("%s: -x: %s = %s = %u", __func__, xp, p, *px); + log_debug("%s: -x: %s = %s = %u (-w %u)", __func__, xp, p, *px, w); free(p); /* Expand vertical position */ @@ -244,7 +244,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, else if (n < 0) n = 0; *py = n; - log_debug("%s: -y: %s = %s = %u", __func__, yp, p, *py); + log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h); free(p); return (1); @@ -359,10 +359,10 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) } } - if (w > tty->sx - 1) - w = tty->sx - 1; - if (h > tty->sy - 1) - h = tty->sy - 1; + if (w > tty->sx) + w = tty->sx; + if (h > tty->sy) + h = tty->sy; if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h)) return (CMD_RETURN_NORMAL); From 4a753dbefc2e67c218cf41141eaa6afab00f774a Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 11:04:21 +0000 Subject: [PATCH 0957/1006] Fix a few memory leaks. --- cmd-parse.y | 9 ++++----- cmd-source-file.c | 5 +++++ key-bindings.c | 5 ++++- spawn.c | 1 + tmux.c | 1 + 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index c27d530e..a08c5819 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -426,7 +426,7 @@ command : assignment arg = xcalloc(1, sizeof *arg); arg->type = CMD_PARSE_STRING; - arg->string = xstrdup($2); + arg->string = $2; TAILQ_INSERT_HEAD(&$$->arguments, arg, entry); } | optional_assignment TOKEN arguments @@ -443,7 +443,7 @@ command : assignment arg = xcalloc(1, sizeof *arg); arg->type = CMD_PARSE_STRING; - arg->string = xstrdup($2); + arg->string = $2; TAILQ_INSERT_HEAD(&$$->arguments, arg, entry); } @@ -543,13 +543,13 @@ argument : TOKEN { $$ = xcalloc(1, sizeof *$$); $$->type = CMD_PARSE_STRING; - $$->string = xstrdup($1); + $$->string = $1; } | EQUALS { $$ = xcalloc(1, sizeof *$$); $$->type = CMD_PARSE_STRING; - $$->string = xstrdup($1); + $$->string = $1; } | '{' argument_statements { @@ -817,7 +817,6 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, goto out; values[count].type = ARGS_COMMANDS; values[count].cmdlist = pr->cmdlist; - values[count].cmdlist->references++; break; } count++; diff --git a/cmd-source-file.c b/cmd-source-file.c index fbe871ca..0bc02e05 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -66,6 +66,7 @@ static void cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata) { struct cmdq_item *new_item; + u_int i; if (cfg_finished) { if (cdata->retval == CMD_RETURN_ERROR && @@ -76,6 +77,8 @@ cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata) cmdq_insert_after(cdata->after, new_item); } + for (i = 0; i < cdata->nfiles; i++) + free(cdata->files[i]); free(cdata->files); free(cdata); } @@ -177,6 +180,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "%s: %s", path, error); retval = CMD_RETURN_ERROR; } + globfree(&g); free(pattern); continue; } @@ -184,6 +188,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) for (j = 0; j < g.gl_pathc; j++) cmd_source_file_add(cdata, g.gl_pathv[j]); + globfree(&g); } free(expanded); diff --git a/key-bindings.c b/key-bindings.c index c0a959e2..e3b21d61 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -187,6 +187,7 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, { struct key_table *table; struct key_binding *bd; + char *s; table = key_bindings_get_table(name, 1); @@ -216,8 +217,10 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, bd->flags |= KEY_BINDING_REPEAT; bd->cmdlist = cmdlist; + s = cmd_list_print(bd->cmdlist, 0); log_debug("%s: %#llx %s = %s", __func__, bd->key, - key_string_lookup_key(bd->key, 1), cmd_list_print(bd->cmdlist, 0)); + key_string_lookup_key(bd->key, 1), s); + free(s); } void diff --git a/spawn.c b/spawn.c index 98b70312..294eb50f 100644 --- a/spawn.c +++ b/spawn.c @@ -179,6 +179,7 @@ spawn_window(struct spawn_context *sc, char **cause) /* Set the name of the new window. */ if (~sc->flags & SPAWN_RESPAWN) { + free(w->name); if (sc->name != NULL) { w->name = format_single(item, sc->name, c, s, NULL, NULL); diff --git a/tmux.c b/tmux.c index 55ab9751..ceaa99da 100644 --- a/tmux.c +++ b/tmux.c @@ -211,6 +211,7 @@ make_label(const char *label, char **cause) free(paths); xasprintf(&base, "%s/tmux-%ld", path, (long)uid); + free(path); if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) { xasprintf(cause, "couldn't create directory %s (%s)", base, strerror(errno)); From 1f0c0914c75980069c6f78872bdee5ceb0ef64e2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 11:08:26 +0000 Subject: [PATCH 0958/1006] Revert one of previous, for some reason it is being freed. --- cmd-parse.y | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd-parse.y b/cmd-parse.y index a08c5819..f79c7fd3 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -817,6 +817,7 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, goto out; values[count].type = ARGS_COMMANDS; values[count].cmdlist = pr->cmdlist; + values[count].cmdlist->references++; break; } count++; From 03b83a5a34a4257be9029e1f5195dcddcd531caa Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 11:48:21 +0000 Subject: [PATCH 0959/1006] Key bindings steal a reference to the command instead of adding their own, it was correct not to add a reference when parsing, but the bind-key then needs to add one. --- cmd-bind-key.c | 1 + cmd-parse.y | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 4a6c8541..bb905bce 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -75,6 +75,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) value = args_value(args, 1); if (count == 2 && value->type == ARGS_COMMANDS) { key_bindings_add(tablename, key, note, repeat, value->cmdlist); + value->cmdlist->references++; return (CMD_RETURN_NORMAL); } diff --git a/cmd-parse.y b/cmd-parse.y index f79c7fd3..a08c5819 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -817,7 +817,6 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, goto out; values[count].type = ARGS_COMMANDS; values[count].cmdlist = pr->cmdlist; - values[count].cmdlist->references++; break; } count++; From 210e71edf36198e3f22525e7f548a71a5681d25c Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 12:33:55 +0000 Subject: [PATCH 0960/1006] Move command argument parsing common functions and don't bother to parse again if given a command rather than a string. --- arguments.c | 137 +++++++++++++++++++++++++++++++++ cmd-command-prompt.c | 178 +++++++++++++++++++++---------------------- cmd-confirm-before.c | 49 +++++------- cmd-display-panes.c | 38 ++++----- cmd-if-shell.c | 112 +++++++++------------------ cmd-run-shell.c | 53 +++++-------- tmux.h | 9 +++ 7 files changed, 330 insertions(+), 246 deletions(-) diff --git a/arguments.c b/arguments.c index 49bc8b2a..1f786529 100644 --- a/arguments.c +++ b/arguments.c @@ -29,8 +29,10 @@ * Manipulate command arguments. */ +/* List of argument values. */ TAILQ_HEAD(args_values, args_value); +/* Single arguments flag. */ struct args_entry { u_char flag; struct args_values values; @@ -38,12 +40,20 @@ struct args_entry { RB_ENTRY(args_entry) entry; }; +/* Parsed argument flags and values. */ struct args { struct args_tree tree; u_int count; struct args_value *values; }; +/* Prepared command state. */ +struct args_command_state { + struct cmd_list *cmdlist; + char *cmd; + struct cmd_parse_input pi; +}; + static struct args_entry *args_find(struct args *, u_char); static int args_cmp(struct args_entry *, struct args_entry *); @@ -477,6 +487,133 @@ args_string(struct args *args, u_int idx) return (args_value_as_string(&args->values[idx])); } +/* Make a command now. */ +struct cmd_list * +args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx) +{ + struct args_command_state *state; + char *error; + struct cmd_list *cmdlist; + + state = args_make_commands_prepare(self, item, idx, NULL, 0, 0); + cmdlist = args_make_commands(state, 0, NULL, &error); + args_make_commands_free(state); + if (cmdlist == NULL) { + cmdq_error(item, "%s", error); + free(error); + } + return (cmdlist); +} + +/* Save bits to make a command later. */ +struct args_command_state * +args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx, + const char *default_command, int wait, int expand) +{ + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item); + struct args_value *value; + struct args_command_state *state; + const char *cmd; + + state = xcalloc(1, sizeof *state); + + if (idx < args->count) { + value = &args->values[idx]; + if (value->type == ARGS_COMMANDS) { + state->cmdlist = value->cmdlist; + state->cmdlist->references++; + return (state); + } + cmd = value->string; + } else { + if (default_command == NULL) + fatalx("argument out of range"); + cmd = default_command; + } + + + if (expand) + state->cmd = format_single_from_target(item, cmd); + else + state->cmd = xstrdup(cmd); + log_debug("%s: %s", __func__, state->cmd); + + if (wait) + state->pi.item = item; + cmd_get_source(self, &state->pi.file, &state->pi.line); + state->pi.c = tc; + if (state->pi.c != NULL) + state->pi.c->references++; + cmd_find_copy_state(&state->pi.fs, target); + + return (state); +} + +/* Return argument as command. */ +struct cmd_list * +args_make_commands(struct args_command_state *state, int argc, char **argv, + char **error) +{ + struct cmd_parse_result *pr; + char *cmd, *new_cmd; + int i; + + if (state->cmdlist != NULL) + return (state->cmdlist); + + cmd = xstrdup(state->cmd); + for (i = 0; i < argc; i++) { + new_cmd = cmd_template_replace(cmd, argv[i], i + 1); + log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd); + free(cmd); + cmd = new_cmd; + } + log_debug("%s: %s", __func__, cmd); + + pr = cmd_parse_from_string(cmd, &state->pi); + free(cmd); + switch (pr->status) { + case CMD_PARSE_ERROR: + *error = pr->error; + return (NULL); + case CMD_PARSE_SUCCESS: + return (pr->cmdlist); + } +} + +/* Free commands state. */ +void +args_make_commands_free(struct args_command_state *state) +{ + if (state->cmdlist != NULL) + cmd_list_free(state->cmdlist); + if (state->pi.c != NULL) + server_client_unref(state->pi.c); + free(state->cmd); + free(state); +} + +/* Get prepared command. */ +char * +args_make_commands_get_command(struct args_command_state *state) +{ + struct cmd *first; + int n; + char *s; + + if (state->cmdlist != NULL) { + first = cmd_list_first(state->cmdlist); + if (first == NULL) + return (xstrdup("")); + return (xstrdup(cmd_get_entry(first)->name)); + } + n = strcspn(state->cmd, " ,"); + xasprintf(&s, "%.*s", n, state->cmd); + return (s); +} + /* Get first value in argument. */ struct args_value * args_first_value(struct args *args, u_char flag) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index f80760ce..a877897e 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -48,21 +48,24 @@ const struct cmd_entry cmd_command_prompt_entry = { .exec = cmd_command_prompt_exec }; +struct cmd_command_prompt_prompt { + char *input; + char *prompt; +}; + struct cmd_command_prompt_cdata { - struct cmdq_item *item; - struct cmd_parse_input pi; + struct cmdq_item *item; + struct args_command_state *state; - int flags; - enum prompt_type prompt_type; + int flags; + enum prompt_type prompt_type; - char *inputs; - char *next_input; + struct cmd_command_prompt_prompt *prompts; + u_int count; + u_int current; - char *prompts; - char *next_prompt; - - char *template; - int idx; + int argc; + char **argv; }; static enum cmd_retval @@ -71,12 +74,12 @@ cmd_command_prompt_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_find_state *target = cmdq_get_target(item); - const char *inputs, *prompts, *type, *s; + const char *type, *s, *input; struct cmd_command_prompt_cdata *cdata; - char *prompt, *comma, *input = NULL; + char *tmp, *prompts, *prompt, *next_prompt; + char *inputs, *next_input; u_int count = args_count(args); - size_t n; - int wait = !args_has(args, 'b'); + int wait = !args_has(args, 'b'), space = 1; if (tc->prompt_string != NULL) return (CMD_RETURN_NORMAL); @@ -84,50 +87,49 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) wait = 0; cdata = xcalloc(1, sizeof *cdata); - cdata->idx = 1; - - cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); - if (wait) - cdata->pi.item = item; - cdata->pi.c = tc; - cmd_find_copy_state(&cdata->pi.fs, target); - if (wait) cdata->item = item; + cdata->state = args_make_commands_prepare(self, item, 0, "%1", wait, + args_has(args, 'F')); - if (count != 0) { - s = args_string(args, 0); - if (args_has(args, 'F')) - cdata->template = format_single_from_target(item, s); - else - cdata->template = xstrdup(s); - } else - cdata->template = xstrdup("%1"); - - if ((prompts = args_get(args, 'p')) != NULL) - cdata->prompts = xstrdup(prompts); - else if (count != 0) { - n = strcspn(cdata->template, " ,"); - xasprintf(&cdata->prompts, "(%.*s) ", (int)n, cdata->template); + if ((s = args_get(args, 'p')) == NULL) { + if (count != 0) { + tmp = args_make_commands_get_command(cdata->state); + xasprintf(&prompts, "(%s)", tmp); + free(tmp); + } else { + prompts = xstrdup(":"); + space = 0; + } + next_prompt = prompts; } else - cdata->prompts = xstrdup(":"); - - /* Get first prompt. */ - cdata->next_prompt = cdata->prompts; - comma = strsep(&cdata->next_prompt, ","); - if (prompts == NULL) - prompt = xstrdup(comma); + next_prompt = prompts = xstrdup (s); + if ((s = args_get(args, 'I')) != NULL) + next_input = inputs = xstrdup(s); else - xasprintf(&prompt, "%s ", comma); + next_input = NULL; + while ((prompt = strsep(&next_prompt, ",")) != NULL) { + cdata->prompts = xreallocarray(cdata->prompts, cdata->count + 1, + sizeof *cdata->prompts); + if (!space) + tmp = xstrdup(prompt); + else + xasprintf(&tmp, "%s ", prompt); + cdata->prompts[cdata->count].prompt = tmp; - /* Get initial prompt input. */ - if ((inputs = args_get(args, 'I')) != NULL) { - cdata->inputs = xstrdup(inputs); - cdata->next_input = cdata->inputs; - input = strsep(&cdata->next_input, ","); + if (next_input != NULL) { + input = strsep(&next_input, ","); + if (input == NULL) + input = ""; + } else + input = ""; + cdata->prompts[cdata->count].input = xstrdup(input); + + cdata->count++; } + free(inputs); + free(prompts); - /* Get prompt type. */ if ((type = args_get(args, 'T')) != NULL) { cdata->prompt_type = status_prompt_type(type); if (cdata->prompt_type == PROMPT_TYPE_INVALID) { @@ -145,10 +147,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_INCREMENTAL; else if (args_has(args, 'k')) cdata->flags |= PROMPT_KEY; - status_prompt_set(tc, target, prompt, input, - cmd_command_prompt_callback, cmd_command_prompt_free, cdata, - cdata->flags, cdata->prompt_type); - free(prompt); + status_prompt_set(tc, target, cdata->prompts[0].prompt, + cdata->prompts[0].input, cmd_command_prompt_callback, + cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type); if (!wait) return (CMD_RETURN_NORMAL); @@ -159,51 +160,39 @@ static int cmd_command_prompt_callback(struct client *c, void *data, const char *s, int done) { - struct cmd_command_prompt_cdata *cdata = data; - char *new_template, *prompt, *comma, *error; - char *input = NULL; - struct cmdq_item *item = cdata->item; - enum cmd_parse_status status; + struct cmd_command_prompt_cdata *cdata = data; + char *error; + struct cmdq_item *item = cdata->item, *new_item; + struct cmd_list *cmdlist; + struct cmd_command_prompt_prompt *prompt; if (s == NULL) goto out; - if (done && (cdata->flags & PROMPT_INCREMENTAL)) - goto out; - - new_template = cmd_template_replace(cdata->template, s, cdata->idx); if (done) { - free(cdata->template); - cdata->template = new_template; + if (cdata->flags & PROMPT_INCREMENTAL) + goto out; + + cmd_append_argv(&cdata->argc, &cdata->argv, s); + if (++cdata->current != cdata->count) { + prompt = &cdata->prompts[cdata->current]; + status_prompt_update(c, prompt->prompt, prompt->input); + return (1); + } } - /* - * Check if there are more prompts; if so, get its respective input - * and update the prompt data. - */ - if (done && (comma = strsep(&cdata->next_prompt, ",")) != NULL) { - xasprintf(&prompt, "%s ", comma); - input = strsep(&cdata->next_input, ","); - status_prompt_update(c, prompt, input); - - free(prompt); - cdata->idx++; - return (1); - } - - if (item != NULL) { - status = cmd_parse_and_insert(new_template, &cdata->pi, item, - cmdq_get_state(item), &error); - } else { - status = cmd_parse_and_append(new_template, &cdata->pi, c, NULL, - &error); - } - if (status == CMD_PARSE_ERROR) { + cmdlist = args_make_commands(cdata->state, cdata->argc, cdata->argv, + &error); + if (cmdlist == NULL) { cmdq_append(c, cmdq_get_error(error)); free(error); + } else if (item == NULL) { + new_item = cmdq_get_command(cmdlist, NULL); + cmdq_append(c, new_item); + } else { + new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } - if (!done) - free(new_template); if (c->prompt_inputcb != cmd_command_prompt_callback) return (1); @@ -217,9 +206,14 @@ static void cmd_command_prompt_free(void *data) { struct cmd_command_prompt_cdata *cdata = data; + u_int i; - free(cdata->inputs); + for (i = 0; i < cdata->count; i++) { + free(cdata->prompts[i].prompt); + free(cdata->prompts[i].input); + } free(cdata->prompts); - free(cdata->template); + cmd_free_argv(cdata->argc, cdata->argv); + args_make_commands_free(cdata->state); free(cdata); } diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 580c379a..4fe43302 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -47,9 +47,8 @@ const struct cmd_entry cmd_confirm_before_entry = { }; struct cmd_confirm_before_data { - char *cmd; struct cmdq_item *item; - struct cmd_parse_input pi; + struct cmd_list *cmdlist; }; static enum cmd_retval @@ -59,31 +58,25 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) struct cmd_confirm_before_data *cdata; struct client *tc = cmdq_get_target_client(item); struct cmd_find_state *target = cmdq_get_target(item); - char *cmd, *copy, *new_prompt, *tmp; - const char *prompt; + char *new_prompt; + const char *prompt, *cmd; int wait = !args_has(args, 'b'); cdata = xcalloc(1, sizeof *cdata); - cdata->cmd = xstrdup(args_string(args, 0)); + cdata->cmdlist = args_make_commands_now(self, item, 0); + if (cdata->cmdlist == NULL) + return (CMD_RETURN_ERROR); + + if (wait) + cdata->item = item; if ((prompt = args_get(args, 'p')) != NULL) xasprintf(&new_prompt, "%s ", prompt); else { - tmp = copy = xstrdup(cdata->cmd); - cmd = strsep(&tmp, " \t"); + cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name; xasprintf(&new_prompt, "Confirm '%s'? (y/n) ", cmd); - free(copy); } - cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); - if (wait) - cdata->pi.item = item; - cdata->pi.c = tc; - cmd_find_copy_state(&cdata->pi.fs, target); - - if (wait) - cdata->item = item; - status_prompt_set(tc, target, new_prompt, NULL, cmd_confirm_before_callback, cmd_confirm_before_free, cdata, PROMPT_SINGLE, PROMPT_TYPE_COMMAND); @@ -99,10 +92,7 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, __unused int done) { struct cmd_confirm_before_data *cdata = data; - const char *cmd = cdata->cmd; - char *error; - struct cmdq_item *item = cdata->item; - enum cmd_parse_status status; + struct cmdq_item *item = cdata->item, *new_item; int retcode = 1; if (c->flags & CLIENT_DEAD) @@ -114,14 +104,13 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, goto out; retcode = 0; - if (item != NULL) { - status = cmd_parse_and_insert(cmd, &cdata->pi, item, - cmdq_get_state(item), &error); - } else - status = cmd_parse_and_append(cmd, &cdata->pi, c, NULL, &error); - if (status == CMD_PARSE_ERROR) { - cmdq_append(c, cmdq_get_error(error)); - free(error); + if (item == NULL) { + new_item = cmdq_get_command(cdata->cmdlist, NULL); + cmdq_append(c, new_item); + } else { + new_item = cmdq_get_command(cdata->cmdlist, + cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } out: @@ -139,6 +128,6 @@ cmd_confirm_before_free(void *data) { struct cmd_confirm_before_data *cdata = data; - free(cdata->cmd); + cmd_list_free(cdata->cmdlist); free(cdata); } diff --git a/cmd-display-panes.c b/cmd-display-panes.c index bc171638..169ab3d5 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -42,8 +42,8 @@ const struct cmd_entry cmd_display_panes_entry = { }; struct cmd_display_panes_data { - struct cmdq_item *item; - char *command; + struct cmdq_item *item; + struct args_command_state *state; }; static void @@ -207,7 +207,7 @@ cmd_display_panes_free(__unused struct client *c, void *data) if (cdata->item != NULL) cmdq_continue(cdata->item); - free(cdata->command); + args_make_commands_free(cdata->state); free(cdata); } @@ -215,10 +215,11 @@ static int cmd_display_panes_key(struct client *c, void *data, struct key_event *event) { struct cmd_display_panes_data *cdata = data; - char *cmd, *expanded, *error; + char *expanded, *error; + struct cmdq_item *item = cdata->item, *new_item; + struct cmd_list *cmdlist; struct window *w = c->session->curw->window; struct window_pane *wp; - enum cmd_parse_status status; u_int index; key_code key; @@ -239,15 +240,19 @@ cmd_display_panes_key(struct client *c, void *data, struct key_event *event) window_unzoom(w); xasprintf(&expanded, "%%%u", wp->id); - cmd = cmd_template_replace(cdata->command, expanded, 1); - status = cmd_parse_and_append(cmd, NULL, c, NULL, &error); - if (status == CMD_PARSE_ERROR) { + cmdlist = args_make_commands(cdata->state, 1, &expanded, &error); + if (cmdlist == NULL) { cmdq_append(c, cmdq_get_error(error)); free(error); + } else if (item == NULL) { + new_item = cmdq_get_command(cmdlist, NULL); + cmdq_append(c, new_item); + } else { + new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } - free(cmd); free(expanded); return (1); } @@ -261,6 +266,7 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) u_int delay; char *cause; struct cmd_display_panes_data *cdata; + int wait = !args_has(args, 'b'); if (tc->overlay_draw != NULL) return (CMD_RETURN_NORMAL); @@ -275,15 +281,11 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) } else delay = options_get_number(s->options, "display-panes-time"); - cdata = xmalloc(sizeof *cdata); - if (args_count(args)) - cdata->command = xstrdup(args_string(args, 0)); - else - cdata->command = xstrdup("select-pane -t '%%'"); - if (args_has(args, 'b')) - cdata->item = NULL; - else + cdata = xcalloc(1, sizeof *cdata); + if (wait) cdata->item = item; + cdata->state = args_make_commands_prepare(self, item, 0, + "select-pane -t \"%%%\"", wait, 0); if (args_has(args, 'N')) { server_client_set_overlay(tc, delay, NULL, NULL, @@ -295,7 +297,7 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) cmd_display_panes_free, NULL, cdata); } - if (args_has(args, 'b')) + if (!wait) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } diff --git a/cmd-if-shell.c b/cmd-if-shell.c index a04bdddd..7804ef27 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -31,8 +31,8 @@ static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmdq_item *); -static void cmd_if_shell_callback(struct job *); -static void cmd_if_shell_free(void *); +static void cmd_if_shell_callback(struct job *); +static void cmd_if_shell_free(void *); const struct cmd_entry cmd_if_shell_entry = { .name = "if-shell", @@ -49,10 +49,8 @@ const struct cmd_entry cmd_if_shell_entry = { }; struct cmd_if_shell_data { - struct cmd_parse_input input; - - char *cmd_if; - char *cmd_else; + struct cmd_list *cmd_if; + struct cmd_list *cmd_else; struct client *client; struct cmdq_item *item; @@ -63,46 +61,42 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct cmdq_state *state = cmdq_get_state(item); struct cmd_if_shell_data *cdata; - char *shellcmd, *error; - const char *cmd = NULL, *file; + struct cmdq_item *new_item; + char *shellcmd; struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; - struct cmd_parse_input pi; - enum cmd_parse_status status; + struct cmd_list *cmdlist = NULL; u_int count = args_count(args); shellcmd = format_single_from_target(item, args_string(args, 0)); if (args_has(args, 'F')) { if (*shellcmd != '0' && *shellcmd != '\0') - cmd = args_string(args, 1); + cmdlist = args_make_commands_now(self, item, 1); else if (count == 3) - cmd = args_string(args, 2); - free(shellcmd); - if (cmd == NULL) + cmdlist = args_make_commands_now(self, item, 2); + else { + free(shellcmd); return (CMD_RETURN_NORMAL); - - memset(&pi, 0, sizeof pi); - cmd_get_source(self, &pi.file, &pi.line); - pi.item = item; - pi.c = tc; - cmd_find_copy_state(&pi.fs, target); - - status = cmd_parse_and_insert(cmd, &pi, item, state, &error); - if (status == CMD_PARSE_ERROR) { - cmdq_error(item, "%s", error); - free(error); - return (CMD_RETURN_ERROR); } + free(shellcmd); + if (cmdlist == NULL) + return (CMD_RETURN_ERROR); + new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); + cmdq_insert_after(item, new_item); return (CMD_RETURN_NORMAL); } cdata = xcalloc(1, sizeof *cdata); - cdata->cmd_if = xstrdup(args_string(args, 1)); - if (count == 3) - cdata->cmd_else = xstrdup(args_string(args, 2)); + cdata->cmd_if = args_make_commands_now(self, item, 1); + if (cdata->cmd_if == NULL) + return (CMD_RETURN_ERROR); + if (count == 3) { + cdata->cmd_else = args_make_commands_now(self, item, 2); + if (cdata->cmd_else == NULL) + return (CMD_RETURN_ERROR); + } if (!args_has(args, 'b')) cdata->client = cmdq_get_client(item); @@ -114,14 +108,6 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'b')) cdata->item = item; - cmd_get_source(self, &file, &cdata->input.line); - if (file != NULL) - cdata->input.file = xstrdup(file); - cdata->input.c = tc; - if (cdata->input.c != NULL) - cdata->input.c->references++; - cmd_find_copy_state(&cdata->input.fs, target); - if (job_run(shellcmd, 0, NULL, s, server_client_get_cwd(cmdq_get_client(item), s), NULL, cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1, @@ -143,43 +129,24 @@ cmd_if_shell_callback(struct job *job) { struct cmd_if_shell_data *cdata = job_get_data(job); struct client *c = cdata->client; - struct cmdq_item *new_item = NULL; - struct cmdq_state *new_state = NULL; - char *cmd; + struct cmdq_item *item = cdata->item, *new_item; + struct cmd_list *cmdlist; int status; - struct cmd_parse_result *pr; status = job_get_status(job); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - cmd = cdata->cmd_else; + cmdlist = cdata->cmd_else; else - cmd = cdata->cmd_if; - if (cmd == NULL) + cmdlist = cdata->cmd_if; + if (cmdlist == NULL) goto out; - pr = cmd_parse_from_string(cmd, &cdata->input); - switch (pr->status) { - case CMD_PARSE_ERROR: - if (cdata->item != NULL) - cmdq_error(cdata->item, "%s", pr->error); - free(pr->error); - break; - case CMD_PARSE_SUCCESS: - if (cdata->item == NULL) - new_state = cmdq_new_state(NULL, NULL, 0); - else - new_state = cmdq_get_state(cdata->item); - new_item = cmdq_get_command(pr->cmdlist, new_state); - if (cdata->item == NULL) - cmdq_free_state(new_state); - cmd_list_free(pr->cmdlist); - break; - } - if (new_item != NULL) { - if (cdata->item == NULL) - cmdq_append(c, new_item); - else - cmdq_insert_after(cdata->item, new_item); + if (item == NULL) { + new_item = cmdq_get_command(cmdlist, NULL); + cmdq_append(c, new_item); + } else { + new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } out: @@ -195,12 +162,9 @@ cmd_if_shell_free(void *data) if (cdata->client != NULL) server_client_unref(cdata->client); - free(cdata->cmd_else); - free(cdata->cmd_if); - - if (cdata->input.c != NULL) - server_client_unref(cdata->input.c); - free((void *)cdata->input.file); + if (cdata->cmd_else != NULL) + cmd_list_free(cdata->cmd_else); + cmd_list_free(cdata->cmd_if); free(cdata); } diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 207e4710..f23a8ec3 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -53,14 +53,13 @@ const struct cmd_entry cmd_run_shell_entry = { struct cmd_run_shell_data { struct client *client; char *cmd; - int shell; + struct cmd_list *cmdlist; char *cwd; struct cmdq_item *item; struct session *s; int wp_id; struct event timer; int flags; - struct cmd_parse_input pi; }; static void @@ -100,11 +99,10 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct window_pane *wp = target->wp; - const char *delay; + const char *delay, *cmd; double d; struct timeval tv; char *end; - const char *cmd = args_string(args, 0); int wait = !args_has(args, 'b'); if ((delay = args_get(args, 'd')) != NULL) { @@ -117,16 +115,14 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); cdata = xcalloc(1, sizeof *cdata); - if (cmd != NULL) - cdata->cmd = format_single_from_target(item, cmd); - - cdata->shell = !args_has(args, 'C'); - if (!cdata->shell) { - cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); - if (wait) - cdata->pi.item = item; - cdata->pi.c = tc; - cmd_find_copy_state(&cdata->pi.fs, target); + if (!args_has(args, 'C')) { + cmd = args_string(args, 0); + if (cmd != NULL) + cdata->cmd = format_single_from_target(item, cmd); + } else { + cdata->cmdlist = args_make_commands_now(self, item, 0); + if (cdata->cmdlist == NULL) + return (CMD_RETURN_ERROR); } if (args_has(args, 't') && wp != NULL) @@ -170,11 +166,9 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) struct cmd_run_shell_data *cdata = arg; struct client *c = cdata->client; const char *cmd = cdata->cmd; - char *error; - struct cmdq_item *item = cdata->item; - enum cmd_parse_status status; + struct cmdq_item *item = cdata->item, *new_item; - if (cmd != NULL && cdata->shell) { + if (cdata->cmdlist == NULL && cmd != NULL) { if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL, cmd_run_shell_callback, cmd_run_shell_free, cdata, cdata->flags, -1, -1) == NULL) @@ -182,21 +176,14 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) return; } - if (cmd != NULL) { - if (item != NULL) { - status = cmd_parse_and_insert(cmd, &cdata->pi, item, - cmdq_get_state(item), &error); + if (cdata->cmdlist != NULL) { + if (item == NULL) { + new_item = cmdq_get_command(cdata->cmdlist, NULL); + cmdq_append(c, new_item); } else { - status = cmd_parse_and_append(cmd, &cdata->pi, c, NULL, - &error); - } - if (status == CMD_PARSE_ERROR) { - if (cdata->item == NULL) { - *error = toupper((u_char)*error); - status_message_set(c, -1, 1, 0, "%s", error); - } else - cmdq_error(cdata->item, "%s", error); - free(error); + new_item = cmdq_get_command(cdata->cmdlist, + cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } } @@ -265,6 +252,8 @@ cmd_run_shell_free(void *data) session_remove_ref(cdata->s, __func__); if (cdata->client != NULL) server_client_unref(cdata->client); + if (cdata->cmdlist != NULL) + cmd_list_free(cdata->cmdlist); free(cdata->cwd); free(cdata->cmd); free(cdata); diff --git a/tmux.h b/tmux.h index fa0268a2..6bdc7e6a 100644 --- a/tmux.h +++ b/tmux.h @@ -37,6 +37,7 @@ extern char **environ; struct args; +struct args_command_state; struct client; struct cmd; struct cmd_find_state; @@ -2213,6 +2214,14 @@ u_char args_next(struct args_entry **); u_int args_count(struct args *); struct args_value *args_value(struct args *, u_int); const char *args_string(struct args *, u_int); +struct cmd_list *args_make_commands_now(struct cmd *, struct cmdq_item *, + u_int); +struct args_command_state *args_make_commands_prepare(struct cmd *, + struct cmdq_item *, u_int, const char *, int, int); +struct cmd_list *args_make_commands(struct args_command_state *, int, char **, + char **); +void args_make_commands_free(struct args_command_state *); +char *args_make_commands_get_command(struct args_command_state *); struct args_value *args_first_value(struct args *, u_char); struct args_value *args_next_value(struct args_value *); long long args_strtonum(struct args *, u_char, long long, long long, From 841ce74b4303133e0aaf379ba25cdfa7de7971b5 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 17:05:43 +0000 Subject: [PATCH 0961/1006] args_make_commands_now needs to take an extra reference to the returned command list since it will be freed already. --- arguments.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arguments.c b/arguments.c index 1f786529..bddb6365 100644 --- a/arguments.c +++ b/arguments.c @@ -127,7 +127,7 @@ args_parse(const struct args_parse *parse, struct args_value *values, u_int count) { struct args *args; - u_int i; + u_int i; struct args_value *value, *new; u_char flag, argument; const char *found, *string, *s; @@ -497,11 +497,13 @@ args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx) state = args_make_commands_prepare(self, item, idx, NULL, 0, 0); cmdlist = args_make_commands(state, 0, NULL, &error); - args_make_commands_free(state); if (cmdlist == NULL) { cmdq_error(item, "%s", error); free(error); } + else + cmdlist->references++; + args_make_commands_free(state); return (cmdlist); } From 78da5a3756a9f51f8e49d2975f74a1ea800b58b6 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 25 Aug 2021 06:36:05 +0000 Subject: [PATCH 0962/1006] Start inputs as NULL so not freeing random stack garbage, GitHub issue 2852. --- cmd-command-prompt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index a877897e..bca1a7fc 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -77,7 +77,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) const char *type, *s, *input; struct cmd_command_prompt_cdata *cdata; char *tmp, *prompts, *prompt, *next_prompt; - char *inputs, *next_input; + char *inputs = NULL, *next_input; u_int count = args_count(args); int wait = !args_has(args, 'b'), space = 1; From a252fadf8a6df72e4a417de2668bffa971455193 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 25 Aug 2021 07:09:30 +0000 Subject: [PATCH 0963/1006] Fix up some printflike attributes. --- control.c | 2 +- log.c | 5 +---- tmux.h | 12 +++++++----- xmalloc.h | 2 ++ 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/control.c b/control.c index efcbc0ff..359d0e78 100644 --- a/control.c +++ b/control.c @@ -386,7 +386,7 @@ control_pause_pane(struct client *c, struct window_pane *wp) } /* Write a line. */ -static void +static void printflike(2, 0) control_vwrite(struct client *c, const char *fmt, va_list ap) { struct control_state *cs = c->control_state; diff --git a/log.c b/log.c index 3387ab0c..ec54711a 100644 --- a/log.c +++ b/log.c @@ -30,9 +30,6 @@ static FILE *log_file; static int log_level; -static void log_event_cb(int, const char *); -static void log_vwrite(const char *, va_list); - /* Log callback for libevent. */ static void log_event_cb(__unused int severity, const char *msg) @@ -101,7 +98,7 @@ log_close(void) } /* Write a log message. */ -static void +static void printflike(1, 0) log_vwrite(const char *msg, va_list ap) { char *fmt, *out; diff --git a/tmux.h b/tmux.h index 6bdc7e6a..668ca270 100644 --- a/tmux.h +++ b/tmux.h @@ -2342,7 +2342,7 @@ struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *); struct cmdq_item *cmdq_get_error(const char *); struct cmdq_item *cmdq_insert_after(struct cmdq_item *, struct cmdq_item *); struct cmdq_item *cmdq_append(struct client *, struct cmdq_item *); -void cmdq_insert_hook(struct session *, struct cmdq_item *, +void printflike(4, 5) cmdq_insert_hook(struct session *, struct cmdq_item *, struct cmd_find_state *, const char *, ...); void cmdq_continue(struct cmdq_item *); u_int cmdq_next(struct client *); @@ -2398,7 +2398,7 @@ void file_fire_done(struct client_file *); void file_fire_read(struct client_file *); int file_can_print(struct client *); void printflike(2, 3) file_print(struct client *, const char *, ...); -void file_vprint(struct client *, const char *, va_list); +void printflike(2, 0) file_vprint(struct client *, const char *, va_list); void file_print_buffer(struct client *, void *, size_t); void printflike(2, 3) file_error(struct client *, const char *, ...); void file_write(struct client *, const char *, int, const void *, size_t, @@ -2498,7 +2498,8 @@ 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 status_message_set(struct client *, int, int, int, const char *, ...); +void printflike(5, 6) status_message_set(struct client *, int, 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 *, @@ -2653,7 +2654,7 @@ void printflike(3, 4) screen_write_puts(struct screen_write_ctx *, const struct grid_cell *, const char *, ...); void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *, ssize_t, const struct grid_cell *, const char *, ...); -void screen_write_vnputs(struct screen_write_ctx *, ssize_t, +void printflike(4, 0) screen_write_vnputs(struct screen_write_ctx *, ssize_t, const struct grid_cell *, const char *, va_list); void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *, u_char); @@ -2922,7 +2923,8 @@ extern const struct window_mode window_client_mode; extern const struct window_mode window_copy_mode; extern const struct window_mode window_view_mode; void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); -void window_copy_vadd(struct window_pane *, const char *, va_list); +void printflike(2, 0) window_copy_vadd(struct window_pane *, const char *, + va_list); void window_copy_pageup(struct window_pane *, int); void window_copy_start_drag(struct client *, struct mouse_event *); char *window_copy_get_word(struct window_pane *, u_int, u_int); diff --git a/xmalloc.h b/xmalloc.h index 3fe4f1cc..b4dcb535 100644 --- a/xmalloc.h +++ b/xmalloc.h @@ -30,12 +30,14 @@ int xasprintf(char **, const char *, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); int xvasprintf(char **, const char *, va_list) + __attribute__((__format__ (printf, 2, 0))) __attribute__((__nonnull__ (2))); int xsnprintf(char *, size_t, const char *, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))) __attribute__((__bounded__ (__string__, 1, 2))); int xvsnprintf(char *, size_t, const char *, va_list) + __attribute__((__format__ (printf, 3, 0))) __attribute__((__nonnull__ (3))) __attribute__((__bounded__ (__string__, 1, 2))); From 6ac09aa47c7fc03a2a418d4723064c100757a428 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 25 Aug 2021 08:36:35 +0100 Subject: [PATCH 0964/1006] Disable a couple of warnings on macOS. --- Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile.am b/Makefile.am index dadf13a0..eb08990a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -30,6 +30,9 @@ AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes AM_CFLAGS += -Wno-unused-result -Wno-format-y2k +if IS_DARWIN +AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align +endif AM_CPPFLAGS += -DDEBUG endif AM_CPPFLAGS += -iquote. From c6d6af49039d7fc3ec14c2240153226709497313 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 25 Aug 2021 07:37:20 +0000 Subject: [PATCH 0965/1006] setupterm needs char * not const char * on some platforms. --- tty-term.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-term.c b/tty-term.c index ae103fd7..b3d01850 100644 --- a/tty-term.c +++ b/tty-term.c @@ -661,7 +661,7 @@ tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps, const char *s; char tmp[11]; - if (setupterm(name, fd, &error) != OK) { + if (setupterm((char *)name, fd, &error) != OK) { switch (error) { case 1: xasprintf(cause, "can't use hardcopy terminal: %s", From 03d173cbd8e72c356512a0e19e356b07d518627a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 25 Aug 2021 08:51:55 +0000 Subject: [PATCH 0966/1006] Validate command argument types (string or command list) and give more useful error messages. --- arguments.c | 87 +++++++++++++++++++++++++++++++++++++++----- cmd-bind-key.c | 16 +++++++- cmd-command-prompt.c | 19 +++++++--- cmd-confirm-before.c | 15 ++++++-- cmd-display-menu.c | 40 ++++++++++++++++---- cmd-display-panes.c | 19 +++++++--- cmd-if-shell.c | 16 +++++++- cmd-run-shell.c | 16 +++++++- cmd-set-option.c | 20 ++++++++-- cmd.c | 10 ++++- key-bindings.c | 4 +- tmux.h | 12 +++++- 12 files changed, 230 insertions(+), 44 deletions(-) diff --git a/arguments.c b/arguments.c index bddb6365..fca57f08 100644 --- a/arguments.c +++ b/arguments.c @@ -124,10 +124,11 @@ args_create(void) /* Parse arguments into a new argument set. */ struct args * args_parse(const struct args_parse *parse, struct args_value *values, - u_int count) + u_int count, char **cause) { struct args *args; u_int i; + enum args_parse_type type; struct args_value *value, *new; u_char flag, argument; const char *found, *string, *s; @@ -153,11 +154,13 @@ args_parse(const struct args_parse *parse, struct args_value *values, if (flag == '\0') break; if (!isalnum(flag)) { + xasprintf(cause, "invalid flag -%c", flag); args_free(args); return (NULL); } found = strchr(parse->template, flag); if (found == NULL) { + xasprintf(cause, "unknown flag -%c", flag); args_free(args); return (NULL); } @@ -167,12 +170,22 @@ args_parse(const struct args_parse *parse, struct args_value *values, args_set(args, flag, NULL); continue; } - new = xcalloc(1, sizeof *value); + new = xcalloc(1, sizeof *new); if (*string != '\0') { new->type = ARGS_STRING; new->string = xstrdup(string); } else { if (i == count) { + xasprintf(cause, + "-%c expects an argument", + flag); + args_free(args); + return (NULL); + } + if (values[i].type != ARGS_STRING) { + xasprintf(cause, + "-%c argument must be a string", + flag); args_free(args); return (NULL); } @@ -192,14 +205,60 @@ args_parse(const struct args_parse *parse, struct args_value *values, s = args_value_as_string(value); log_debug("%s: %u = %s", __func__, i, s); + if (parse->cb != NULL) { + type = parse->cb(args, args->count, cause); + if (type == ARGS_PARSE_INVALID) { + args_free(args); + return (NULL); + } + } else + type = ARGS_PARSE_STRING; + args->values = xrecallocarray(args->values, args->count, args->count + 1, sizeof *args->values); - args_copy_value(&args->values[args->count++], value); + new = &args->values[args->count++]; + + switch (type) { + case ARGS_PARSE_INVALID: + fatalx("unexpected argument type"); + case ARGS_PARSE_STRING: + if (value->type != ARGS_STRING) { + xasprintf(cause, + "argument %u must be \"string\"", + args->count); + args_free(args); + return (NULL); + } + args_copy_value(new, value); + break; + case ARGS_PARSE_COMMANDS_OR_STRING: + args_copy_value(new, value); + break; + case ARGS_PARSE_COMMANDS: + if (value->type != ARGS_COMMANDS) { + xasprintf(cause, + "argument %u must be { commands }", + args->count); + args_free(args); + return (NULL); + } + args_copy_value(new, value); + break; + } } } - if ((parse->lower != -1 && args->count < (u_int)parse->lower) || - (parse->upper != -1 && args->count > (u_int)parse->upper)) { + if (parse->lower != -1 && args->count < (u_int)parse->lower) { + xasprintf(cause, + "too few arguments (need at least %u)", + parse->lower); + args_free(args); + return (NULL); + } + if (parse->upper != -1 && args->count > (u_int)parse->upper) { + xasprintf(cause, + "too many arguments (need at most %u)", + parse->upper); args_free(args); return (NULL); } @@ -254,15 +313,25 @@ args_free(struct args *args) void args_vector(struct args *args, int *argc, char ***argv) { - struct args_value *value; - u_int i; + char *s; + u_int i; *argc = 0; *argv = NULL; for (i = 0; i < args->count; i++) { - value = &args->values[i]; - cmd_append_argv(argc, argv, args_value_as_string(value)); + switch (args->values[i].type) { + case ARGS_NONE: + break; + case ARGS_STRING: + cmd_append_argv(argc, argv, args->values[i].string); + break; + case ARGS_COMMANDS: + s = cmd_list_print(args->values[i].cmdlist, 0); + cmd_append_argv(argc, argv, s); + free(s); + break; + } } } diff --git a/cmd-bind-key.c b/cmd-bind-key.c index bb905bce..778d655b 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -27,13 +27,16 @@ * Bind a key to a command. */ -static enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmdq_item *); +static enum args_parse_type cmd_bind_key_args_parse(struct args *, u_int, + char **); +static enum cmd_retval cmd_bind_key_exec(struct cmd *, + struct cmdq_item *); const struct cmd_entry cmd_bind_key_entry = { .name = "bind-key", .alias = "bind", - .args = { "nrN:T:", 1, -1, NULL }, + .args = { "nrN:T:", 1, -1, cmd_bind_key_args_parse }, .usage = "[-nr] [-T key-table] [-N note] key " "[command [arguments]]", @@ -41,6 +44,15 @@ const struct cmd_entry cmd_bind_key_entry = { .exec = cmd_bind_key_exec }; +static enum args_parse_type +cmd_bind_key_args_parse(__unused struct args *args, u_int idx, + __unused char **cause) +{ + if (idx == 1) + return (ARGS_PARSE_COMMANDS_OR_STRING); + return (ARGS_PARSE_STRING); +} + static enum cmd_retval cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) { diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index bca1a7fc..737c44c7 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -29,8 +29,10 @@ * Prompt for command in client. */ -static enum cmd_retval cmd_command_prompt_exec(struct cmd *, - struct cmdq_item *); +static enum args_parse_type cmd_command_prompt_args_parse(struct args *, + u_int, char **); +static enum cmd_retval cmd_command_prompt_exec(struct cmd *, + struct cmdq_item *); static int cmd_command_prompt_callback(struct client *, void *, const char *, int); @@ -40,7 +42,7 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1bFkiI:Np:t:T:", 0, 1, NULL }, + .args = { "1bFkiI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse }, .usage = "[-1bFkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [-T type] [template]", @@ -68,6 +70,13 @@ struct cmd_command_prompt_cdata { char **argv; }; +static enum args_parse_type +cmd_command_prompt_args_parse(__unused struct args *args, __unused u_int idx, + __unused char **cause) +{ + return (ARGS_PARSE_COMMANDS_OR_STRING); +} + static enum cmd_retval cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) { @@ -197,8 +206,8 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, return (1); out: - if (item != NULL) - cmdq_continue(item); + if (item != NULL) + cmdq_continue(item); return (0); } diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 4fe43302..8f50ba2d 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -28,8 +28,10 @@ * Asks for confirmation before executing a command. */ -static enum cmd_retval cmd_confirm_before_exec(struct cmd *, - struct cmdq_item *); +static enum args_parse_type cmd_confirm_before_args_parse(struct args *, + u_int, char **); +static enum cmd_retval cmd_confirm_before_exec(struct cmd *, + struct cmdq_item *); static int cmd_confirm_before_callback(struct client *, void *, const char *, int); @@ -39,7 +41,7 @@ const struct cmd_entry cmd_confirm_before_entry = { .name = "confirm-before", .alias = "confirm", - .args = { "bp:t:", 1, 1, NULL }, + .args = { "bp:t:", 1, 1, cmd_confirm_before_args_parse }, .usage = "[-b] [-p prompt] " CMD_TARGET_CLIENT_USAGE " command", .flags = CMD_CLIENT_TFLAG, @@ -51,6 +53,13 @@ struct cmd_confirm_before_data { struct cmd_list *cmdlist; }; +static enum args_parse_type +cmd_confirm_before_args_parse(__unused struct args *args, __unused u_int idx, + __unused char **cause) +{ + return (ARGS_PARSE_COMMANDS_OR_STRING); +} + static enum cmd_retval cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) { diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 378c62e1..b36894f4 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -28,16 +28,18 @@ * Display a menu on a client. */ -static enum cmd_retval cmd_display_menu_exec(struct cmd *, - struct cmdq_item *); -static enum cmd_retval cmd_display_popup_exec(struct cmd *, - struct cmdq_item *); +static enum args_parse_type cmd_display_menu_args_parse(struct args *, + u_int, char **); +static enum cmd_retval cmd_display_menu_exec(struct cmd *, + struct cmdq_item *); +static enum cmd_retval cmd_display_popup_exec(struct cmd *, + struct cmdq_item *); const struct cmd_entry cmd_display_menu_entry = { .name = "display-menu", .alias = "menu", - .args = { "c:t:OT:x:y:", 1, -1, NULL }, + .args = { "c:t:OT:x:y:", 1, -1, cmd_display_menu_args_parse }, .usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] " "[-x position] [-y position] name key command ...", @@ -53,8 +55,8 @@ const struct cmd_entry cmd_display_popup_entry = { .args = { "BCc:d:Eh:t:w:x:y:", 0, -1, NULL }, .usage = "[-BCE] [-c target-client] [-d start-directory] [-h height] " - CMD_TARGET_PANE_USAGE " [-w width] " - "[-x position] [-y position] [command]", + CMD_TARGET_PANE_USAGE " [-w width] " + "[-x position] [-y position] [shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -62,6 +64,30 @@ const struct cmd_entry cmd_display_popup_entry = { .exec = cmd_display_popup_exec }; +static enum args_parse_type +cmd_display_menu_args_parse(struct args *args, u_int idx, __unused char **cause) +{ + u_int i = 0; + enum args_parse_type type = ARGS_PARSE_STRING; + + for (;;) { + type = ARGS_PARSE_STRING; + if (i == idx) + break; + if (*args_string(args, i++) == '\0') + continue; + + type = ARGS_PARSE_STRING; + if (i++ == idx) + break; + + type = ARGS_PARSE_COMMANDS_OR_STRING; + if (i++ == idx) + break; + } + return (type); +} + static int cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, struct args *args, u_int *px, u_int *py, u_int w, u_int h) diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 169ab3d5..5773a2d0 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -27,14 +27,16 @@ * Display panes on a client. */ -static enum cmd_retval cmd_display_panes_exec(struct cmd *, - struct cmdq_item *); +static enum args_parse_type cmd_display_panes_args_parse(struct args *, + u_int, char **); +static enum cmd_retval cmd_display_panes_exec(struct cmd *, + struct cmdq_item *); const struct cmd_entry cmd_display_panes_entry = { .name = "display-panes", .alias = "displayp", - .args = { "bd:Nt:", 0, 1, NULL }, + .args = { "bd:Nt:", 0, 1, cmd_display_panes_args_parse }, .usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, @@ -46,6 +48,13 @@ struct cmd_display_panes_data { struct args_command_state *state; }; +static enum args_parse_type +cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx, + __unused char **cause) +{ + return (ARGS_PARSE_COMMANDS_OR_STRING); +} + static void cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) @@ -139,7 +148,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, 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, buf, len, len); tty_putn(tty, " ", 1, 1); tty_putn(tty, lbuf, llen, llen); } else { @@ -263,7 +272,7 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct client *tc = cmdq_get_target_client(item); struct session *s = tc->session; - u_int delay; + u_int delay; char *cause; struct cmd_display_panes_data *cdata; int wait = !args_has(args, 'b'); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 7804ef27..907a96e1 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -29,7 +29,10 @@ * Executes a tmux command if a shell command returns true or false. */ -static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmdq_item *); +static enum args_parse_type cmd_if_shell_args_parse(struct args *, u_int, + char **); +static enum cmd_retval cmd_if_shell_exec(struct cmd *, + struct cmdq_item *); static void cmd_if_shell_callback(struct job *); static void cmd_if_shell_free(void *); @@ -38,7 +41,7 @@ const struct cmd_entry cmd_if_shell_entry = { .name = "if-shell", .alias = "if", - .args = { "bFt:", 2, 3, NULL }, + .args = { "bFt:", 2, 3, cmd_if_shell_args_parse }, .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " "[command]", @@ -56,6 +59,15 @@ struct cmd_if_shell_data { struct cmdq_item *item; }; +static enum args_parse_type +cmd_if_shell_args_parse(__unused struct args *args, u_int idx, + __unused char **cause) +{ + if (idx == 1 || idx == 2) + return (ARGS_PARSE_COMMANDS_OR_STRING); + return (ARGS_PARSE_STRING); +} + static enum cmd_retval cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) { diff --git a/cmd-run-shell.c b/cmd-run-shell.c index f23a8ec3..662312fb 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -30,7 +30,10 @@ * Runs a command without a window. */ -static enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmdq_item *); +static enum args_parse_type cmd_run_shell_args_parse(struct args *, u_int, + char **); +static enum cmd_retval cmd_run_shell_exec(struct cmd *, + struct cmdq_item *); static void cmd_run_shell_timer(int, short, void *); static void cmd_run_shell_callback(struct job *); @@ -41,7 +44,7 @@ const struct cmd_entry cmd_run_shell_entry = { .name = "run-shell", .alias = "run", - .args = { "bd:Ct:", 0, 1, NULL }, + .args = { "bd:Ct:", 0, 1, cmd_run_shell_args_parse }, .usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -62,6 +65,15 @@ struct cmd_run_shell_data { int flags; }; +static enum args_parse_type +cmd_run_shell_args_parse(struct args *args, __unused u_int idx, + __unused char **cause) +{ + if (args_has(args, 'C')) + return (ARGS_PARSE_COMMANDS_OR_STRING); + return (ARGS_PARSE_STRING); +} + static void cmd_run_shell_print(struct job *job, const char *msg) { diff --git a/cmd-set-option.c b/cmd-set-option.c index 8839dc0d..fbe32cfa 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -27,13 +27,16 @@ * Set an option. */ -static enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmdq_item *); +static enum args_parse_type cmd_set_option_args_parse(struct args *, + u_int, char **); +static enum cmd_retval cmd_set_option_exec(struct cmd *, + struct cmdq_item *); const struct cmd_entry cmd_set_option_entry = { .name = "set-option", .alias = "set", - .args = { "aFgopqst:uUw", 1, 2, NULL }, + .args = { "aFgopqst:uUw", 1, 2, cmd_set_option_args_parse }, .usage = "[-aFgopqsuUw] " CMD_TARGET_PANE_USAGE " option [value]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -46,7 +49,7 @@ const struct cmd_entry cmd_set_window_option_entry = { .name = "set-window-option", .alias = "setw", - .args = { "aFgoqt:u", 1, 2, NULL }, + .args = { "aFgoqt:u", 1, 2, cmd_set_option_args_parse }, .usage = "[-aFgoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, @@ -59,7 +62,7 @@ const struct cmd_entry cmd_set_hook_entry = { .name = "set-hook", .alias = NULL, - .args = { "agpRt:uw", 1, 2, NULL }, + .args = { "agpRt:uw", 1, 2, cmd_set_option_args_parse }, .usage = "[-agpRuw] " CMD_TARGET_PANE_USAGE " hook [command]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -68,6 +71,15 @@ const struct cmd_entry cmd_set_hook_entry = { .exec = cmd_set_option_exec }; +static enum args_parse_type +cmd_set_option_args_parse(__unused struct args *args, u_int idx, + __unused char **cause) +{ + if (idx == 1) + return (ARGS_PARSE_COMMANDS_OR_STRING); + return (ARGS_PARSE_STRING); +} + static enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) { diff --git a/cmd.c b/cmd.c index 930ee56c..62c8b9ab 100644 --- a/cmd.c +++ b/cmd.c @@ -502,6 +502,7 @@ cmd_parse(struct args_value *values, u_int count, const char *file, u_int line, const struct cmd_entry *entry; struct cmd *cmd; struct args *args; + char *error; if (count == 0 || values[0].type != ARGS_STRING) { xasprintf(cause, "no command"); @@ -511,11 +512,16 @@ cmd_parse(struct args_value *values, u_int count, const char *file, u_int line, if (entry == NULL) return (NULL); - args = args_parse(&entry->args, values, count); - if (args == NULL) { + args = args_parse(&entry->args, values, count, &error); + if (args == NULL && error == NULL) { xasprintf(cause, "usage: %s %s", entry->name, entry->usage); return (NULL); } + if (args == NULL) { + xasprintf(cause, "command %s: %s", entry->name, error); + free(error); + return (NULL); + } cmd = xcalloc(1, sizeof *cmd); cmd->entry = entry; diff --git a/key-bindings.c b/key-bindings.c index e3b21d61..66e1ec9f 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -634,8 +634,10 @@ key_bindings_init(void) for (i = 0; i < nitems(defaults); i++) { pr = cmd_parse_from_string(defaults[i], NULL); - if (pr->status != CMD_PARSE_SUCCESS) + if (pr->status != CMD_PARSE_SUCCESS) { + log_debug("%s", pr->error); fatalx("bad default key: %s", defaults[i]); + } cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL)); cmd_list_free(pr->cmdlist); } diff --git a/tmux.h b/tmux.h index 668ca270..96baaf4d 100644 --- a/tmux.h +++ b/tmux.h @@ -1378,8 +1378,16 @@ struct args_value { struct args_entry; RB_HEAD(args_tree, args_entry); +/* Arguments parsing type. */ +enum args_parse_type { + ARGS_PARSE_INVALID, + ARGS_PARSE_STRING, + ARGS_PARSE_COMMANDS_OR_STRING, + ARGS_PARSE_COMMANDS +}; + /* Arguments parsing state. */ -typedef enum args_type (*args_parse_cb)(struct args *, u_int); +typedef enum args_parse_type (*args_parse_cb)(struct args *, u_int, char **); struct args_parse { const char *template; int lower; @@ -2201,7 +2209,7 @@ int tty_keys_next(struct tty *); void args_set(struct args *, u_char, struct args_value *); struct args *args_create(void); struct args *args_parse(const struct args_parse *, struct args_value *, - u_int); + u_int, char **); void args_vector(struct args *, int *, char ***); void args_free_value(struct args_value *); void args_free(struct args *); From a3c6057b514eb575ba40fc2f8b2d471aea9909fc Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 25 Aug 2021 09:18:08 +0000 Subject: [PATCH 0967/1006] bind-key needs to allow commands for any argument for the moment. --- cmd-bind-key.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 778d655b..be0ae40e 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -45,12 +45,10 @@ const struct cmd_entry cmd_bind_key_entry = { }; static enum args_parse_type -cmd_bind_key_args_parse(__unused struct args *args, u_int idx, +cmd_bind_key_args_parse(__unused struct args *args, __unused u_int idx, __unused char **cause) { - if (idx == 1) - return (ARGS_PARSE_COMMANDS_OR_STRING); - return (ARGS_PARSE_STRING); + return (ARGS_PARSE_COMMANDS_OR_STRING); } static enum cmd_retval From 6616b42b2c28cd7a1b6d52dfc895f5a1d3dce8b3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 25 Aug 2021 10:24:33 +0100 Subject: [PATCH 0968/1006] Fix test. --- regress/control-client-sanity.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/regress/control-client-sanity.sh b/regress/control-client-sanity.sh index 691d3d87..48ffd0ee 100644 --- a/regress/control-client-sanity.sh +++ b/regress/control-client-sanity.sh @@ -14,6 +14,7 @@ $TMUX -f/dev/null new -d -x200 -y200 || exit 1 $TMUX -f/dev/null splitw || exit 1 sleep 1 cat <$TMP +refresh-client -C 200x200 selectp -t%0 splitw neww From f4f8d3b5ede9da429025e51650db4fbad48171b5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 25 Aug 2021 10:15:15 +0000 Subject: [PATCH 0969/1006] Ignore client creating session when working out size if it is a control client. --- resize.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/resize.c b/resize.c index 38cda23d..91dea48e 100644 --- a/resize.c +++ b/resize.c @@ -243,6 +243,13 @@ default_window_size(struct client *c, struct session *s, struct window *w, } } + /* + * Ignore the given client if it is a control client - the creating + * client should only affect the size if it is not a control client. + */ + if (c != NULL && (c->flags & CLIENT_CONTROL)) + c = NULL; + /* * Look for a client to base the size on. If none exists (or the type * is manual), use the default-size option. From 24636be42b4b0463afe5c72e1d982f28729a0579 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 25 Aug 2021 10:18:01 +0000 Subject: [PATCH 0970/1006] Improve some logging. --- resize.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/resize.c b/resize.c index 91dea48e..f321e7d2 100644 --- a/resize.c +++ b/resize.c @@ -51,7 +51,7 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) if (sy < w->layout_root->sy) sy = w->layout_root->sy; window_resize(w, sx, sy, xpixel, ypixel); - log_debug("%s: @%u resized to %u,%u; layout %u,%u", __func__, w->id, + log_debug("%s: @%u resized to %ux%u; layout %ux%u", __func__, w->id, sx, sy, w->layout_root->sx, w->layout_root->sy); /* Restore the window zoom state. */ @@ -186,6 +186,10 @@ clients_calculate_size(int type, int current, struct client *c, log_debug("%s: after %s (%ux%u), size is %ux%u", __func__, loop->name, cx, cy, *sx, *sy); } + if (*sx != UINT_MAX && *sy != UINT_MAX) + log_debug("%s: calculated size %ux%u", __func__, *sx, *sy); + else + log_debug("%s: no calculated size", __func__); /* Return whether a suitable size was found. */ if (type == WINDOW_SIZE_LARGEST) { @@ -231,16 +235,14 @@ default_window_size(struct client *c, struct session *s, struct window *w, * 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; - log_debug("%s: using %ux%u from %s", __func__, *sx, *sy, - c->name); - goto done; - } + if (type == WINDOW_SIZE_LATEST && 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; + log_debug("%s: using %ux%u from %s", __func__, *sx, *sy, + c->name); + goto done; } /* @@ -304,7 +306,7 @@ recalculate_size(struct window *w, int now) */ if (w->active == NULL) return; - log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy); + log_debug("%s: @%u is %ux%u", __func__, w->id, w->sx, w->sy); /* * Type is manual, smallest, largest, latest. Current is the @@ -335,6 +337,7 @@ recalculate_size(struct window *w, int now) * size. */ if (!changed) { + log_debug("%s: @%u no size change", __func__, w->id); tty_update_window_offset(w); return; } @@ -344,7 +347,7 @@ recalculate_size(struct window *w, int now) * 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); + log_debug("%s: @%u new size %ux%u", __func__, w->id, sx, sy); if (now || type == WINDOW_SIZE_MANUAL) resize_window(w, sx, sy, xpixel, ypixel); else { From fd756a150b43d319d08ac4117f34edef9e0438c4 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 27 Aug 2021 17:15:57 +0000 Subject: [PATCH 0971/1006] Allow control mode clients to set a hard limit on the window width and height, GitHub issue 2594. --- cmd-refresh-client.c | 72 ++++++++++++++++++++++++++-------- cmd-resize-window.c | 4 +- resize.c | 93 ++++++++++++++++++++++++++++++++++++-------- server-client.c | 24 ++++++++---- tmux.1 | 14 ++++++- tmux.h | 9 +++++ window.c | 2 + 7 files changed, 174 insertions(+), 44 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 24a49dcb..821558ae 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -77,6 +77,58 @@ out: free(copy); } +static enum cmd_retval +cmd_refresh_client_control_client_size(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); + const char *size = args_get(args, 'C'); + u_int w, x, y; + struct client_window *cw; + + if (sscanf(size, "@%u:%ux%u", &w, &x, &y) == 3) { + if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || + y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { + cmdq_error(item, "size too small or too big"); + return (CMD_RETURN_ERROR); + } + log_debug("%s: client %s window @%u: size %ux%u", __func__, + tc->name, w, x, y); + cw = server_client_add_client_window(tc, w); + cw->sx = x; + cw->sy = y; + tc->flags |= CLIENT_WINDOWSIZECHANGED; + recalculate_sizes_now(1); + return (CMD_RETURN_NORMAL); + } + if (sscanf(size, "@%u:", &w) == 1) { + cw = server_client_get_client_window(tc, w); + if (cw != NULL) { + log_debug("%s: client %s window @%u: no size", __func__, + tc->name, w); + cw->sx = 0; + cw->sy = 0; + recalculate_sizes_now(1); + } + return (CMD_RETURN_NORMAL); + } + + if (sscanf(size, "%u,%u", &x, &y) != 2 && + sscanf(size, "%ux%u", &x, &y) != 2) { + cmdq_error(item, "bad size argument"); + return (CMD_RETURN_ERROR); + } + if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || + y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { + cmdq_error(item, "size too small or too big"); + return (CMD_RETURN_ERROR); + } + tty_set_size(&tc->tty, x, y, 0, 0); + tc->flags |= CLIENT_SIZECHANGED; + recalculate_sizes_now(1); + return (CMD_RETURN_NORMAL); +} + static void cmd_refresh_client_update_offset(struct client *tc, const char *value) { @@ -117,8 +169,8 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct tty *tty = &tc->tty; struct window *w; - const char *size, *errstr; - u_int x, y, adjust; + const char *errstr; + u_int adjust; struct args_value *av; if (args_has(args, 'c') || @@ -205,21 +257,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'C')) { if (~tc->flags & CLIENT_CONTROL) goto not_control_client; - size = args_get(args, 'C'); - if (sscanf(size, "%u,%u", &x, &y) != 2 && - sscanf(size, "%ux%u", &x, &y) != 2) { - cmdq_error(item, "bad size argument"); - return (CMD_RETURN_ERROR); - } - if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || - y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { - cmdq_error(item, "size too small or too big"); - return (CMD_RETURN_ERROR); - } - tty_set_size(&tc->tty, x, y, 0, 0); - tc->flags |= CLIENT_SIZECHANGED; - recalculate_sizes_now(1); - return (CMD_RETURN_NORMAL); + return (cmd_refresh_client_control_client_size(self, item)); } if (args_has(args, 'S')) { diff --git a/cmd-resize-window.c b/cmd-resize-window.c index 24d73c87..ad739165 100644 --- a/cmd-resize-window.c +++ b/cmd-resize-window.c @@ -108,7 +108,9 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) } options_set_number(w->options, "window-size", WINDOW_SIZE_MANUAL); - resize_window(w, sx, sy, xpixel, ypixel); + w->manual_sx = sx; + w->manual_sy = sy; + recalculate_size(w, 1); return (CMD_RETURN_NORMAL); } diff --git a/resize.c b/resize.c index f321e7d2..8416ad6a 100644 --- a/resize.c +++ b/resize.c @@ -87,7 +87,9 @@ ignore_client_size(struct client *c) return (1); } } - if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED)) + if ((c->flags & CLIENT_CONTROL) && + (~c->flags & CLIENT_SIZECHANGED) && + (~c->flags & CLIENT_WINDOWSIZECHANGED)) return (1); return (0); } @@ -113,23 +115,25 @@ clients_calculate_size(int type, int current, struct client *c, 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) { - log_debug("%s: type is manual", __func__); - return (0); - } + struct client *loop; + struct client_window *cw; + u_int cx, cy, n = 0; /* * Start comparing with 0 for largest and UINT_MAX for smallest or * latest. */ - if (type == WINDOW_SIZE_LARGEST) - *sx = *sy = 0; - else - *sx = *sy = UINT_MAX; + if (type == WINDOW_SIZE_LARGEST) { + *sx = 0; + *sy = 0; + } else if (type == WINDOW_SIZE_MANUAL) { + *sx = w->manual_sx; + *sy = w->manual_sy; + log_debug("%s: manual size %ux%u", __func__, *sx, *sy); + } else { + *sx = UINT_MAX; + *sy = UINT_MAX; + } *xpixel = *ypixel = 0; /* @@ -139,14 +143,18 @@ clients_calculate_size(int type, int current, struct client *c, if (type == WINDOW_SIZE_LATEST && w != NULL) n = clients_with_window(w); + /* Skip setting the size if manual */ + if (type == WINDOW_SIZE_MANUAL) + goto skip; + /* Loop over the clients and work out the size. */ TAILQ_FOREACH(loop, &clients, entry) { if (loop != c && ignore_client_size(loop)) { - log_debug("%s: ignoring %s", __func__, loop->name); + log_debug("%s: ignoring %s (1)", __func__, loop->name); continue; } if (loop != c && skip_client(loop, type, current, s, w)) { - log_debug("%s: skipping %s", __func__, loop->name); + log_debug("%s: skipping %s (1)", __func__, loop->name); continue; } @@ -160,9 +168,23 @@ clients_calculate_size(int type, int current, struct client *c, continue; } + /* + * If the client has a per-window size, use this instead if it is + * smaller. + */ + if (w != NULL) + cw = server_client_get_client_window(loop, w->id); + else + cw = NULL; + /* Work out this client's size. */ - cx = loop->tty.sx; - cy = loop->tty.sy - status_line_size(loop); + if (cw != NULL) { + cx = cw->sx; + cy = cw->sy; + } else { + 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 @@ -191,7 +213,44 @@ clients_calculate_size(int type, int current, struct client *c, else log_debug("%s: no calculated size", __func__); +skip: + /* + * Do not allow any size to be larger than the per-client window size + * if one exists. + */ + if (w != NULL) { + TAILQ_FOREACH(loop, &clients, entry) { + if (loop != c && ignore_client_size(loop)) + continue; + if (loop != c && skip_client(loop, type, current, s, w)) + continue; + + /* Look up per-window size if any. */ + if (~loop->flags & CLIENT_WINDOWSIZECHANGED) + continue; + cw = server_client_get_client_window(loop, w->id); + if (cw == NULL) + continue; + + /* Clamp the size. */ + log_debug("%s: %s size for @%u is %ux%u", __func__, + loop->name, w->id, cw->sx, cw->sy); + if (cw->sx != 0 && *sx > cw->sx) + *sx = cw->sx; + if (cw->sy != 0 && *sy > cw->sy) + *sy = cw->sy; + } + } + if (*sx != UINT_MAX && *sy != UINT_MAX) + log_debug("%s: calculated size %ux%u", __func__, *sx, *sy); + else + log_debug("%s: no calculated size", __func__); + /* Return whether a suitable size was found. */ + if (type == WINDOW_SIZE_MANUAL) { + log_debug("%s: type is manual", __func__); + return (1); + } if (type == WINDOW_SIZE_LARGEST) { log_debug("%s: type is largest", __func__); return (*sx != 0 && *sy != 0); diff --git a/server-client.c b/server-client.c index ac9a7475..062e72d4 100644 --- a/server-client.c +++ b/server-client.c @@ -2443,7 +2443,7 @@ server_client_get_flags(struct client *c) } /* Get client window. */ -static struct client_window * +struct client_window * server_client_get_client_window(struct client *c, u_int id) { struct client_window cw = { .window = id }; @@ -2451,6 +2451,21 @@ server_client_get_client_window(struct client *c, u_int id) return (RB_FIND(client_windows, &c->windows, &cw)); } +/* Add client window. */ +struct client_window * +server_client_add_client_window(struct client *c, u_int id) +{ + struct client_window *cw; + + cw = server_client_get_client_window(c, id); + if (cw == NULL) { + cw = xcalloc(1, sizeof *cw); + cw->window = id; + RB_INSERT(client_windows, &c->windows, cw); + } + return cw; +} + /* Get client active pane. */ struct window_pane * server_client_get_pane(struct client *c) @@ -2479,12 +2494,7 @@ server_client_set_pane(struct client *c, struct window_pane *wp) if (s == NULL) return; - cw = server_client_get_client_window(c, s->curw->window->id); - if (cw == NULL) { - cw = xcalloc(1, sizeof *cw); - cw->window = s->curw->window->id; - RB_INSERT(client_windows, &c->windows, cw); - } + cw = server_client_add_client_window(c, s->curw->window->id); cw->pane = wp; log_debug("%s pane now %%%u", c->name, wp->id); } diff --git a/tmux.1 b/tmux.1 index 28c54905..d48415fe 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1331,7 +1331,7 @@ specified multiple times. .Op Fl cDlLRSU .Op Fl A Ar pane:state .Op Fl B Ar name:what:format -.Op Fl C Ar XxY +.Op Fl C Ar size .Op Fl f Ar flags .Op Fl t Ar target-client .Op Ar adjustment @@ -1375,7 +1375,17 @@ window, changing the current window in the attached session will reset it. .Pp .Fl C -sets the width and height of a control mode client. +sets the width and height of a control mode client or of a window for a +control mode client, +.Ar size +must be one of +.Ql widthxheight +or +.Ql window ID:widthxheight , +for example +.Ql 80x24 +or +.Ql @0:80x24 . .Fl A allows a control mode client to trigger actions on a pane. The argument is a pane ID (with leading diff --git a/tmux.h b/tmux.h index 96baaf4d..98c3b9a3 100644 --- a/tmux.h +++ b/tmux.h @@ -998,6 +998,8 @@ struct window { u_int sx; u_int sy; + u_int manual_sx; + u_int manual_sy; u_int xpixel; u_int ypixel; @@ -1555,6 +1557,10 @@ RB_HEAD(client_files, client_file); struct client_window { u_int window; struct window_pane *pane; + + u_int sx; + u_int sy; + RB_ENTRY(client_window) entry; }; RB_HEAD(client_windows, client_window); @@ -1650,6 +1656,7 @@ struct client { #define CLIENT_ACTIVEPANE 0x80000000ULL #define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL +#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -2465,6 +2472,8 @@ void server_client_push_stderr(struct client *); const char *server_client_get_cwd(struct client *, struct session *); void server_client_set_flags(struct client *, const char *); const char *server_client_get_flags(struct client *); +struct client_window *server_client_get_client_window(struct client *, u_int); +struct client_window *server_client_add_client_window(struct client *, u_int); struct window_pane *server_client_get_pane(struct client *); void server_client_set_pane(struct client *, struct window_pane *); void server_client_remove_pane(struct window_pane *); diff --git a/window.c b/window.c index 584dbf9c..64d75d43 100644 --- a/window.c +++ b/window.c @@ -318,6 +318,8 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) w->sx = sx; w->sy = sy; + w->manual_sx = sx; + w->manual_sy = sy; w->xpixel = xpixel; w->ypixel = ypixel; From daec63e5e6eb3390d53f4bf7f8a327df77e46c95 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 27 Aug 2021 17:25:55 +0000 Subject: [PATCH 0972/1006] Replace %% in command lists (by copying them) for template arguments , this means they can be used with {} as well. Also make argument processing from an existing vector preserve commands. GitHub issue 2858. --- arguments.c | 115 ++++++++++++++++++++++++++++++++++----- client.c | 12 +++-- cmd-bind-key.c | 8 ++- cmd-choose-tree.c | 18 +++++-- cmd-command-prompt.c | 25 ++++++--- cmd-display-menu.c | 2 +- cmd-new-session.c | 5 +- cmd-new-window.c | 4 +- cmd-parse.y | 124 +++++++++++++++++++++++-------------------- cmd-respawn-pane.c | 6 +-- cmd-respawn-window.c | 5 +- cmd-split-window.c | 6 +-- cmd.c | 48 +++++++++++++++++ key-bindings.c | 80 ++++++++++++++-------------- server-client.c | 6 ++- tmux.h | 10 +++- 16 files changed, 324 insertions(+), 150 deletions(-) diff --git a/arguments.c b/arguments.c index fca57f08..d7274ff8 100644 --- a/arguments.c +++ b/arguments.c @@ -166,7 +166,7 @@ args_parse(const struct args_parse *parse, struct args_value *values, } argument = *++found; if (argument != ':') { - log_debug("%s: add -%c", __func__, flag); + log_debug("%s: -%c", __func__, flag); args_set(args, flag, NULL); continue; } @@ -192,7 +192,7 @@ args_parse(const struct args_parse *parse, struct args_value *values, args_copy_value(new, &values[i++]); } s = args_value_as_string(new); - log_debug("%s: add -%c = %s", __func__, flag, s); + log_debug("%s: -%c = %s", __func__, flag, s); args_set(args, flag, new); break; } @@ -203,7 +203,8 @@ args_parse(const struct args_parse *parse, struct args_value *values, value = &values[i]; s = args_value_as_string(value); - log_debug("%s: %u = %s", __func__, i, s); + log_debug("%s: %u = %s (type %d)", __func__, i, s, + value->type); if (parse->cb != NULL) { type = parse->cb(args, args->count, cause); @@ -265,6 +266,63 @@ args_parse(const struct args_parse *parse, struct args_value *values, return (args); } +/* Copy and expand a value. */ +static void +args_copy_copy_value(struct args_value *to, struct args_value *from, int argc, + char **argv) +{ + char *s, *expanded; + int i; + + to->type = from->type; + switch (from->type) { + case ARGS_NONE: + break; + case ARGS_STRING: + expanded = xstrdup(from->string); + for (i = 0; i < argc; i++) { + s = cmd_template_replace(expanded, argv[i], i + 1); + free(expanded); + expanded = s; + } + to->string = expanded; + break; + case ARGS_COMMANDS: + to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv); + break; + } +} + +/* Copy an arguments set. */ +struct args * +args_copy(struct args *args, int argc, char **argv) +{ + struct args *new_args; + struct args_entry *entry; + struct args_value *value, *new_value; + u_int i; + + new_args = args_create(); + RB_FOREACH(entry, args_tree, &args->tree) { + if (entry->count == 1) { + args_set(new_args, entry->flag, NULL); + continue; + } + TAILQ_FOREACH(value, &entry->values, entry) { + new_value = xcalloc(1, sizeof *new_value); + args_copy_copy_value(new_value, value, argc, argv); + args_set(new_args, entry->flag, new_value); + } + } + new_args->count = args->count; + new_args->values = xcalloc(args->count, sizeof *new_args->values); + for (i = 0; i < args->count; i++) { + new_value = &new_args->values[i]; + args_copy_copy_value(new_value, &args->values[i], argc, argv); + } + return (new_args); +} + /* Free a value. */ void args_free_value(struct args_value *value) @@ -282,6 +340,16 @@ args_free_value(struct args_value *value) free(value->cached); } +/* Free values. */ +void +args_free_values(struct args_value *values, u_int count) +{ + u_int i; + + for (i = 0; i < count; i++) + args_free_value(&values[i]); +} + /* Free an arguments set. */ void args_free(struct args *args) @@ -290,10 +358,8 @@ args_free(struct args *args) struct args_entry *entry1; struct args_value *value; struct args_value *value1; - u_int i; - for (i = 0; i < args->count; i++) - args_free_value(&args->values[i]); + args_free_values(args->values, args->count); free(args->values); RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { @@ -311,7 +377,7 @@ args_free(struct args *args) /* Convert arguments to vector. */ void -args_vector(struct args *args, int *argc, char ***argv) +args_to_vector(struct args *args, int *argc, char ***argv) { char *s; u_int i; @@ -335,6 +401,21 @@ args_vector(struct args *args, int *argc, char ***argv) } } +/* Convert arguments from vector. */ +struct args_value * +args_from_vector(int argc, char **argv) +{ + struct args_value *values; + int i; + + values = xcalloc(argc, sizeof *values); + for (i = 0; i < argc; i++) { + values[i].type = ARGS_STRING; + values[i].string = xstrdup(argv[i]); + } + return (values); +} + /* Add to string. */ static void printflike(3, 4) args_print_add(char **buf, size_t *len, const char *fmt, ...) @@ -424,7 +505,7 @@ args_print(struct args *args) char * args_escape(const char *s) { - static const char dquoted[] = " #';${}"; + static const char dquoted[] = " #';${}%"; static const char squoted[] = " \""; char *escaped, *result; int flags, quotes = 0; @@ -538,6 +619,13 @@ args_count(struct args *args) return (args->count); } +/* Get argument values. */ +struct args_value * +args_values(struct args *args) +{ + return (args->values); +} + /* Get argument value. */ struct args_value * args_value(struct args *args, u_int idx) @@ -570,8 +658,8 @@ args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx) cmdq_error(item, "%s", error); free(error); } - else - cmdlist->references++; + else + cmdlist->references++; args_make_commands_free(state); return (cmdlist); } @@ -631,8 +719,11 @@ args_make_commands(struct args_command_state *state, int argc, char **argv, char *cmd, *new_cmd; int i; - if (state->cmdlist != NULL) - return (state->cmdlist); + if (state->cmdlist != NULL) { + if (argc == 0) + return (state->cmdlist); + return (cmd_list_copy(state->cmdlist, argc, argv)); + } cmd = xstrdup(state->cmd); for (i = 0; i < argc; i++) { diff --git a/client.c b/client.c index 002cc6c7..cb64205e 100644 --- a/client.c +++ b/client.c @@ -244,6 +244,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, ssize_t linelen; char *line = NULL, **caps = NULL, *cause; u_int ncaps = 0; + struct args_value *values; /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ signal(SIGCHLD, SIG_IGN); @@ -259,17 +260,20 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, msg = MSG_COMMAND; /* - * It sucks parsing the command string twice (in client and - * later in server) but it is necessary to get the start server - * flag. + * It's annoying parsing the command string twice (in client + * and later in server) but it is necessary to get the start + * server flag. */ - pr = cmd_parse_from_arguments(argc, argv, NULL); + values = args_from_vector(argc, argv); + pr = cmd_parse_from_arguments(values, argc, NULL); if (pr->status == CMD_PARSE_SUCCESS) { if (cmd_list_any_have(pr->cmdlist, CMD_STARTSERVER)) flags |= CLIENT_STARTSERVER; cmd_list_free(pr->cmdlist); } else free(pr->error); + args_free_values(values, argc); + free(values); } /* Create client process structure (starts logging). */ diff --git a/cmd-bind-key.c b/cmd-bind-key.c index be0ae40e..dab03b01 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -58,8 +58,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) key_code key; const char *tablename, *note = args_get(args, 'N'); struct cmd_parse_result *pr; - char **argv; - int argc, repeat; + int repeat; struct args_value *value; u_int count = args_count(args); @@ -92,9 +91,8 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) if (count == 2) pr = cmd_parse_from_string(args_string(args, 1), NULL); else { - args_vector(args, &argc, &argv); - pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL); - cmd_free_argv(argc, argv); + pr = cmd_parse_from_arguments(args_values(args) + 1, count - 1, + NULL); } switch (pr->status) { case CMD_PARSE_ERROR: diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 9258f366..7aa1d217 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -24,13 +24,16 @@ * Enter a mode. */ -static enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmdq_item *); +static enum args_parse_type cmd_choose_tree_args_parse(struct args *args, + u_int idx, char **cause); +static enum cmd_retval cmd_choose_tree_exec(struct cmd *, + struct cmdq_item *); const struct cmd_entry cmd_choose_tree_entry = { .name = "choose-tree", .alias = NULL, - .args = { "F:f:GK:NO:rst:wZ", 0, 1, NULL }, + .args = { "F:f:GK:NO:rst:wZ", 0, 1, cmd_choose_tree_args_parse }, .usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", @@ -44,7 +47,7 @@ const struct cmd_entry cmd_choose_client_entry = { .name = "choose-client", .alias = NULL, - .args = { "F:f:K:NO:rt:Z", 0, 1, NULL }, + .args = { "F:f:K:NO:rt:Z", 0, 1, cmd_choose_tree_args_parse }, .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", @@ -58,7 +61,7 @@ const struct cmd_entry cmd_choose_buffer_entry = { .name = "choose-buffer", .alias = NULL, - .args = { "F:f:K:NO:rt:Z", 0, 1, NULL }, + .args = { "F:f:K:NO:rt:Z", 0, 1, cmd_choose_tree_args_parse }, .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", @@ -81,6 +84,13 @@ const struct cmd_entry cmd_customize_mode_entry = { .exec = cmd_choose_tree_exec }; +static enum args_parse_type +cmd_choose_tree_args_parse(__unused struct args *args, __unused u_int idx, + __unused char **cause) +{ + return (ARGS_PARSE_COMMANDS_OR_STRING); +} + static enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) { diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 737c44c7..820053ec 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -169,11 +169,13 @@ static int cmd_command_prompt_callback(struct client *c, void *data, const char *s, int done) { - struct cmd_command_prompt_cdata *cdata = data; - char *error; - struct cmdq_item *item = cdata->item, *new_item; - struct cmd_list *cmdlist; - struct cmd_command_prompt_prompt *prompt; + struct cmd_command_prompt_cdata *cdata = data; + char *error; + struct cmdq_item *item = cdata->item, *new_item; + struct cmd_list *cmdlist; + struct cmd_command_prompt_prompt *prompt; + int argc = 0; + char **argv = NULL; if (s == NULL) goto out; @@ -181,7 +183,6 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, if (cdata->flags & PROMPT_INCREMENTAL) goto out; - cmd_append_argv(&cdata->argc, &cdata->argv, s); if (++cdata->current != cdata->count) { prompt = &cdata->prompts[cdata->current]; status_prompt_update(c, prompt->prompt, prompt->input); @@ -189,8 +190,15 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, } } - cmdlist = args_make_commands(cdata->state, cdata->argc, cdata->argv, - &error); + argc = cdata->argc; + argv = cmd_copy_argv(cdata->argc, cdata->argv); + cmd_append_argv(&argc, &argv, s); + if (done) { + cdata->argc = argc; + cdata->argv = cmd_copy_argv(argc, argv); + } + + cmdlist = args_make_commands(cdata->state, argc, argv, &error); if (cmdlist == NULL) { cmdq_append(c, cmdq_get_error(error)); free(error); @@ -201,6 +209,7 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); cmdq_insert_after(item, new_item); } + cmd_free_argv(argc, argv); if (c->prompt_inputcb != cmd_command_prompt_callback) return (1); diff --git a/cmd-display-menu.c b/cmd-display-menu.c index b36894f4..5df01c7a 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -408,7 +408,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) shell = _PATH_BSHELL; cmd_append_argv(&argc, &argv, shell); } else - args_vector(args, &argc, &argv); + args_to_vector(args, &argc, &argv); if (args_has(args, 'E') > 1) flags |= POPUP_CLOSEEXITZERO; diff --git a/cmd-new-session.c b/cmd-new-session.c index 93c7e7b4..cb9abfb3 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -42,7 +42,8 @@ const struct cmd_entry cmd_new_session_entry = { .args = { "Ac:dDe:EF:f:n:Ps:t:x:Xy:", 0, -1, NULL }, .usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] " "[-f flags] [-n window-name] [-s session-name] " - CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", + CMD_TARGET_SESSION_USAGE " [-x width] [-y height] " + "[shell-command]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -283,7 +284,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) sc.tc = c; sc.name = args_get(args, 'n'); - args_vector(args, &sc.argc, &sc.argv); + args_to_vector(args, &sc.argc, &sc.argv); sc.idx = -1; sc.cwd = args_get(args, 'c'); diff --git a/cmd-new-window.c b/cmd-new-window.c index e88795c2..e7f0868f 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -40,7 +40,7 @@ const struct cmd_entry cmd_new_window_entry = { .args = { "abc:de:F:kn:PSt:", 0, -1, NULL }, .usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] " - "[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", + "[-n window-name] " CMD_TARGET_WINDOW_USAGE " [shell-command]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX }, @@ -105,7 +105,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) sc.tc = tc; sc.name = args_get(args, 'n'); - args_vector(args, &sc.argc, &sc.argv); + args_to_vector(args, &sc.argc, &sc.argv); sc.environ = environ_create(); av = args_first_value(args, 'e'); diff --git a/cmd-parse.y b/cmd-parse.y index a08c5819..2b5b7e0b 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -44,13 +44,15 @@ struct cmd_parse_scope { enum cmd_parse_argument_type { CMD_PARSE_STRING, - CMD_PARSE_COMMANDS + CMD_PARSE_COMMANDS, + CMD_PARSE_PARSED_COMMANDS }; struct cmd_parse_argument { enum cmd_parse_argument_type type; char *string; struct cmd_parse_commands *commands; + struct cmd_list *cmdlist; TAILQ_ENTRY(cmd_parse_argument) entry; }; @@ -608,6 +610,9 @@ cmd_parse_free_argument(struct cmd_parse_argument *arg) case CMD_PARSE_COMMANDS: cmd_parse_free_commands(arg->commands); break; + case CMD_PARSE_PARSED_COMMANDS: + cmd_list_free(arg->cmdlist); + break; } free(arg); } @@ -723,6 +728,11 @@ cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix) cmd_parse_log_commands(arg->commands, s); free(s); break; + case CMD_PARSE_PARSED_COMMANDS: + s = cmd_list_print(arg->cmdlist, 0); + log_debug("%s %u:%u: %s", prefix, i, j, s); + free(s); + break; } j++; } @@ -818,6 +828,11 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, values[count].type = ARGS_COMMANDS; values[count].cmdlist = pr->cmdlist; break; + case CMD_PARSE_PARSED_COMMANDS: + values[count].type = ARGS_COMMANDS; + values[count].cmdlist = arg->cmdlist; + values[count].cmdlist->references++; + break; } count++; } @@ -1023,39 +1038,19 @@ cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi) return (&pr); } -static void -cmd_parse_add_command(struct cmd_parse_commands *cmds, - struct cmd_parse_input *pi, int argc, char **argv) +struct cmd_parse_result * +cmd_parse_from_arguments(struct args_value *values, u_int count, + struct cmd_parse_input *pi) { + static struct cmd_parse_result pr; + struct cmd_parse_input input; + struct cmd_parse_commands *cmds; struct cmd_parse_command *cmd; struct cmd_parse_argument *arg; - int i; - - cmd_log_argv(argc, argv, "%s", __func__); - - cmd = xcalloc(1, sizeof *cmd); - cmd->line = pi->line; - - TAILQ_INIT(&cmd->arguments); - for (i = 0; i < argc; i++) { - arg = xcalloc(1, sizeof *arg); - arg->type = CMD_PARSE_STRING; - arg->string = xstrdup(argv[i]); - TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); - } - - TAILQ_INSERT_TAIL(cmds, cmd, entry); -} - -struct cmd_parse_result * -cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) -{ - static struct cmd_parse_result pr; - struct cmd_parse_input input; - struct cmd_parse_commands *cmds; - char **copy, **new_argv; - size_t size; - int i, last, new_argc; + u_int i; + char *copy; + size_t size; + int end; /* * The commands are already split up into arguments, so just separate @@ -1066,40 +1061,51 @@ cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) memset(&input, 0, sizeof input); pi = &input; } - cmd_log_argv(argc, argv, "%s", __func__); cmds = cmd_parse_new_commands(); - copy = cmd_copy_argv(argc, argv); - last = 0; - for (i = 0; i < argc; i++) { - size = strlen(copy[i]); - if (size == 0 || copy[i][size - 1] != ';') - continue; - copy[i][--size] = '\0'; - if (size > 0 && copy[i][size - 1] == '\\') { - copy[i][size - 1] = ';'; - continue; + cmd = xcalloc(1, sizeof *cmd); + cmd->line = pi->line; + TAILQ_INIT(&cmd->arguments); + + for (i = 0; i < count; i++) { + end = 0; + if (values[i].type == ARGS_STRING) { + copy = xstrdup(values[i].string); + size = strlen(copy); + if (size != 0 && copy[size - 1] == ';') { + copy[--size] = '\0'; + if (size > 0 && copy[size - 1] == '\\') + copy[size - 1] = ';'; + else + end = 1; + } + if (!end || size != 0) { + arg = xcalloc(1, sizeof *arg); + arg->type = CMD_PARSE_STRING; + arg->string = copy; + TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); + } + } else if (values[i].type == ARGS_COMMANDS) { + arg = xcalloc(1, sizeof *arg); + arg->type = CMD_PARSE_PARSED_COMMANDS; + arg->cmdlist = values[i].cmdlist; + arg->cmdlist->references++; + TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); + } else + fatalx("unknown argument type"); + if (end) { + TAILQ_INSERT_TAIL(cmds, cmd, entry); + cmd = xcalloc(1, sizeof *cmd); + cmd->line = pi->line; + TAILQ_INIT(&cmd->arguments); } - - new_argc = i - last; - new_argv = copy + last; - if (size != 0) - new_argc++; - - if (new_argc != 0) - cmd_parse_add_command(cmds, pi, new_argc, new_argv); - last = i + 1; } - if (last != argc) { - new_argv = copy + last; - new_argc = argc - last; + if (!TAILQ_EMPTY(&cmd->arguments)) + TAILQ_INSERT_TAIL(cmds, cmd, entry); + else + free(cmd); - if (new_argc != 0) - cmd_parse_add_command(cmds, pi, new_argc, new_argv); - } - - cmd_free_argv(argc, copy); cmd_parse_build_commands(cmds, pi, &pr); cmd_parse_free_commands(cmds); return (&pr); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 652ef755..6d60002e 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_respawn_pane_entry = { .args = { "c:e:kt:", 0, -1, NULL }, .usage = "[-k] [-c start-directory] [-e environment] " - CMD_TARGET_PANE_USAGE " [command]", + CMD_TARGET_PANE_USAGE " [shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -61,10 +61,8 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) sc.wl = wl; sc.wp0 = wp; - sc.lc = NULL; - sc.name = NULL; - args_vector(args, &sc.argc, &sc.argv); + args_to_vector(args, &sc.argc, &sc.argv); sc.environ = environ_create(); av = args_first_value(args, 'e'); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 4e6dc2a9..9a1a02c9 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_respawn_window_entry = { .args = { "c:e:kt:", 0, -1, NULL }, .usage = "[-k] [-c start-directory] [-e environment] " - CMD_TARGET_WINDOW_USAGE " [command]", + CMD_TARGET_WINDOW_USAGE " [shell-command]", .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -61,8 +61,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) sc.wl = wl; sc.tc = tc; - sc.name = NULL; - args_vector(args, &sc.argc, &sc.argv); + args_to_vector(args, &sc.argc, &sc.argv); sc.environ = environ_create(); av = args_first_value(args, 'e'); diff --git a/cmd-split-window.c b/cmd-split-window.c index 8b078db1..92f515ca 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -42,7 +42,8 @@ const struct cmd_entry cmd_split_window_entry = { .args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL }, .usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] " - "[-F format] [-l size] " CMD_TARGET_PANE_USAGE " [command]", + "[-F format] [-l size] " CMD_TARGET_PANE_USAGE + "[shell-command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -136,8 +137,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) sc.wp0 = wp; sc.lc = lc; - sc.name = NULL; - args_vector(args, &sc.argc, &sc.argv); + args_to_vector(args, &sc.argc, &sc.argv); sc.environ = environ_create(); av = args_first_value(args, 'e'); diff --git a/cmd.c b/cmd.c index 62c8b9ab..5647a861 100644 --- a/cmd.c +++ b/cmd.c @@ -544,6 +544,23 @@ cmd_free(struct cmd *cmd) free(cmd); } +/* Copy a command. */ +struct cmd * +cmd_copy(struct cmd *cmd, int argc, char **argv) +{ + struct cmd *new_cmd; + + new_cmd = xcalloc(1, sizeof *new_cmd); + new_cmd->entry = cmd->entry; + new_cmd->args = args_copy(cmd->args, argc, argv); + + if (cmd->file != NULL) + new_cmd->file = xstrdup(cmd->file); + new_cmd->line = cmd->line; + + return (new_cmd); +} + /* Get a command as a string. */ char * cmd_print(struct cmd *cmd) @@ -618,6 +635,37 @@ cmd_list_free(struct cmd_list *cmdlist) free(cmdlist); } +/* Copy a command list, expanding %s in arguments. */ +struct cmd_list * +cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv) +{ + struct cmd *cmd; + struct cmd_list *new_cmdlist; + struct cmd *new_cmd; + u_int group = cmdlist->group; + char *s; + + s = cmd_list_print(cmdlist, 0); + log_debug("%s: %s", __func__, s); + free(s); + + new_cmdlist = cmd_list_new(); + TAILQ_FOREACH(cmd, cmdlist->list, qentry) { + if (cmd->group != group) { + new_cmdlist->group = cmd_list_next_group++; + group = cmd->group; + } + new_cmd = cmd_copy(cmd, argc, argv); + cmd_list_append(new_cmdlist, new_cmd); + } + + s = cmd_list_print(new_cmdlist, 0); + log_debug("%s: %s", __func__, s); + free(s); + + return (new_cmdlist); +} + /* Get a command list as a string. */ char * cmd_list_print(struct cmd_list *cmdlist, int escaped) diff --git a/key-bindings.c b/key-bindings.c index 66e1ec9f..9f7e734a 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -29,7 +29,7 @@ " 'Previous' 'p' {switch-client -p}" \ " ''" \ " 'Renumber' 'N' {move-window -r}" \ - " 'Rename' 'n' {command-prompt -I \"#S\" \"rename-session -- '%%'\"}" \ + " 'Rename' 'n' {command-prompt -I \"#S\" {rename-session -- '%%'}}" \ " ''" \ " 'New Session' 's' {new-session}" \ " 'New Window' 'w' {new-window}" @@ -41,7 +41,7 @@ " 'Kill' 'X' {kill-window}" \ " 'Respawn' 'R' {respawn-window -k}" \ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ - " 'Rename' 'n' {command-prompt -FI \"#W\" \"rename-window -t#{window_id} -- '%%'\"}" \ + " 'Rename' 'n' {command-prompt -FI \"#W\" {rename-window -t '#{window_id}' -- '%%'}}" \ " ''" \ " 'New After' 'w' {new-window -a}" \ " 'New At End' 'W' {new-window}" @@ -350,16 +350,16 @@ key_bindings_init(void) "bind -N 'Break pane to a new window' ! { break-pane }", "bind -N 'Split window vertically' '\"' { split-window }", "bind -N 'List all paste buffers' '#' { list-buffers }", - "bind -N 'Rename current session' '$' { command-prompt -I'#S' \"rename-session -- '%%'\" }", + "bind -N 'Rename current session' '$' { command-prompt -I'#S' { rename-session -- '%%' } }", "bind -N 'Split window horizontally' % { split-window -h }", "bind -N 'Kill current window' & { confirm-before -p\"kill-window #W? (y/n)\" kill-window }", - "bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex \"select-window -t ':%%'\" }", + "bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex { select-window -t ':%%' } }", "bind -N 'Switch to previous client' ( { switch-client -p }", "bind -N 'Switch to next client' ) { switch-client -n }", - "bind -N 'Rename current window' , { command-prompt -I'#W' \"rename-window -- '%%'\" }", + "bind -N 'Rename current window' , { command-prompt -I'#W' { rename-window -- '%%' } }", "bind -N 'Delete the most recent paste buffer' - { delete-buffer }", - "bind -N 'Move the current window' . { command-prompt -T target \"move-window -t '%%'\" }", - "bind -N 'Describe key binding' '/' { command-prompt -kpkey 'list-keys -1N \"%%%\"' }", + "bind -N 'Move the current window' . { command-prompt -T target { move-window -t '%%' } }", + "bind -N 'Describe key binding' '/' { command-prompt -kpkey { list-keys -1N '%%' } }", "bind -N 'Select window 0' 0 { select-window -t:=0 }", "bind -N 'Select window 1' 1 { select-window -t:=1 }", "bind -N 'Select window 2' 2 { select-window -t:=2 }", @@ -382,7 +382,7 @@ key_bindings_init(void) "bind -N 'Paste the most recent paste buffer' ] { paste-buffer -p }", "bind -N 'Create a new window' c { new-window }", "bind -N 'Detach the current client' d { detach-client }", - "bind -N 'Search for a pane' f { command-prompt \"find-window -Z -- '%%'\" }", + "bind -N 'Search for a pane' f { command-prompt { find-window -Z -- '%%' } }", "bind -N 'Display window information' i { display-message }", "bind -N 'Select the previously current window' l { last-window }", "bind -N 'Toggle the marked pane' m { select-pane -m }", @@ -482,25 +482,25 @@ key_bindings_init(void) "bind -Tcopy-mode C-k { send -X copy-pipe-end-of-line-and-cancel }", "bind -Tcopy-mode C-n { send -X cursor-down }", "bind -Tcopy-mode C-p { send -X cursor-up }", - "bind -Tcopy-mode C-r { command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"' }", - "bind -Tcopy-mode C-s { command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"' }", + "bind -Tcopy-mode C-r { command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental '%%' } }", + "bind -Tcopy-mode C-s { command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental '%%' } }", "bind -Tcopy-mode C-v { send -X page-down }", "bind -Tcopy-mode C-w { send -X copy-pipe-and-cancel }", "bind -Tcopy-mode Escape { send -X cancel }", "bind -Tcopy-mode Space { send -X page-down }", "bind -Tcopy-mode , { send -X jump-reverse }", "bind -Tcopy-mode \\; { send -X jump-again }", - "bind -Tcopy-mode F { command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"' }", + "bind -Tcopy-mode F { command-prompt -1p'(jump backward)' { send -X jump-backward '%%' } }", "bind -Tcopy-mode N { send -X search-reverse }", "bind -Tcopy-mode R { send -X rectangle-toggle }", - "bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"' }", + "bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward '%%' } }", "bind -Tcopy-mode X { send -X set-mark }", - "bind -Tcopy-mode f { command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"' }", - "bind -Tcopy-mode g { command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"' }", + "bind -Tcopy-mode f { command-prompt -1p'(jump forward)' { send -X jump-forward '%%' } }", + "bind -Tcopy-mode g { command-prompt -p'(goto line)' { send -X goto-line '%%' } }", "bind -Tcopy-mode n { send -X search-again }", "bind -Tcopy-mode q { send -X cancel }", "bind -Tcopy-mode r { send -X refresh-from-pane }", - "bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"' }", + "bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward '%%' } }", "bind -Tcopy-mode Home { send -X start-of-line }", "bind -Tcopy-mode End { send -X end-of-line }", "bind -Tcopy-mode MouseDown1Pane select-pane", @@ -516,15 +516,15 @@ key_bindings_init(void) "bind -Tcopy-mode Down { send -X cursor-down }", "bind -Tcopy-mode Left { send -X cursor-left }", "bind -Tcopy-mode Right { send -X cursor-right }", - "bind -Tcopy-mode M-1 { command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"' }", - "bind -Tcopy-mode M-2 { command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"' }", - "bind -Tcopy-mode M-3 { command-prompt -Np'(repeat)' -I3 'send -N \"%%%\"' }", - "bind -Tcopy-mode M-4 { command-prompt -Np'(repeat)' -I4 'send -N \"%%%\"' }", - "bind -Tcopy-mode M-5 { command-prompt -Np'(repeat)' -I5 'send -N \"%%%\"' }", - "bind -Tcopy-mode M-6 { command-prompt -Np'(repeat)' -I6 'send -N \"%%%\"' }", - "bind -Tcopy-mode M-7 { command-prompt -Np'(repeat)' -I7 'send -N \"%%%\"' }", - "bind -Tcopy-mode M-8 { command-prompt -Np'(repeat)' -I8 'send -N \"%%%\"' }", - "bind -Tcopy-mode M-9 { command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"' }", + "bind -Tcopy-mode M-1 { command-prompt -Np'(repeat)' -I1 { send -N '%%' } }", + "bind -Tcopy-mode M-2 { command-prompt -Np'(repeat)' -I2 { send -N '%%' } }", + "bind -Tcopy-mode M-3 { command-prompt -Np'(repeat)' -I3 { send -N '%%' } }", + "bind -Tcopy-mode M-4 { command-prompt -Np'(repeat)' -I4 { send -N '%%' } }", + "bind -Tcopy-mode M-5 { command-prompt -Np'(repeat)' -I5 { send -N '%%' } }", + "bind -Tcopy-mode M-6 { command-prompt -Np'(repeat)' -I6 { send -N '%%' } }", + "bind -Tcopy-mode M-7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }", + "bind -Tcopy-mode M-8 { command-prompt -Np'(repeat)' -I8 { send -N '%%' } }", + "bind -Tcopy-mode M-9 { command-prompt -Np'(repeat)' -I9 { send -N '%%' } }", "bind -Tcopy-mode M-< { send -X history-top }", "bind -Tcopy-mode M-> { send -X history-bottom }", "bind -Tcopy-mode M-R { send -X top-line }", @@ -562,25 +562,25 @@ key_bindings_init(void) "bind -Tcopy-mode-vi Space { send -X begin-selection }", "bind -Tcopy-mode-vi '$' { send -X end-of-line }", "bind -Tcopy-mode-vi , { send -X jump-reverse }", - "bind -Tcopy-mode-vi / { command-prompt -T search -p'(search down)' 'send -X search-forward \"%%%\"' }", + "bind -Tcopy-mode-vi / { command-prompt -T search -p'(search down)' { send -X search-forward '%%' } }", "bind -Tcopy-mode-vi 0 { send -X start-of-line }", - "bind -Tcopy-mode-vi 1 { command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"' }", - "bind -Tcopy-mode-vi 2 { command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"' }", - "bind -Tcopy-mode-vi 3 { command-prompt -Np'(repeat)' -I3 'send -N \"%%%\"' }", - "bind -Tcopy-mode-vi 4 { command-prompt -Np'(repeat)' -I4 'send -N \"%%%\"' }", - "bind -Tcopy-mode-vi 5 { command-prompt -Np'(repeat)' -I5 'send -N \"%%%\"' }", - "bind -Tcopy-mode-vi 6 { command-prompt -Np'(repeat)' -I6 'send -N \"%%%\"' }", - "bind -Tcopy-mode-vi 7 { command-prompt -Np'(repeat)' -I7 'send -N \"%%%\"' }", - "bind -Tcopy-mode-vi 8 { command-prompt -Np'(repeat)' -I8 'send -N \"%%%\"' }", - "bind -Tcopy-mode-vi 9 { command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"' }", - "bind -Tcopy-mode-vi : { command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"' }", + "bind -Tcopy-mode-vi 1 { command-prompt -Np'(repeat)' -I1 { send -N '%%' } }", + "bind -Tcopy-mode-vi 2 { command-prompt -Np'(repeat)' -I2 { send -N '%%' } }", + "bind -Tcopy-mode-vi 3 { command-prompt -Np'(repeat)' -I3 { send -N '%%' } }", + "bind -Tcopy-mode-vi 4 { command-prompt -Np'(repeat)' -I4 { send -N '%%' } }", + "bind -Tcopy-mode-vi 5 { command-prompt -Np'(repeat)' -I5 { send -N '%%' } }", + "bind -Tcopy-mode-vi 6 { command-prompt -Np'(repeat)' -I6 { send -N '%%' } }", + "bind -Tcopy-mode-vi 7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }", + "bind -Tcopy-mode-vi 8 { command-prompt -Np'(repeat)' -I8 { send -N '%%' } }", + "bind -Tcopy-mode-vi 9 { command-prompt -Np'(repeat)' -I9 { send -N '%%' } }", + "bind -Tcopy-mode-vi : { command-prompt -p'(goto line)' { send -X goto-line '%%' } }", "bind -Tcopy-mode-vi \\; { send -X jump-again }", - "bind -Tcopy-mode-vi ? { command-prompt -T search -p'(search up)' 'send -X search-backward \"%%%\"' }", + "bind -Tcopy-mode-vi ? { command-prompt -T search -p'(search up)' { send -X search-backward '%%' } }", "bind -Tcopy-mode-vi A { send -X append-selection-and-cancel }", "bind -Tcopy-mode-vi B { send -X previous-space }", "bind -Tcopy-mode-vi D { send -X copy-pipe-end-of-line-and-cancel }", "bind -Tcopy-mode-vi E { send -X next-space-end }", - "bind -Tcopy-mode-vi F { command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"' }", + "bind -Tcopy-mode-vi F { command-prompt -1p'(jump backward)' { send -X jump-backward '%%' } }", "bind -Tcopy-mode-vi G { send -X history-bottom }", "bind -Tcopy-mode-vi H { send -X top-line }", "bind -Tcopy-mode-vi J { send -X scroll-down }", @@ -588,14 +588,14 @@ key_bindings_init(void) "bind -Tcopy-mode-vi L { send -X bottom-line }", "bind -Tcopy-mode-vi M { send -X middle-line }", "bind -Tcopy-mode-vi N { send -X search-reverse }", - "bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"' }", + "bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward '%%' } }", "bind -Tcopy-mode-vi V { send -X select-line }", "bind -Tcopy-mode-vi W { send -X next-space }", "bind -Tcopy-mode-vi X { send -X set-mark }", "bind -Tcopy-mode-vi ^ { send -X back-to-indentation }", "bind -Tcopy-mode-vi b { send -X previous-word }", "bind -Tcopy-mode-vi e { send -X next-word-end }", - "bind -Tcopy-mode-vi f { command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"' }", + "bind -Tcopy-mode-vi f { command-prompt -1p'(jump forward)' { send -X jump-forward '%%' } }", "bind -Tcopy-mode-vi g { send -X history-top }", "bind -Tcopy-mode-vi h { send -X cursor-left }", "bind -Tcopy-mode-vi j { send -X cursor-down }", @@ -605,7 +605,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi o { send -X other-end }", "bind -Tcopy-mode-vi q { send -X cancel }", "bind -Tcopy-mode-vi r { send -X refresh-from-pane }", - "bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"' }", + "bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward '%%' } }", "bind -Tcopy-mode-vi v { send -X rectangle-toggle }", "bind -Tcopy-mode-vi w { send -X next-word }", "bind -Tcopy-mode-vi '{' { send -X previous-paragraph }", diff --git a/server-client.c b/server-client.c index 062e72d4..a7cad0a5 100644 --- a/server-client.c +++ b/server-client.c @@ -2124,6 +2124,7 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) int argc; char **argv, *cause; struct cmd_parse_result *pr; + struct args_value *values; if (c->flags & CLIENT_EXIT) return; @@ -2149,7 +2150,8 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) *argv = xstrdup("new-session"); } - pr = cmd_parse_from_arguments(argc, argv, NULL); + values = args_from_vector(argc, argv); + pr = cmd_parse_from_arguments(values, argc, NULL); switch (pr->status) { case CMD_PARSE_ERROR: cause = pr->error; @@ -2157,6 +2159,8 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) case CMD_PARSE_SUCCESS: break; } + args_free_values(values, argc); + free(values); cmd_free_argv(argc, argv); cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL)); diff --git a/tmux.h b/tmux.h index 98c3b9a3..ca67fd03 100644 --- a/tmux.h +++ b/tmux.h @@ -2217,8 +2217,11 @@ void args_set(struct args *, u_char, struct args_value *); struct args *args_create(void); struct args *args_parse(const struct args_parse *, struct args_value *, u_int, char **); -void args_vector(struct args *, int *, char ***); +struct args *args_copy(struct args *, int, char **); +void args_to_vector(struct args *, int *, char ***); +struct args_value *args_from_vector(int, char **); void args_free_value(struct args_value *); +void args_free_values(struct args_value *, u_int); void args_free(struct args *); char *args_print(struct args *); char *args_escape(const char *); @@ -2227,6 +2230,7 @@ const char *args_get(struct args *, u_char); u_char args_first(struct args *, struct args_entry **); u_char args_next(struct args_entry **); u_int args_count(struct args *); +struct args_value *args_values(struct args *); struct args_value *args_value(struct args *, u_int); const char *args_string(struct args *, u_int); struct cmd_list *args_make_commands_now(struct cmd *, struct cmdq_item *, @@ -2291,9 +2295,11 @@ u_int cmd_get_group(struct cmd *); void cmd_get_source(struct cmd *, const char **, u_int *); struct cmd *cmd_parse(struct args_value *, u_int, const char *, u_int, char **); +struct cmd *cmd_copy(struct cmd *, int, char **); void cmd_free(struct cmd *); char *cmd_print(struct cmd *); struct cmd_list *cmd_list_new(void); +struct cmd_list *cmd_list_copy(struct cmd_list *, int, char **); void cmd_list_append(struct cmd_list *, struct cmd *); void cmd_list_append_all(struct cmd_list *, struct cmd_list *); void cmd_list_move(struct cmd_list *, struct cmd_list *); @@ -2327,7 +2333,7 @@ enum cmd_parse_status cmd_parse_and_append(const char *, struct cmdq_state *, char **); struct cmd_parse_result *cmd_parse_from_buffer(const void *, size_t, struct cmd_parse_input *); -struct cmd_parse_result *cmd_parse_from_arguments(int, char **, +struct cmd_parse_result *cmd_parse_from_arguments(struct args_value *, u_int, struct cmd_parse_input *); /* cmd-queue.c */ From 388f0fe973869baeb06982ecaaf2a263c6f8fd2c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 25 Aug 2021 15:56:25 +0100 Subject: [PATCH 0973/1006] Update CHANGES. --- CHANGES | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 38c888c1..40ecee72 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,67 @@ CHANGES FROM 3.2a TO 3.3 +* Disable evports on SunOS since they are broken. + +* Do not expand the file given with tmux -f so it can contain :s. + +* Bump FORMAT_LOOOP_LIMIT and add a log message when hit. + +* Add a terminal feature for the mouse (since FreeBSD termcap does not have kmous). + +* Forbid empty session names. + +* Improve error reporting when the tmux /tmp directory cannot be created or + used. + +* Give #() commands a one second grace period where the output is empty before + telling the user they aren't doing anything ("not ready"). + +* When building, pick default-terminal from the first of tmux-256color, tmux, + screen-256color, screen that is available on the build system. + +* Do not close popups on resize, instead adjust them to fit. + +* Add a client-active hook. + +* Make window-linked and window-unlinked window options. + +* Do not configure on macOS without the user making a choice about utf8proc + (either --enable-utf8proc or --disable-utf8proc). + +* Do not freeze output in panes when a popup is open, let them continue to + redraw. + +* Add pipe variants of the line copy commands. + +* Change copy-line and copy-end-of-line not to cancel and add -and-cancel + variants, like the other copy commands. + +* Support the OSC palette-setting sequences in popups. + +* Add a pane-colours array option to specify the defaults palette. + +* Add support for Unicode zero-width joiner. + +* Make newline a style delimiter as well so they can cross multiple lines for + readability in configuration files. + +* Change focus to be driven by events rather than scanning panes so the + ordering of in and out is consistent. + +* Add display-popup -B to open a popup without a border. + +* Add a menu for popups that can be opened with button 3 outside the popup or + on the left or top border. Resizing now only works on the right and bottom + borders or when using Meta. The menu allows a popup to be closed, expanded to + the full size of the client, centered in the client or changed into a pane. + +* Make command-prompt and confirm-before block by default (like run-shell). A + new -b flags runs them in the background as before. Also set return code for + confirm-before. + +* Parse {} as actual commands immediately, this means the contents of {} has to + be valid tmux commands as well as matching the syntax. + * Change cursor style handling so tmux understands which sequences contain blinking and sets the flag appropriately, means that it works whether cnorm disables blinking or not. This now matches xterm's behaviour. @@ -11,7 +73,7 @@ CHANGES FROM 3.2a TO 3.3 * Add -F for command-prompt and use it to fix "Rename" on the window menu. * Add different command histories for different types of prompts ("command", - "search" etc). From Anindya Mukherjee. + "search" etc). CHANGES FROM 3.2 TO 3.2a From d62aee506b9b9868b806319502f2b753cafe96ef Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 30 Aug 2021 08:24:43 +0100 Subject: [PATCH 0974/1006] Regress conf from https://raw.githubusercontent.com/lacygoill/config/master/.config/tmux/tmux.conf. --- .../01840240e807e837dbf76d85b4b938de.conf | 1496 +++++++++++++++++ 1 file changed, 1496 insertions(+) create mode 100644 regress/conf/01840240e807e837dbf76d85b4b938de.conf diff --git a/regress/conf/01840240e807e837dbf76d85b4b938de.conf b/regress/conf/01840240e807e837dbf76d85b4b938de.conf new file mode 100644 index 00000000..dcd4899e --- /dev/null +++ b/regress/conf/01840240e807e837dbf76d85b4b938de.conf @@ -0,0 +1,1496 @@ +# Options {{{1 +# Server {{{2 + +# Don't pile up more than 10 buffers (down from 50 by default).{{{ +# +# Rationale: Keeping a tidy stack, with relevant information, could help us +# integrate tmux buffers in our workflow more often. +# +# However, maybe we could keep a big stack of buffers, and filter them by +# pressing `f` in the window opened by `choose-buffer`. +# Alternatively, we could also try to use fzf to fuzzy search through their +# contents... +#}}} +set -s buffer-limit 10 + + +# What does this option control?{{{ +# +# It sets the time in milliseconds for which tmux waits after an escape is input +# to determine if it is part of a function or meta key sequences. +# The default is 500 millisec onds. +#}}} +# Why do you reset it?{{{ +# +# The default value introduces lag when we use Vim and escape from insert to +# normal mode. We want to reduce the timeout. +#}}} +# Why don't you set it to 0 ?{{{ +# +# > Some people set it to zero but I consider that risky if you're connecting +# > over a wide-area network or there is anything else that might insert small +# > delays between the delivery of chars in such a sequence. +# +# Source: https://github.com/tmux/tmux/issues/353#issuecomment-294570322 +# +# Basically, we should still let a few ms to be sure all the keys in a control +# sequence will have enough time to reach tmux. +#}}} +set -s escape-time 10 + +# If the terminal supports focus events, they will be requested by the tmux +# client and passed through to the tmux server, then to the programs it runs. +# Necessary to be able to listen to `FocusGained` and `FocusLost`. +# Also necessary for `pane-focus-[in|out]` hooks. +set -s focus-events on + +# history of tmux commands (pfx :) +set -s history-file "$HOME/.tmux/command_history" + +# What does this do?{{{ +# +# It makes tmux sometimes send an OSC 52 sequence – which sets the terminal +# clipboard content – if there is an `Ms` entry in the terminfo description of +# the outer terminal. +#}}} +# What are the possible values of this option?{{{ +# +# - `on` +# - `external` +# - `off` +# +# --- +# +# There are 3 ways to create a tmux buffer: +# +# 1. invoke the `set-buffer` or `load-buffer` tmux commands +# 2. copy text in copy mode (`send -X copy-selection`, `copy-pipe`, ...) +# 3. send an OSC 52 sequence from an application inside tmux (e.g. `$ printf ...`) +# +# `1.` always creates a tmux buffer; never sets the X clipboard. +# `2.` always creates a tmux buffer; sets the X clipboard via OSC 52 iff `set-clipboard` is not `off`. +# `3.` creates a tmux buffer *and* sets the X clipboard via OSC 52 iff `set-clipboard` is `on`. +# +# IOW, `external` makes tmux *automatically* set the X clipboard when you yank +# sth in copy mode via OSC 52, while `on` does the same, but also allows an +# application to *manually* set a tmux buffer/the X clipboard via a OSC 52. +# +# Source: https://unix.stackexchange.com/a/560464/289772 +#}}} +# What is the possible drawback of the value `on`?{{{ +# +# https://github.com/tmux/tmux/wiki/Clipboard#security-concerns +#}}} +# How to disable OSC 52 for some terminals, but not all?{{{ +# +# # or use 'external' +# set -s set-clipboard on +# set -as terminal-overrides 'yourTERMname:Ms@' +# ^ +# man terminfo /Similar Terminals/;/canceled +#}}} +set -s set-clipboard external + +# Why do you move your 'terminal-overrides' settings in another file?{{{ +# +# It makes it easier to source the settings only when the tmux server is started; +# not when we manually re-source `tmux.conf`. +#}}} +# Why do you need them to be sourced only once?{{{ +# +# We *append* strings to the value of the 'terminal-overrides' option. +# I don't want to append the same strings again and again every time I re-source +# `tmux.conf`. +#}}} +# Why don't you simply reset the option before appending a value?{{{ +# +# That would make us lose the default value of the option: +# +# terminal-overrides[0] "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007:Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007:Ss=\\E[%p1%d q:Se=\\E[2 q" +# terminal-overrides[1] screen*:XT +# +# --- +# +# Besides, one of the value we append to 'terminal-overrides' depends on the value of `$TERM`. +# +# if '[ "$TERM" != "st-256color" ]' 'set -as terminal-overrides ",*:Cr=\\E]112\\007"' +# ^----------------------^ +# +# And the value of `$TERM` will be correct only the first time we source `tmux.conf`. +#}}} + +# What's the value of `$TERM` here?{{{ +# +# The first time our `tmux.conf` is sourced, it matches the `$TERM` of the +# outer terminal; the next times, it's the value of 'default-terminal' (i.e. +# 'tmux-256color'). +#}}} +# What's the meaning of this `if` guard?{{{ +# +# The condition `[ "$TERM" != "tmux-256color" ]` is true only the first time our +# `tmux.conf` is sourced. +# So, this part of the guard means: “if this is the first time the file is sourced”. +# This is equivalent to `has('vim_starting')` in Vim. +# +# The condition `[ -n "$DISPLAY" ]` is true only in a GUI environment. +# So, this part of the guard means: “don't source the file if we are in a console”. +# Indeed, I doubt a linux console is able to understand any sequence we might +# want to use. +#}}} +# Could we use `%if` instead of `if`?{{{ +# +# You could try this: +# +# %if "#{!=:$TERM,#{default-terminal}}" +# source-file "$HOME/.config/tmux/terminal-overrides.conf" +# %endif +# +# But it doesn't seem to work; the guard would not prevent the file from being re-sourced. +# I think that's because, in this case, `$TERM` refers to the value in the +# environment of the tmux server process; and for the latter, `TERM` always +# matches the outer terminal. +#}}} +if '[ "$TERM" != "#{default-terminal}" -a -n "$DISPLAY" ]' { source "$HOME/.config/tmux/terminal-overrides.conf" } + +# Leave `default-terminal` at the end.{{{ +# +# In my limited testing, moving it above would not cause an issue, but better be +# safe than sorry. +# In particular, I want to be sure that the value of `$TERM` is not +# 'tmux-256color' the first time our `tmux.conf` is sourced; otherwise +# `terminal-overrides.conf` would never be sourced. +#}}} +# Why `-s` instead of `-g`? {{{ +# +# Since tmux 2.1, `default-terminal` is a server option, not a session option. +# +# > As a side effect this changes default-terminal to be a server rather than a +# > session option. +# +# https://github.com/tmux/tmux/commit/7382ba82c5b366be84ca55c7842426bcf3d1f521 +# Confirmed by the fact that `default-terminal` is described in the section of +# the server options in the man page. +# Also confirmed by the fact that it's listed in the output of: +# +# tmux show -s +# +# However, according to nicm: +# +# > You do not have to use -s or -w for set-option except for user options. +# > tmux can work it out from the option name. +# > For show-option you do need it. +# +# So, we could omit `-s`, but I prefer to be explicit. +#}}} +# Why not let tmux use the default value `screen` (for `$TERM`)?{{{ +# +# By default, most terminals set `$TERM` to `xterm` because the `xterm` entry is +# present and set in the terminfo db of most machines. +# tmux sets it to `screen`, again, because it's a popular entry (more than the +# `tmux` one). +# The `xterm`/`screen` value implies that the terminal will declare supporting +# only 8 colors; confirmed by `$ tput colors`. +# +# Because of this, the theme of some programs might be off (including Vim and +# the terminal itself). We want the terminal to declare it supports 256 colors, +# which anyway is usually true. +#}}} +# Do we need `$TERM` to contain `tmux`?{{{ +# +# Yes. To support italics: +# +# The `screen-256color` entry in the terminfo db doesn't have a `sitm` field. +# IOW, the db reports that screen is unable to support italics, which is true. +# So, if we set `$TERM` to `screen-256color`, when an application will want to +# make some text appear italicized, it will think it's not possible. +# But it *is* possible, because we use tmux, not screen. And tmux *does* +# support the italics style. +# The solution is to set `$TERM` to `tmux-256color` so that when an application +# queries the terminfo db, it finds the field `sitm` with the right value +# `\E[3m`. +# +# See also: +# https://github.com/tmux/tmux/wiki/FAQ#i-dont-see-italics-or-italics-and-reverse-are-the-wrong-way-round +# https://github.com/tmux/tmux/issues/175#issuecomment-152719805 +#}}} +# `256color`?{{{ +# +# For a Vim color scheme to be correctly applied, no. +# Because it seems that our current theme automatically sets the number of +# colors to 256: +# +# :runtime colors/seoul256.vim +# :echo &t_Co +# +# But, for the color schemes of other programs, maybe. +#}}} +set -s default-terminal tmux-256color + +# Session {{{2 + +# Don't ring the bell in the current window. +set -g bell-action other + +# Why?{{{ +# +# If a new window is created without any command to execute, tmux reads the +# session option `default-command` to find one. +# By default, its value is an empty string which instructs tmux to create a +# *login* shell using the value of the default-shell option. +# The default value of the latter is `$SHELL` (atm: `/usr/local/bin/zsh`). +# +# When we create a new window, we want a *non*-login shell, because a login zsh +# shell sources `~/.zprofile`, which we use to execute code specific to a +# virtual *console* (set the background to white in the console). +# This code is not suited to a virtual *terminal*. +# +# More generally, we don't want a *non*-login shell to source login files +# (`.profile`, `.zprofile`, `.zlogin`). +# +# So, we give the value zsh to `default-command` to prevent tmux from starting a +# login shell. +#}}} +set -g default-command "$SHELL" + +# Don't detach the client when the current session is killed. +set -g detach-on-destroy off + +# display status line messages and other on-screen indicators for 1s +# (or until a key is pressed) +set -g display-time 1000 +# display the indicators shown by the display-panes command for 5s +set -g display-panes-time 5000 + +# increase scrollback buffer (2000 → 50000) +# +# `history-limit` has nothing to do with the history of executed tmux commands. +# It controls the amount of lines you can scroll back when you enter copy mode. +set -g history-limit 50000 + +# Index options +# When we create a new window, tmux looks for an unused index, starting from 0.{{{ +# +# I prefer 1, because: +# +# - I usually count from 1, not 0 +# - this lets us run `:movew -t :0` to move the current window in first position +# +# Note that you can't run `:movew -t :1` to move the window in first position, +# because 1 is already taken by the first window. +# `:movew` expects an index which is “free” (i.e. not used by any existing window). +# +# Also, note that when running `:movew -t :0`, tmux renumbers all windows +# automatically from whatever value is assigned to the 'base-index' option. +#}}} +set -g base-index 1 +# │ +# └ must be applied globally to all sessions +# +# same thing for the panes +set -gw pane-base-index 1 +# ││ +# │└ window option +# └ must be applied globally to all windows + +# make tmux capture the mouse and allow mouse events to be bound as key bindings +set -g mouse on + +# use `M-space` as a prefix +set -g prefix M-space + +# renumber windows, when:{{{ +# +# - we destroy one of them +# - we move the first window (index 1) at the end (index 99), by running `movew -t :99` +# +# to prevent holes in the sequence of indexes +#}}} +set -g renumber-windows on + +# time for repeating of a hotkey bound using the -r flag without having to type the prefix again; default: 500 +set -g repeat-time 1000 + +# Some consoles don't like attempts to set the window title. +# This might cause tmux to freeze the terminal when you attach to a session. +# https://github.com/tmux/tmux/wiki/FAQ#tmux-freezes-my-terminal-when-i-attach-to-a-session-i-have-to-kill--9-the-shell-it-was-started-from-to-recover +set -g set-titles off + +# update the status line every 5 seconds (instead of 15s by default) +set -g status-interval 5 + +# emacs key bindings in tmux command prompt (prefix + :) are better than vi keys, +# even for vim users. +set -g status-keys emacs + +# color of status line +set -g -F status-style "bg=#{?#{==:$DISPLAY,},blue,colour138}" + +# Center the position of the window list component of the status line +set -g status-justify centre + +# cache the number of cpu cores in `~/.ncore`, which a shell command in 'status-right' is going to need +if '[ ! -s "${HOME}/.ncore" ]' \ + { run "lscpu | awk '/^CPU\\(s\\):\\s*[0-9]/ { print $2 }' >\"${HOME}/.ncore\"" } + +# set the contents of the status line +# What's `#[...]`?{{{ +# +# It lets you embed some styles. +# If you want to apply the same style all over the left part or the right part +# of the status line, you can also use `status-left-style` or `status-right-style`: +# +# set -g status-left '#[fg=colour15,bold] #S' +# ⇔ +# set -g status-left ' #S' +# set -g status-left-style '#[fg=colour15,bold]' +# +# However, I prefer embedding the styles inside the value of `status-left` and +# `status-right`, because: +# +# - it's more concise +# - it's more powerful: you can set the style of an arbitrary *portion* of the status line +# +# --- +# +# Note that you can use this syntax only in the value of an option which sets +# the *contents* of sth, not its style. +# So, this is *not* a valid syntax: +# +# # ✘ +# set -g status-left-style '#[fg=colour15,bold]' +# +# Here, you must get rid of `#[...]`: +# +# # ✔ +# set -g status-left-style 'fg=colour15,bold' +#}}} +# What's `#{?...}`?{{{ +# +# A conditional: +# +# #{?test,val1,val2} +# +# For example: +# +# {?client_prefix,#[bold],} +# +# This will be evaluated into the style `bold` if the prefix has been pressed, +# or nothing otherwise. +#}}} +# Why do you use `nobold`?{{{ +# +# We set the style `bold` for some part of the status line. +# But a style applies to *all* the remaining text in the status line. +# We need `nobold` to reset the style. +#}}} +# How can I include the time of the day or the hour in the status line?{{{ +# +# Use `date(1)` and `%` items: +# +# %a = day of week +# %d = day of month +# %b = month +# %R = hour +# +# See `man date`. +#}}} +set -g status-left ' #[fg=colour7]#{?client_prefix,#[fg=colour0],}#S#[fg=colour7]' + +# Which alternative could I use to get the cpu load?{{{ +# +# $ uptime|awk '{split(substr($0, index($0, "load")), a, ":"); print a[2]}' +# +# https://github.com/tmux/tmux/wiki/FAQ#what-is-the-best-way-to-display-the-load-average-why-no-l +#}}} +# Why don't you write the code in a script and invoke it with `#(my-script.sh)`?{{{ +# +# A script needs another shell to be interpreted. +# The latter adds overhead, which would almost double the time the code needs to +# be run. +# +# Check this: +# +# $ cat <<'EOF' >/tmp/sh.sh +# awk 'BEGIN { getline load <"/proc/loadavg"; getline ncore <(ENVIRON["HOME"]"/.ncore"); printf("%d", 100 * load / ncore) }' +# EOF +# $ chmod +x /tmp/sh.sh +# +# $ time zsh -c 'repeat 100 /tmp/sh.sh' +# ... 0,420 total˜ +# $ time zsh -c 'repeat 100 awk '\''BEGIN { getline load <"/proc/loadavg"; getline ncore <(ENVIRON["HOME"]"/.ncore"); printf("%d", 100 * load / ncore) }'\''' +# ... 0,193 total˜ +#}}} +# Why do you double the percent sign in `printf("%%d", ...)`?{{{ +# +# The code is going to be expanded inside the value of 'status-right'. +# And the latter is always passed to `strftime(3)` for which the percent sign +# has a special meaning. +# As a result, if you don't double the percent sign – to make it literal – `%d` +# will be replaced with the current day of the month (01, 02, ..., 31). +# +# From `man tmux /status-right`: +# +# > As with status-left, string will be passed to strftime(3) and character +# > pairs are replaced. +#}}} +cpu='awk '\''BEGIN { \ + getline load <"/proc/loadavg"; \ + getline ncore <(ENVIRON["HOME"]"/.ncore"); \ + printf("%%d", 100 * load / ncore) }'\''' + +mem='free | awk '\''/Mem/ { total = $2; used = $3 }; \ + END { printf("%%d", 100 * used / total) }'\''' +# TODO: Maybe we could get rid of `free(1)`, by inspecting `/proc/meminfo`. +# However, I can find the second field ("total") of `free(1)` in this file +# (first line: "MemTotal"), but not the third one ("used"). +# +# Update: From `man free`: +# +# used Used memory (calculated as total - free - buffers - cache) +# +# You would have to read 4 files to compute the `used` field. +# Make some tests (with `time(1)`) to see whether the resulting `awk(1)` command +# is slower than what we currently run. + +# TODO: Color the numbers in red if they exceed some threshold. +# Try to capture the output of the 2 shell commands in tmux variables, so that +# we can test them in a conditional. +set -g status-right '#[fg=colour235]M #[fg=colour15,bold]' +set -ga status-right "#($mem) " +set -ga status-right '#[fg=colour235,nobold]C #[fg=colour15,bold]' +set -ga status-right "#($cpu) " + +setenv -gu cpu +setenv -gu mem + +# Why do you want `COLORTERM` to be automatically updated?{{{ +# +# It can be useful to detect a terminal which lies about its identity. +# E.g., xfce4-terminal advertises itself as `xterm-256color`, but the value of +# its `COLORTERM` variable is 'xfce4-terminal'. +# +# So, the more reliable `COLORTERM` is, the better we can detect that we're in +# xfce4-terminal, and react appropriately. +# This can be useful, for example, to prevent vim-term from sending `CSI 2 SPC q` +# when we're leaving Vim from xfce4-terminal on Ubuntu 16.04. +# The latter doesn't understand this sequence, and once sent to the terminal, +# tmux will regularly reprint the sequence wherever our cursor is. +#}}} +# Why could I be tempted to run the same command for `LESSOPEN`, `LESSCLOSE`, `LS_COLORS`?{{{ +# +# setenv -gu LESSOPEN +# setenv -gu LESSCLOSE +# setenv -gu LS_COLORS +# +# These environment variables are set in `~/.zshenv`, but only on the condition +# they've not been set yet. +# The purpose of the condition is to make the shell quicker to start. +# Indeed, setting these variables adds around 8ms to the shell's startup time. +# However, if they are set in the tmux global environment, then they'll never be +# reset when we start a new shell, because the condition will never be +# satisfied. +# +# This means that we can't change the value of these variables by simply editing +# `~/.zshenv`, which can be an issue. +#}}} +# Why don't you do it?{{{ +# +# Because it would add around 8ms to the startup time of every shell we ask tmux +# to open. +#}}} +#  Isn't this an issue?{{{ +# +# No. +# If you want to modify one of these variables, and if you want the change to be +# applied immediately without restarting the tmux server, do it in the context +# of the tmux global environment: +# +# $ tmux setenv -g LESSOPEN new_value +#}}} +if '[ "$TERM" != "#{default-terminal}" ]' { set -ga update-environment COLORTERM } +# TODO: We have several similar `if` conditions. Maybe we should write only one, +# and put inside all the commands we want to run. +# This would make tmux start a little faster. + +# display a message when activity is detected in a window +# Why?{{{ +# +# We haven't customized the status line to include an indicator when some +# activity is detected in a window, because by default we don't monitor activity +# ('monitor-activity' is off). +# Indeed, generally, most windows will produce some output and have some activity. +# Seeing a lot of indicators in the status line, all the time, is meaningless. +# +# But we do have a key binding to temporarily toggle 'monitor-activity' in the +# current window; so, we need a way to be notified when some activity is later +# detected in it. +#}}} +set -g visual-activity on + +# Window {{{2 + +# Use vi key bindings in copy mode. +set -gw mode-keys vi + +# colors of *borders* of focused and non-focused panes +set -gw pane-active-border-style 'fg=colour138,bg=#cacaca' +set -gw pane-border-style 'fg=colour138,bg=#cacaca' + +# How to insert the index of a window?{{{ +# +# Use the alias `#I`. +#}}} +# How to insert its flags, like `Z` for a zoomed window?{{{ +# +# Use the alias `#F`. +#}}} +# Set what to display for the current window (then for the other ones), and how, +# in the status line window list. +# See: https://github.com/tmux/tmux/issues/74#issuecomment-129130023 +set -gw window-status-current-format '#[fg=colour232,bg=colour253]#W#F#{?window_zoomed_flag, #[fg=blue]#P/#{window_panes},}' +set -gw window-status-format '#[fg=colour232#,bg=colour248]#W#F#[bg=default]' + +# Pane {{{2 + +# colors of focused and non-focused panes +# Because we use `-gw`, the colors will affect any pane.{{{ +# +# But, at runtime, you could use `-p` to set the color of a given pane when it's +# (un)focused differently. +#}}} +set -gw window-active-style 'bg=#dbd6d1' +set -gw window-style 'bg=#cacaca' +# }}}1 +# Key Bindings {{{1 +# root {{{2 + +# `F1`, ..., `F10` are used in `htop(1)`. +# `F11` and `F12` are used in WeeChat to scroll in the nicklist bar. +bind -T root S-F8 run -b 'tmux_log' + +# Why do you rebind `command-prompt` to `pfx + ;`? It's already bound to `pfx + :`...{{{ +# +# We often press the prefix key by accident, then press `:` to open Vim's +# command-line. As a result, we open tmux command-line; it's distracting. +#}}} +bind ';' command-prompt +unbind : +# Do *not* bind `command-prompt` to `M-:`; we press it by accident too frequently. + +# focus next/previous window +# I'm frequently pressing these key bindings in Vim's insert mode. It's distracting!{{{ +# +# Idea1: Use `pfx w` to focus an arbitrary window, and supercharge `pfx h/l` to +# focus the next/previous pane or window. +# +# bind -r h if -F '#{pane_at_left}' 'prev' 'selectp -L' +# bind -r l if -F '#{pane_at_right}' 'next' 'selectp -R' +# +# Idea2: Use `M-h/l` twice when the current pane is running Vim. +# +# set -g @foo 0 +# bind -T root M-l if -F '#{m:*vim,#{pane_current_command}}' { if -F '#{@foo}' { set -g @foo 0 ; next } { set -g @foo 1 } } { next } +# bind -T root M-h if -F '#{m:*vim,#{pane_current_command}}' { if -F '#{@foo}' { set -g @foo 0 ; prev } { set -g @foo 1 } } { prev } +# +# Idea3: Make tmux send `M-h` or `M-l` when Vim is running in the current pane, +# and let Vim decide what to do, based on whether we're in insert mode or normal +# mode. +# +# noremap! m_hl('h') +# noremap! m_hl('l') +# nno m_hl('h') +# nno m_hl('l') +# xno m_hl('h') +# xno m_hl('l') +# +# fu s:m_hl(seq) abort +# if mode() =~# '^[ic]$' +# return '' +# endif +# call system('tmux '..(a:seq is# 'l' ? 'next' : 'prev')) +# return '' +# endfu +# bind -T root M-l if -F '#{m:*vim,#{pane_current_command}}' 'send M-l' 'next' +# bind -T root M-h if -F '#{m:*vim,#{pane_current_command}}' 'send M-h' 'prev' +# +# Problem: When we're running Vim without config (`-Nu NONE`), we can't focus another tmux window. +# This is because those custom mappings are not installed then. +#}}} +# Why do you inspect these window flags?{{{ +# +# To prevent the commands from wrapping around the edges. +# They do that by default, and that bothers me at the moment. +# +# For example, I often want to focus the next window, and press `¹` by accident +# instead of `²`. It still works, because we only have 2 windows, but that +# prevents me from learning to press the correct keys. +# +# Once you're confident that those keys are well-chosen, and they've passed into +# your muscle memory, you could just install: +# +# bind -T root ¹ prev +# bind -T root ² next +#}}} +bind -T root ¹ if -F '#{window_start_flag}' {} { prev } +bind -T root ² if -F '#{window_end_flag}' {} { next } +# TODO: Sometimes, we press these keys by accident. Find better ones?{{{ +# +# In particular, when we press `[ox` (where `x` is some character) to toggle +# some Vim setting, we need to press `AltGr` to produce `[`; but sometimes, we +# don't release the key before pressing `o`. +# As a result, we press `AltGr + o` which produce `²`; tmux intercepts the +# keypress, and tries to visit the previous window, while in reality, we wanted +# to toggle some Vim setting. +#}}} + +# `M-s` to enter copy mode +# Do *not* bind `M-s` to anything while in copy mode!{{{ +# +# This would make you lose an interesting feature of the `copy-mode` command +# while you're already in copy mode, reading the output of some command such as +# `list-keys`. +# +# The default behavior makes tmux show you the contents of the original window: +# +# # you're reading a file +# :list-keys +# :copy-mode +# # the window shows again the file you were reading +# # press `q`, and you get back the output of `:list-keys` +# +# I don't know where this is documented. +# And I don't know why the `copy-mode` command is invoked when we press `M-s` +# while in copy mode. +# We only have one key binding using `M-s` as its lhs, and it's in the root +# table, not in the copy-mode table. +# +# Note that this feature is not specific to our `M-s` key binding. +# I can reproduce with no config (and `C-b [` instead of `M-s`). +#}}} +bind -T root M-s copy-mode + +bind -T root M-z resize-pane -Z +bind -T root M-Z lastp \; resize-pane -Z + +# Do *not* exit copy mode when when we reach the bottom of the scrollback buffer.{{{ +# +# We remove the `-e` flag which is passed to `copy-mode` by default, so that if +# we enter copy mode by scrolling the mouse wheel upward, and we press a key +# which reaches the bottom of the scrollback buffer, we don't quit copy mode. +# +# If you leave `-e` in the default key binding, here's what could happen: +# you scroll the wheel upward to enter copy mode; at one point, you keep +# pressing `C-d` to scroll toward the bottom; once you reach the bottom, you'll +# quit copy mode, and `C-d` will close the shell if the command-line is empty. +#}}} +# Where did you find the code?{{{ +# +# $ tmux -Lx -f/dev/null start \; lsk | grep 'root.*WheelUpPane' +#}}} +bind -T root WheelUpPane \ + if -F -t= '#{mouse_any_flag}' \ + { send -M } \ + { if -Ft= '#{pane_in_mode}' 'send -M' 'copy-mode -t=' } + +# But *do* exit copy mode if we scroll downward with the mouse wheel and reach the bottom of the buffer. +bind -T copy-mode-vi WheelDownPane \ + selectp \; \ + send -X -N 5 scroll-down \; \ + if -F '#{scroll_position}' '' 'send -X cancel' + +# copy-mode-vi {{{2 + +bind -T copy-mode-vi C-Space send -X set-mark +# actually, it should be named "exchange-point-and-mark"... +bind -T copy-mode-vi C-x send -X jump-to-mark + +# jump Back to the Beginning of the previous shell command{{{ +# +# Look for the previous shell prompt, to get to the beginning of the last +# command output. After pressing the key binding, you can visit all the other +# prompts by pressing `n` or `N`. +# +# Inspiration: https://www.youtube.com/watch?v=uglorjY0Ntg +#}}} +bind -T copy-mode-vi ! send -X start-of-line \; send -X search-backward '٪' + +# Make tmux use the prefix 'buf_' instead of 'buffer' when naming the buffer +# storing the copied selection. +bind -T copy-mode-vi Enter send -X copy-selection-and-cancel 'buf_' + +bind -T copy-mode-vi g switchc -T g-prefix +bind -T g-prefix g send -X history-top +# Open a visually selected text (filepath or url) by pressing `gf` or `gx`. +# TODO: The key binding will break if the file name contains double quotes.{{{ +# +# Find a way to escape special characters. +# +# I tried `$ tmux display -p '#{q:#(tmux pasteb)}'`, but it doesn't work. +# You probably need `q:`, but a modifier needs to be followed by the name of a +# replacement variable and `#(tmux pasteb)` is not one. +# +# Update: +# I don't think you should use `q:`. +# Maybe you should try to find a shell utility which quotes special characters +# in a given text. +# Does such a tool exist? +# If it does, maybe you could try: `#(magic_tool $(tmux pasteb))`. +# +# Update: +# `#{}` can be used for a user option (`@foo`). +# You could temporarily set a user option with the the filepath/url, and quote +# it with `q:`. +# +# Make some tests on that: +# +# http://example.org/foo/bar"baz.html +# http://example.org/foo/bar'baz.html +# https://www.reddit.com/r/Fantasy/ +# +# bind -T copy-mode-vi x send -X copy-selection-and-cancel \; \ +# run 'tmux set @copied_url "$(tmux showb)"' \; \ +# run 'xdg-open "#{q:@copied_url}"' +# +# For some reason, we need 2 `run-shell`, otherwise, it seems that the key +# binding doesn't update the url, when we try a new one. +# +# For some reason, when we try to open this: +# +# http://example.org/a'b.html +# +# tmux opens this instead: +# +# http://example.org/a/'b.html +# ^ +# ✘ +# +# For some reason, when we try to open this: +# +# http://example.org/a"b.html +# +# tmux doesn't escape the double quote. +# +# Text ended before matching quote was found for ".˜ +# (The text was '/usr/bin/firefox "http://example.org/foo/bar"baz.html"')˜ +# +# $ tmux set @foo "a'b" \; display -p '#{q:@foo}' +# a\'b˜ +# +# $ tmux set @foo "a'b" \; run 'echo #{q:@foo}' +# a'b˜ +# +# Why doesn't `run-shell` expand the `#{q:}` format? +# +# Update: you need to quote the format: +# +# $ tmux set @foo "a'b" \; run 'echo "#{q:@foo}"' +# a\'b˜ +# +# Update: +# I don't think it's possible. +# Try to open the urls via `urlscan(1)`. +# The latter fails for the first urls. +# If a specialized tool fails, I doubt we can do better. +#}}} +# FIXME: `gf` fails to open a file path starting with `~/`. +bind -T g-prefix f send -X pipe "xargs -I {} tmux run 'xdg-open \"{}\"'" +bind -T g-prefix x send -X pipe "xargs -I {} tmux run 'xdg-open \"{}\"'" + +bind -T copy-mode-vi v if -F '#{selection_present}' { send -X clear-selection } { send -X begin-selection } +bind -T copy-mode-vi V if -F '#{selection_present}' { send -X clear-selection } { send -X select-line } +bind -T copy-mode-vi C-v if -F '#{selection_present}' \ + { if -F '#{rectangle_toggle}' \ + { send -X rectangle-toggle ; send -X clear-selection } \ + { send -X rectangle-toggle } \ + } { send -X begin-selection ; send -X rectangle-toggle } + +# set -s copy-command 'xsel -i' +# if there's a selection, make `y` yank it +# without selection, make `yy` yank the current line and `yiw` the current word +bind -T copy-mode-vi y if -F '#{selection_present}' \ + { send -X copy-pipe-and-cancel 'xsel -i -b' 'buf_' } \ + { switchc -T operator-pending-and-cancel } +# y**y** +bind -T operator-pending-and-cancel y send -X copy-line 'buf_' +# y**iw** +bind -T operator-pending-and-cancel i switchc -T text-object-and-cancel +# Do *not* use `select-word`: https://github.com/tmux/tmux/issues/2126 +bind -T text-object-and-cancel w \ + { send -X cursor-right + send -X previous-word + send -X begin-selection + send -X next-word-end + send -X copy-selection-and-cancel 'buf_' } + +bind -T copy-mode-vi . run "zsh -c \"tmux source =(sed -n '/^# #{@dot_command}/,/^$/p' $HOME/.config/tmux/repeat.conf)\"" + +# **"A**yiw **"A**yy v_**"A**y +bind -T copy-mode-vi '"' switchc -T specify-register +bind -T specify-register A switchc -T operate-on-register +# Why the `if 'tmux showb'`?{{{ +# +# `append-selection` doesn't accept the optional prefix buffer name argument. +# If there's no buffer, `append-selection` will create a buffer with the prefix +# name `buffer`; we want the prefix `buf_`. +#}}} +# "A**y**iw "A**y**y v_"A**y** +bind -T operate-on-register y if -F '#{selection_present}' \ + { if 'tmux showb' { send -X append-selection } { send -X copy-selection 'buf_' }} \ + { switchc -T operator-pending } +# "Ay**i**w +bind -T operator-pending i switchc -T text-object +# "Ay**y** +bind -T operator-pending y { run "zsh -c \"tmux source =(sed -n '/^# yy/,/^$/p' $HOME/.config/tmux/repeat.conf)\"" } + +# v**i**w +bind -T copy-mode-vi i switchc -T text-object +# Why pass a second command to the first `if`?{{{ +# +# To support `"Ayiw`. +#}}} +# vi**w** "Ayi**w** +bind -T text-object w if -F '#{selection_present}' \ + { send -X stop-selection + send -X cursor-right + send -X previous-word + send -X begin-selection + send -X next-word-end } \ + { run "zsh -c \"tmux source =(sed -n '/^# yiw/,/^$/p' $HOME/.config/tmux/repeat.conf)\"" } + +bind -T copy-mode-vi Y send -X copy-end-of-line 'buf_' + +# Why don't you pass `-b` to run?{{{ +# +# There's no need to. +# `pipe-and-cancel` doesn't block. +# The shell command passed as an argument is forked. +#}}} +bind -T copy-mode-vi S send -X pipe-and-cancel \ + "xargs -I {} tmux run 'xdg-open \"https://www.startpage.com/do/dsearch?query={}\"'" + +# Why? {{{ +# +# `search-backward-incremental` is better than `search-forward`, because it +# highlights all the matches as you type (like in Vim when 'hlsearch' and +# 'incsearch' are both set); you need to pass `-i` to `command-prompt` for it to work. +# +# Also, these key bindings make the prompt less noisy (`/` is shorter than `search down`). +# +# Inspired from the default emacs key bindings in copy mode. +# }}} +bind -T copy-mode-vi / command-prompt -ip '/' { send -X search-forward-incremental '%%' } +bind -T copy-mode-vi ? command-prompt -ip '?' { send -X search-backward-incremental '%%' } + +bind -T copy-mode-vi % send -X next-matching-bracket +bind -T copy-mode-vi _ send -X start-of-line + +# move current window position forward/backward +# Why not using `h` and `l`, or `j` and `k`?{{{ +# +# `M-C-[jk]` could be more useful for something else (WeeChat?), and doesn't +# match a horizontal motion. +# `M-C-[hl]` conflicts with our window manager (move virtual desktop). +# `M-C-[HL]` is hard to press. +#}}} +bind -r > if -F '#{window_end_flag}' { movew -t :0 } { swapw -t :+ ; selectw -n } +bind -r < if -F '#{window_start_flag}' { movew -t :99 } { swapw -t :- ; selectw -p } +# If you don't like `:99`, you could write this instead:{{{ +# +# bind -r < if -F '#{window_start_flag}' \ +# { run 'tmux movew -t:$((#{W:#{?window_end_flag,#I,}}+1))' } \ +# { swapw -t :- ; selectw -p } +#}}} + +# prefix {{{2 + +# NOTE: `C-\` is free. + +# cycle through predefined layouts +# Do *not* pass `-r` to `bind`!{{{ +# +# We use Space in Vim as a prefix key. +# If you use `-r` here, when you're in Vim there's a risk that when you press +# Space as a prefix key, it's consumed by tmux to run `nextl` instead. +#}}} +bind Space nextl + +# focus last pane, without breaking the zoomed state of the window +bind M-Space lastp -Z + +# display short description for the next keypress (inspired from a default key binding) +bind M-h command-prompt -k -p key { lsk -1N '%%' } +# ├┘ ├─┘{{{ +# │ └ -N instead show keys and attached notes in the root and prefix key tables; +# │ with -1 only the first matching key and note is shown +# └ -k is like -1 but the key press is translated to a key name +#}}} +# What is the side effect of `-1`?{{{ +# +# Not only does it limit the output of the command to the first matching key and +# note, but it also redirects where it's displayed. +# Without `-1`, it's displayed on the terminal in copy-mode. +# With `-1`, it's displayed as a message on the tmux status line. +#}}} + +# display short description for the next 2 keypresses +bind M-l command-prompt -1p 'key1,key2' \ + { run "tmux lsk | awk '/-T prefix\s+%1\s+/ { print \$NF }' | xargs -I {} tmux lsk -1N -P 'M-Space %1 ' -T {} '%2'" } + +# repeat last shell command in last active pane +# Warning: This overrides a default key binding:{{{ +# +# bind-key -T prefix . command-prompt -T target "move-window -t '%%'" +#}}} +bind . if -F -t '{last}' '#{m:*sh,#{pane_current_command}}' { send -t '{last}' Up Enter } + +# copy clipboard selection into tmux buffer +# Why `run`?{{{ +# +# To make the shell evaluate the command substitution. +#}}} +# `--`?{{{ +# +# The evaluation of the substitution command could start with a hyphen. +# And if that happens, tmux could parse the text as an option passed to +# `set-buffer` (i.e. `-a`, `-b`, or `-n`). +#}}} +# `-o`?{{{ +# +# To make `xsel(1x)` output something. +#}}} +bind b switchc -T buffer +bind -T buffer -N 'copy clipboard into tmux buffer' > run 'tmux setb -- "$(xsel -ob)"' \; display 'clipboard copied into tmux buffer' +bind -T buffer -N 'copy tmux buffer into clipboard' < choose-buffer -F '#{buffer_sample}' \ + { run 'tmux showb -b "%%" | xsel -ib' ; display 'tmux buffer copied into clipboard' } + +# We use `*` instead of `q` because it's more consistent with `#`. +# They both show information. Besides, if I hit `pfx q` by accident (which +# happens often), I won't be distracted by the panes numbers. + +bind * displayp + +bind ! show-messages + +# make these default key bindings repeatable +# Do *not* pass `-r` to `bind`!{{{ +# +# Suppose you're in the 'study' session, Vim is running, and the Vim window is +# horizontally split in 2 viewports. +# You press `pfx + )` to switch to the 'fun' session, then you press `)` to go +# back to the 'study' session. +# Finally you press C-j to focus the Vim split below; it won't work because of this: +# +# bind -r C-j resizep -D 5 +#}}} +bind ( switchc -p +bind ) switchc -n + +# Problem: tmux-fingers doesn't let us search outside the current screen. +# Solution: Install a key binding which lets us search through the scrollback buffer, in a Vim buffer. +# What does `-J` do for `capture-pane`?{{{ +# +# It joins wrapped lines. +# +# Suppose that we have a long line in a file, which doesn't fit on a single line +# of the terminal, but on two. +# If you run `$ cat file`, this too-long line will be displayed on two +# consecutive lines of the terminal. +# Without `-J`, tmux would copy – in one of its buffers – the two consecutive +# lines on two different lines. +# +# But that's not what we want. +# We want the buffer to join back these two lines, as they were originally in the file. +#}}} +# `-S -`? {{{ +# +# `-S` specifies the starting line number, from where to copy. +# The special value `-` refers to the start of the history. +# Without this, `capture-pane` would capture only from the first visible line in +# the pane; we want the *whole* scrollback buffer. +#}}} +# Why don't you use `split-window` instead of `popup`?{{{ +# +# With `split-window`, you would also need to run `resize-pane -Z`. +# But what if there's already a zoomed pane in the current window? +# After quitting Vim, the latter would be unzoomed. +# So, we use `popup` to preserve a possible zoomed pane in the current window. +#}}} +bind -T root M-c if -F \ + '#{||:#{m:*vim,#{pane_current_command}},#{==:#{pane_current_command},man}}' \ + '' \ + { capture-pane -b scrollback -J -S - + popup -E -xC -yC -w75% -h75% -d '#{pane_current_path}' \ + "tmux showb -b scrollback | vim --not-a-term +'call tmux#formatCapture#main()' - ; \ + tmux deleteb -b scrollback" } + +# split window vertically / horizontally +# When the window is zoomed, we often forget that it's already split, and wrongly press `pfx _`.{{{ +# +# Make `pfx _` smarter; i.e. if the window is already split and zoomed, don't +# split it again, instead focus the previous pane. +#}}} +bind _ if -F '#{window_zoomed_flag}' 'lastp' 'split-window -v -c "#{pane_current_path}"' +bind | if -F '#{window_zoomed_flag}' 'lastp' 'split-window -h -c "#{pane_current_path}"' +bind - if -F '#{window_zoomed_flag}' 'lastp' 'split-window -fv -c "#{pane_current_path}"' +bind '\' if -F '#{window_zoomed_flag}' 'lastp' 'split-window -fh -c "#{pane_current_path}"' +# ├───────────────────────┘ +# └ keep current working directory + +bind -N 'bring arbitrary pane in current window' [ command-prompt -p 'join pane from:' { join-pane -s '%%' } +bind -N 'send current pane in arbitrary window' ] command-prompt -p 'send pane to:' { join-pane -t '%%' } +bind T breakp + +# Why a space before every shell command (` cmus`, ` weechat`, ...)?{{{ +# +# It's useful to prevent zsh from saving the command in the history when we +# cancel the search with `C-c` or `C-d` (`setopt HIST_IGNORE_SPACE`). +#}}} +# What's the `-n` option passed to `neww`?{{{ +# +# It sets the name of the window. +#}}} +# What about the `-c` option?{{{ +# +# It sets the cwd of the shell. +#}}} +bind M-1 rename -t 0 fun \; \ + renamew -t 1 music \; \ + send ' cmus' 'Enter' '2' 'Enter' 'Enter' \; \ + neww -n irc -c $HOME \; \ + send ' weechat' 'Enter' \; \ + new -s study \; \ + send ' nv' 'Enter' + +# you need the display command to force tmux to clear the log (`set message-limit 0` is not enough) +bind C set -F @message-limit-save '#{message-limit}' \; \ + set message-limit 0 \; display 'message log cleared' \; \ + set -F message-limit '#{@message-limit-save}' \; set -u @message-limit-save + +# Note that `clear-history` doesn't clear *all* the history.{{{ +# +# The last lines of the scrollback buffer which fits in one screen are preserved. +# So, if you enter copy mode, you'll still be able to scroll back *some* lines, +# but not more than a few dozens. +#}}} +# We can't use `C-l` for the lhs, because we already use it in another key binding:{{{ +# +# bind -r C-l resizep -R 5 +#}}} +bind C-c send C-l \; clear-history +# │ │ +# │ └ clear tmux scrollback buffer +# └ clear terminal screen + +# Do *not* use `q` in the prefix table; pressed by accident too frequently. +bind Q switchc -T Q-prefix +bind -T Q-prefix q confirm 'kill-pane' +bind -T Q-prefix Q confirm 'kill-window' + +# Why `TERM="#{client_termname}"` in the 'sourced files' entry?{{{ +# +# When `$ tmux -v -Ldm` is started, it inherits the TERM of the current tmux +# server, which is set by 'default-terminal'. +# As a result, the condition `[ if '[ "$TERM" != #{default-terminal} ]'` is +# true, and several files which we expect to be sourced, won't be sourced. +# +# We want to see all files which would be sourced if we were to start tmux from +# a regular shell; so we need to reset TERM. +#}}} +# Why don't you pass `-v` to `show-options`?{{{ +# +# If you do, it would considerably simplify our commands; we wouldn't need `sed(1)` at all. +# Unfortunately, it would also fuck up the output if one of the alias is defined +# on several lines. +# +# By avoiding `-v`, we make sure that each alias is output on a single line. +#}}} +bind i display-menu -x 0 -y 0 \ + 'server information' i info \ + 'key bindings' K lsk \ + 'aliases' a { run 'tmux show -s command-alias | column -t -s= | sed "s/^command-alias\[[0-9]*]\s*//; s/^\"\|\"$//g"' } \ + 'sourced files' f { run 'cd "$(mktemp -d /tmp/.tmux.XXXXXXXXXX)" \ + ; TERM="#{client_termname}" tmux -v -Ldm start \ + ; grep loading tmux-server*.log | grep -v grep | sed "s/.*loading \(.*\)/\1/"' } \ + '' \ + 'options' o { display-menu -y 0 \ + 'server options' C-s { show -s } \ + 'global session options' s { show -g } \ + 'local session options' S { show } \ + '' \ + 'global window options' w { show -gw } \ + 'local window options' W { show -w } \ + 'local pane options' p { show -p } \ + } \ + '' \ + 'global hooks' h { show-hooks -g } \ + 'local hooks' H { show-hooks } \ + '' \ + 'global environment' e { showenv -g } \ + 'local environment' E { showenv } \ + '' \ + 'outer terminfo description' t { run 'infocmp -1x "#{client_termname}" | sort' } \ + 'inner terminfo description' T { run 'infocmp -1x "#{default-terminal}" | sort' } \ + '' \ + 'default settings' d { display-menu -y 0 \ + 'key bindings' K { run 'tmux -Ldm -f/dev/null start \; lsk' } \ + 'aliases' a { run 'tmux -Ldm -f/dev/null start \; show -sv command-alias | column -t -s= | sed "s/^command-alias\\[[0-9]]\\s*//; s/^\"\|\"$//g"' } \ + '' \ + 'server options' C-s { run 'tmux -Ldm -f/dev/null start \; show -s' } \ + 'window options' w { run 'tmux -Ldm -f/dev/null start \; show -gw' } \ + 'session options' s { run 'tmux -Ldm -f/dev/null start \; show -g' } \ + } + +# By default, `detach-session` is bound to `d`. +# I find that too easy to press, so we move it to `@`. +# Why `@`? +# I didn't find anything better, and it seems hard to press by accident... +bind @ detach + +# resize pane +bind -r C-h resizep -L 5 +bind -r C-j resizep -D 5 +bind -r C-k resizep -U 5 +bind -r C-l resizep -R 5 + +# focus neighboring panes +# Do *not* make them repeatable. It leads to a confusing user experience.{{{ +# +# Example: Press `pfx k` to focus the pane above which is running Vim, then press `j`. +# Expected: Vim scrolls downward. +# Actual Result: tmux focuses back the pane below. +# +# If you have many panes, and you need to focus one, try `display-panes` +# (currently bound to `pfx *`), then press the index of the desired pane. +#}}} +bind h selectp -L +bind l selectp -R +bind j selectp -D +bind k selectp -U + +# move pane to the far right/left/bottom/top +bind H split-window -fhb \; swap-pane -t ! \; kill-pane -t ! +bind L split-window -fh \; swap-pane -t ! \; kill-pane -t ! +bind J split-window -fv \; swap-pane -t ! \; kill-pane -t ! +bind K split-window -fvb \; swap-pane -t ! \; kill-pane -t ! + +# Toggle mouse. +# Temporarily preventing tmux from handling the mouse can be useful in some +# terminals to copy text in the clipboard. +# Why `M` for the lhs?{{{ +# +# It provides a good mnemonic for “mouse”. +# I don't use `C-m` nor `M-m` because, atm, they're used to display some default menus. +# +# However, note that `pfx M` is used by default to clear the marked pane (`select-pane -M`). +# It's not a big deal to lose it, because we can get the same result by focusing +# the marked pane and then pressing `pfx m` (`select-pane -m`). +# The latter marks the pane if it's not already, or clears the mark otherwise. +#}}} +bind M set -g mouse \; display 'mouse: #{?#{mouse},ON,OFF}' + +# toggle 'monitor-activity' in current window +bind C-a set -w monitor-activity \; display 'monitor-activity: #{?#{monitor-activity},ON,OFF}' + +# kill all panes except the current one (similar to `:only` or `C-w o` in Vim) +bind o kill-pane -a + +# paste last tmux buffer +# Do *not* choose a key too easy to type.{{{ +# +# It's dangerous. +# In Vim, the contents of the buffer will be typed, which will have unexpected +# results, unless you're in insert mode. +#}}} +bind C-p paste-buffer -p + +# choose and paste arbitrary tmux buffer +# What's `-Z`?{{{ +# +# It makes tmux zoom the pane so that it takes the whole window. +#}}} +# `-F`?{{{ +# +# It specifies the format with which each buffer should be displayed. +# In it, you can use these replacement variables: +# +# ┌────────────────┬─────────────────────────────┐ +# │ buffer_created │ creation date of the buffer │ +# ├────────────────┼─────────────────────────────┤ +# │ buffer_name │ name of the buffer │ +# ├────────────────┼─────────────────────────────┤ +# │ buffer_sample │ starting text of the buffer │ +# ├────────────────┼─────────────────────────────┤ +# │ buffer_size │ size of the buffer │ +# └────────────────┴─────────────────────────────┘ +# +# Note that even with an empty format, tmux will still display the name of a +# buffer followed by a colon. +# So, `buffer_name` is not very useful (unless you want to print the name of a +# buffer twice). +#}}} +# the `-p` argument passed to `paste-buffer`?{{{ +# +# It prevents the shell from automatically running a pasted text which contains +# a newline. +# +# See `man tmux /^\s*paste-buffer` +#}}} +bind p choose-buffer -Z -F '#{buffer_sample}' "paste-buffer -p -b '%%'" + +# similar to `C-w r` and `C-w R` in Vim +bind -r r rotate-window -D \; selectp -t :.+ +bind -r R rotate-window -U \; selectp -t :.- + +# reload tmux config +bind C-r source "$HOME/.config/tmux/tmux.conf" \; display 'Configuration reloaded' + +# You need to install the `urlscan(1)` utility for this key binding to work. +# Why do you include `deleteb` inside the shell cmd run by `split-window`?{{{ +# +# If you move it outside: +# +# bind u capture-pane \; split-window -l 10 'urlscan =(tmux showb)' \; deleteb +# +# `urlscan(1)` can't find any link. +# +# This is because: +# +# 1. deleteb is run before `$ tmux showb` +# 2. thus `$ tmux showb` outputs nothing +# 3. urlscan finds no url +# +# You can get the same effect by running: +# +# $ tmux split-window 'urlscan =(echo "")' +#}}} +# We name the tmux buffer so that we can remove it reliably at the end.{{{ +# +# Indeed, you might copy some text while the urlscan pane is opened (not +# necessarily in the latter; in any session, window, pane), creating a new +# buffer at the top of the stack. +# If you just run `$ tmux deleteb`, it would remove that buffer instead of the +# buffer created by `capture-pane`. +#}}} +# Why `head -c -1`?{{{ +# +# If there is no url in the tmux buffer, we want tmux to automatically close the +# pane. That's why we use `ifne(1)` later; it runs the second `urlscan(1)` on +# the condition that the output of the previous one is empty. The problem is +# that even when the first `urlscan(1)` fails to find any url, it still outputs +# a single newline. We need to remove it, so that `ifne(1)` works as expected. +#}}} +bind u capture-pane -b urlscan \; \ + split-window -l 10 " + tmux showb -b urlscan | \ + urlscan --no-browser | \ + head -c -1 | \ + ifne urlscan --compact \ + --dedupe \ + --nohelp \ + --regex \"(http|ftp)s?://[^ '\\\">)}\\\\]]+\" \ + ; tmux deleteb -b urlscan + " + +# focus next pane +bind -r C-w selectp -t :.+ + +# similar to `C-w x` in Vim +bind x swap-pane -U +bind X swap-pane -D +# }}}1 +# Hooks {{{1 +# Don't use this hook: `set-hook -g after-split-window 'selectl even-vertical'`{{{ +# +# You wouldn't be able to split vertically anymore. +# Splitting vertically would result in an horizontal split no matter what. +# +# The hook is given as an example in `man tmux`; its purpose is to resize +# equally all the panes whenever you split a pane horizontally. +#}}} + +set-hook -g pane-focus-out '' + +# Plugins {{{1 + +# Why the guard?{{{ +# +# To prevent the plugins from re-installing their key bindings every time we +# resource `tmux.conf`. +# Indeed, we only unbind the key bindings from the copy-mode table once. +# +# Besides, it's probably a bad idea to resource plugins. +#}}} +if '[ "$TERM" != "#{default-terminal}" ]' { source "$HOME/.config/tmux/plugins/run" } + +# Rebind {{{1 + +# Purpose:{{{ +# +# The tmux-yank plugin installs this key binding: +# +# bind-key -T copy-mode-vi Y send-keys -X copy-pipe-and-cancel "tmux paste-buffer" +# +# It copies the selection, quit copy mode, then paste the buffer. +# +# However, it doesn't support the bracketed paste mode. +# So we redefine the key binding, and pass `-p` to `paste-buffer` to surround +# the text with the sequences `Esc [ 200 ~` and `Esc [ 201 ~`. +# This way, if we select a text containing a newline, then press `p`, it's not +# automatically run by the shell. +# +# From `man tmux /^\s*paste-buffer`: +# +# > If -p is specified, paste bracket control codes are inserted around the +# > buffer if the application has requested bracketed paste mode. +# +# Note that this requires that the shell supports the bracketed paste mode. +# I.e. if you're using zsh, you need zsh version 5.1 or greater, and if you're +# using bash, you need bash 4.4 or greater. +# +# --- +# +# The `-p` option was added to tmux in the commit `f4fdddc`. +# According to the changelog, this was somewhere between tmux 1.6 and 1.7. +# +# --- +# +# Note that the original key binding used `copy-pipe-and-cancel` which – while +# working – doesn't make sense; you can't pipe anything to `$ tmux paste-buffer`, +# since it doesn't read its input. +#}}} +bind -T copy-mode-vi p send -X copy-selection-and-cancel \; paste-buffer -p \; deleteb +# ^^ + +# Unbind {{{1 + +# How to find the default key bindings installed with no config?{{{ +# +# $ tmux -Lx -f/dev/null new +# C-b ? +# VG$ +# Enter +# $ vim +# i +# C-b ] +# +# Make sure to release `Ctrl` before pressing `]`. +#}}} +# How to unbind `#`, `~`, `'`, `"`?{{{ +# +# Quote the key (with single or double quotes). +# +# From `man tmux /^KEY BINDINGS`: +# +# > Note that to bind the ‘"’ or ‘'’ keys, quotation marks are necessary. +#}}} +# How to unbind `;`?{{{ +# +# Escape it. +# +# From `man tmux /^COMMANDS`: +# +# > A literal semicolon may be included by escaping it with a backslash (for +# > example, when specifying a command sequence to bind-key). +#}}} + +# TODO: +# Remove all default key bindings which you're not interested in. +# Some of them could be hit by accident. +# Keep only the ones you really use. +# Besides, it will give us a smaller table of key bindings, which will be easier +# to read when we have an issue with one of our key bindings. +# Have a look at `~/Desktop/tmux.md`. + +# prefix {{{2 + +# send-prefix +unbind C-b +# rotate-window +unbind C-o +# show-messages +unbind '~' +# split-window +unbind '"' +# choose-buffer -Z +unbind = +# detach-client +unbind d +# next-window +unbind n +# swap-pane -[UD] +unbind '{' +unbind '}' +# select-pane -[UDLR] +unbind Up +unbind Down +unbind Left +unbind Right +# tmux clear-history (tmux-logging) +unbind M-c +# rotate-window -D +unbind M-o +# resize-pane -U 5 +unbind M-up +# resize-pane -D 5 +unbind M-down +# resize-pane -L 5 +unbind M-left +# resize-pane -R 5 +unbind M-right +# resize-pane -[UDLR] +unbind C-Up +unbind C-Down +unbind C-Left +unbind C-Right +# run ~/.config/tmux/plugins/tmux-logging/scripts/screen_capture.sh (tmux-logging) +unbind M-p +# run ~/.config/tmux/plugins/tmux-logging/scripts/save_complete_history.sh (tmux-logging) +unbind M-P +# resize-pane -Z +unbind z +# command-prompt -i -p / { send-keys -X search-forward-incremental "%%" } +unbind / + +# copy-mode-vi {{{2 + +# By default, it's bound to `send-keys -X copy-pipe-and-cancel`.{{{ +# +# I don't like that, because I often select some text with the mouse by accident +# (or when I'm bored); when that happens, obviously, tmux creates a buffer. +# +# This pollutes our list of buffers, and makes the interesting ones harder to +# find. Besides, if when I want to copy some text, I will certainly not do it +# with the mouse (not accurate enough). +#}}} +unbind -T copy-mode-vi MouseDragEnd1Pane +# send-keys -X begin-selection +unbind -T copy-mode-vi Space +# send-keys -X copy-pipe-and-cancel +unbind -T copy-mode-vi C-j +# send -X copy-pipe-and-cancel 'xsel -i --clipboard; tmux paste-buffer' (tmux-yank) +unbind -T copy-mode-vi M-y +# (tmux-yank) +unbind -T copy-mode-vi Y + +# copy-mode {{{2 + +# We don't need the key bindings from the copy-mode table; we use the copy-mode-*vi* table. +# Why the guard?{{{ +# +# Once the table is empty, it's removed. +# So, if you later try to unbind a key binding from it, an error will be raised: +# +# Table copy-mode doesn't exist +# +# Which can be repeated for every key binding you try to remove: +# +# Table copy-mode doesn't exist +# Table copy-mode doesn't exist +# ... +# +# Run `show-messages` to see them. +# +# This is annoying when you reload `tmux.conf`. +#}}} +if '[ "$TERM" != "#{default-terminal}" ]' { source "$HOME/.config/tmux/unbind-copy-mode.conf" } +# }}}1 From 329c2c2a919d3c66b08672ff37af4e2393dc9a95 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Sep 2021 07:11:03 +0000 Subject: [PATCH 0975/1006] Change copying arguments to that flags without arguments are inserted correctly and empty arguments lists do not crash. Fixes crash reported by & ok mpi@. --- arguments.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arguments.c b/arguments.c index d7274ff8..20d5e7ba 100644 --- a/arguments.c +++ b/arguments.c @@ -302,10 +302,13 @@ args_copy(struct args *args, int argc, char **argv) struct args_value *value, *new_value; u_int i; + cmd_log_argv(argc, argv, "%s", __func__); + new_args = args_create(); RB_FOREACH(entry, args_tree, &args->tree) { - if (entry->count == 1) { - args_set(new_args, entry->flag, NULL); + if (TAILQ_EMPTY(&entry->values)) { + for (i = 0; i < entry->count; i++) + args_set(new_args, entry->flag, NULL); continue; } TAILQ_FOREACH(value, &entry->values, entry) { @@ -314,6 +317,8 @@ args_copy(struct args *args, int argc, char **argv) args_set(new_args, entry->flag, new_value); } } + if (args->count == 0) + return (new_args); new_args->count = args->count; new_args->values = xcalloc(args->count, sizeof *new_args->values); for (i = 0; i < args->count; i++) { From 5a4b2fd68c19be99d37b2a3dc9d0bfd7959d1843 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Sep 2021 06:57:48 +0000 Subject: [PATCH 0976/1006] Fix parsing of aliases again (GitHub issue 2842), also make argument parsing a bit simpler and fix the names of some client flags. --- cmd-parse.y | 47 ++++++++++++++++++++++------------------------- menu.c | 2 +- screen.c | 6 +++--- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 2b5b7e0b..b75a4ad9 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -742,15 +742,14 @@ cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix) static int cmd_parse_expand_alias(struct cmd_parse_command *cmd, - struct cmd_parse_input *pi, struct cmd_parse_result *pr, - struct cmd_list **cmdlist) + struct cmd_parse_input *pi, struct cmd_parse_result *pr) { - struct cmd_parse_argument *arg, *arg1, *first, *after; + struct cmd_parse_argument *arg, *arg1, *first; struct cmd_parse_commands *cmds; struct cmd_parse_command *last; char *alias, *name, *cause; - *cmdlist = NULL; + memset(pr, 0, sizeof *pr); first = TAILQ_FIRST(&cmd->arguments); if (first == NULL || first->type != CMD_PARSE_STRING) { @@ -775,43 +774,38 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd, last = TAILQ_LAST(cmds, cmd_parse_commands); if (last == NULL) { - *cmdlist = cmd_list_new(); + pr->status = CMD_PARSE_SUCCESS; + pr->cmdlist = cmd_list_new(); return (1); } TAILQ_REMOVE(&cmd->arguments, first, entry); cmd_parse_free_argument(first); - after = TAILQ_FIRST(&last->arguments); TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) { TAILQ_REMOVE(&cmd->arguments, arg, entry); - if (after == NULL) - TAILQ_INSERT_TAIL(&last->arguments, arg, entry); - else - TAILQ_INSERT_AFTER(&last->arguments, after, arg, entry); - after = arg; + TAILQ_INSERT_TAIL(&last->arguments, arg, entry); } cmd_parse_log_commands(cmds, __func__); cmd_parse_build_commands(cmds, pi, pr); - if (pr->status != CMD_PARSE_SUCCESS) - *cmdlist = pr->cmdlist; return (1); } -static struct cmd_list * +static void cmd_parse_build_command(struct cmd_parse_command *cmd, struct cmd_parse_input *pi, struct cmd_parse_result *pr) { struct cmd_parse_argument *arg; - struct cmd_list *cmdlist = NULL; struct cmd *add; char *cause; struct args_value *values = NULL; u_int count = 0, idx; - if (cmd_parse_expand_alias(cmd, pi, pr, &cmdlist)) - return (cmdlist); + memset(pr, 0, sizeof *pr); + + if (cmd_parse_expand_alias(cmd, pi, pr)) + return; TAILQ_FOREACH(arg, &cmd->arguments, entry) { values = xrecallocarray(values, count, count + 1, @@ -844,14 +838,14 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, free(cause); goto out; } - cmdlist = cmd_list_new(); - cmd_list_append(cmdlist, add); + pr->status = CMD_PARSE_SUCCESS; + pr->cmdlist = cmd_list_new(); + cmd_list_append(pr->cmdlist, add); out: for (idx = 0; idx < count; idx++) args_free_value(&values[idx]); free(values); - return (cmdlist); } static void @@ -860,9 +854,11 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, { struct cmd_parse_command *cmd; u_int line = UINT_MAX; - struct cmd_list *current = NULL, *result, *add; + struct cmd_list *current = NULL, *result; char *s; + memset(pr, 0, sizeof *pr); + /* Check for an empty list. */ if (TAILQ_EMPTY(cmds)) { pr->status = CMD_PARSE_SUCCESS; @@ -891,14 +887,14 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, current = cmd_list_new(); line = pi->line = cmd->line; - add = cmd_parse_build_command(cmd, pi, pr); - if (add == NULL) { + cmd_parse_build_command(cmd, pi, pr); + if (pr->status != CMD_PARSE_SUCCESS) { cmd_list_free(result); cmd_list_free(current); return; } - cmd_list_append_all(current, add); - cmd_list_free(add); + cmd_list_append_all(current, pr->cmdlist); + cmd_list_free(pr->cmdlist); } if (current != NULL) { cmd_parse_print_commands(pi, current); @@ -1061,6 +1057,7 @@ cmd_parse_from_arguments(struct args_value *values, u_int count, memset(&input, 0, sizeof input); pi = &input; } + memset(&pr, 0, sizeof pr); cmds = cmd_parse_new_commands(); diff --git a/menu.c b/menu.c index 7fb1996b..d7094f89 100644 --- a/menu.c +++ b/menu.c @@ -381,7 +381,7 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px, cmd_find_copy_state(&md->fs, fs); screen_init(&md->s, menu->width + 4, menu->count + 2, 0); if (~md->flags & MENU_NOMOUSE) - md->s.mode |= MODE_MOUSE_ALL; + md->s.mode |= MODE_MOUSE_BUTTON; md->s.mode &= ~MODE_CURSOR; md->px = px; diff --git a/screen.c b/screen.c index a7801f45..dcbae278 100644 --- a/screen.c +++ b/screen.c @@ -677,9 +677,9 @@ screen_mode_to_string(int mode) if (mode & MODE_WRAP) strlcat(tmp, "WRAP,", sizeof tmp); if (mode & MODE_MOUSE_STANDARD) - strlcat(tmp, "STANDARD,", sizeof tmp); + strlcat(tmp, "MOUSE_STANDARD,", sizeof tmp); if (mode & MODE_MOUSE_BUTTON) - strlcat(tmp, "BUTTON,", sizeof tmp); + strlcat(tmp, "MOUSE_BUTTON,", sizeof tmp); if (mode & MODE_BLINKING) strlcat(tmp, "BLINKING,", sizeof tmp); if (mode & MODE_MOUSE_UTF8) @@ -691,7 +691,7 @@ screen_mode_to_string(int mode) if (mode & MODE_FOCUSON) strlcat(tmp, "FOCUSON,", sizeof tmp); if (mode & MODE_MOUSE_ALL) - strlcat(tmp, "ALL,", sizeof tmp); + strlcat(tmp, "MOUSE_ALL,", sizeof tmp); if (mode & MODE_ORIGIN) strlcat(tmp, "ORIGIN,", sizeof tmp); if (mode & MODE_CRLF) From 34312fd6ee8992ce8d13ec63b074466d5f624acc Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Sep 2021 13:38:32 +0000 Subject: [PATCH 0977/1006] Expand argument to run-shell again. --- arguments.c | 5 +++-- cmd-confirm-before.c | 2 +- cmd-if-shell.c | 8 ++++---- cmd-run-shell.c | 2 +- tmux.h | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/arguments.c b/arguments.c index 20d5e7ba..1555c2f0 100644 --- a/arguments.c +++ b/arguments.c @@ -651,13 +651,14 @@ args_string(struct args *args, u_int idx) /* Make a command now. */ struct cmd_list * -args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx) +args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx, + int expand) { struct args_command_state *state; char *error; struct cmd_list *cmdlist; - state = args_make_commands_prepare(self, item, idx, NULL, 0, 0); + state = args_make_commands_prepare(self, item, idx, NULL, 0, expand); cmdlist = args_make_commands(state, 0, NULL, &error); if (cmdlist == NULL) { cmdq_error(item, "%s", error); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 8f50ba2d..95841962 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -72,7 +72,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) int wait = !args_has(args, 'b'); cdata = xcalloc(1, sizeof *cdata); - cdata->cmdlist = args_make_commands_now(self, item, 0); + cdata->cmdlist = args_make_commands_now(self, item, 0, 0); if (cdata->cmdlist == NULL) return (CMD_RETURN_ERROR); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 907a96e1..9cdcfc51 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -84,9 +84,9 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) shellcmd = format_single_from_target(item, args_string(args, 0)); if (args_has(args, 'F')) { if (*shellcmd != '0' && *shellcmd != '\0') - cmdlist = args_make_commands_now(self, item, 1); + cmdlist = args_make_commands_now(self, item, 1, 0); else if (count == 3) - cmdlist = args_make_commands_now(self, item, 2); + cmdlist = args_make_commands_now(self, item, 2, 0); else { free(shellcmd); return (CMD_RETURN_NORMAL); @@ -101,11 +101,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata = xcalloc(1, sizeof *cdata); - cdata->cmd_if = args_make_commands_now(self, item, 1); + cdata->cmd_if = args_make_commands_now(self, item, 1, 0); if (cdata->cmd_if == NULL) return (CMD_RETURN_ERROR); if (count == 3) { - cdata->cmd_else = args_make_commands_now(self, item, 2); + cdata->cmd_else = args_make_commands_now(self, item, 2, 0); if (cdata->cmd_else == NULL) return (CMD_RETURN_ERROR); } diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 662312fb..7d672f85 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -132,7 +132,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) if (cmd != NULL) cdata->cmd = format_single_from_target(item, cmd); } else { - cdata->cmdlist = args_make_commands_now(self, item, 0); + cdata->cmdlist = args_make_commands_now(self, item, 0, 1); if (cdata->cmdlist == NULL) return (CMD_RETURN_ERROR); } diff --git a/tmux.h b/tmux.h index ca67fd03..7a77cdae 100644 --- a/tmux.h +++ b/tmux.h @@ -2234,7 +2234,7 @@ struct args_value *args_values(struct args *); struct args_value *args_value(struct args *, u_int); const char *args_string(struct args *, u_int); struct cmd_list *args_make_commands_now(struct cmd *, struct cmdq_item *, - u_int); + u_int, int); struct args_command_state *args_make_commands_prepare(struct cmd *, struct cmdq_item *, u_int, const char *, int, int); struct cmd_list *args_make_commands(struct args_command_state *, int, char **, From a0b39dba3145d46dabdceca14bd88a6c10ce590d Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Sep 2021 19:37:17 +0000 Subject: [PATCH 0978/1006] Turn on both button and all mouse modes for menus since some terminals only support the former. --- menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu.c b/menu.c index d7094f89..043dafdd 100644 --- a/menu.c +++ b/menu.c @@ -381,7 +381,7 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px, cmd_find_copy_state(&md->fs, fs); screen_init(&md->s, menu->width + 4, menu->count + 2, 0); if (~md->flags & MENU_NOMOUSE) - md->s.mode |= MODE_MOUSE_BUTTON; + md->s.mode |= (MODE_MOUSE_ALL|MODE_MOUSE_BUTTON); md->s.mode &= ~MODE_CURSOR; md->px = px; From 768fb9080ff757fc05c3af05e596083e2ba5851f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 9 Sep 2021 21:55:03 +0000 Subject: [PATCH 0979/1006] Keep -? as usage. --- arguments.c | 4 ++++ cmd.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arguments.c b/arguments.c index 1555c2f0..985aadc0 100644 --- a/arguments.c +++ b/arguments.c @@ -153,6 +153,10 @@ args_parse(const struct args_parse *parse, struct args_value *values, flag = *string++; if (flag == '\0') break; + if (flag == '?') { + args_free(args); + return (NULL); + } if (!isalnum(flag)) { xasprintf(cause, "invalid flag -%c", flag); args_free(args); diff --git a/cmd.c b/cmd.c index 5647a861..123fd366 100644 --- a/cmd.c +++ b/cmd.c @@ -502,7 +502,7 @@ cmd_parse(struct args_value *values, u_int count, const char *file, u_int line, const struct cmd_entry *entry; struct cmd *cmd; struct args *args; - char *error; + char *error = NULL; if (count == 0 || values[0].type != ARGS_STRING) { xasprintf(cause, "no command"); From 5cdc1bdd32f1500beb01bfc632e314d784a07082 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Sep 2021 08:52:46 +0000 Subject: [PATCH 0980/1006] Disable aliases inside aliases for the moment. --- cmd-parse.y | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index b75a4ad9..1d692770 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -749,6 +749,8 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd, struct cmd_parse_command *last; char *alias, *name, *cause; + if (pi->flags & CMD_PARSE_NOALIAS) + return (0); memset(pr, 0, sizeof *pr); first = TAILQ_FIRST(&cmd->arguments); @@ -786,9 +788,11 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd, TAILQ_REMOVE(&cmd->arguments, arg, entry); TAILQ_INSERT_TAIL(&last->arguments, arg, entry); } - cmd_parse_log_commands(cmds, __func__); + cmd_parse_log_commands(cmds, __func__); + pi->flags |= CMD_PARSE_NOALIAS; cmd_parse_build_commands(cmds, pi, pr); + pi->flags &= ~CMD_PARSE_NOALIAS; return (1); } @@ -865,7 +869,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, pr->cmdlist = cmd_list_new(); return; } - cmd_parse_log_commands(cmds, __func__); + cmd_parse_log_commands(cmds, __func__); /* * Parse each command into a command list. Create a new command list @@ -1422,7 +1426,7 @@ yylex_token_escape(char **buf, size_t *len) if (o3 >= '0' && o3 <= '7') { ch = 64 * (ch - '0') + 8 * (o2 - '0') + - (o3 - '0'); + (o3 - '0'); yylex_append1(buf, len, ch); return (1); } From 3b10392bfce384abb2734e9a7052718b7d70f212 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 10 Sep 2021 15:00:37 +0100 Subject: [PATCH 0981/1006] Icons, from someone on GitHub in issue 2870. --- logo/icons/128x128/tmux.png | Bin 0 -> 1853 bytes logo/icons/16x16/tmux.png | Bin 0 -> 791 bytes logo/icons/24x24/tmux.png | Bin 0 -> 901 bytes logo/icons/32x32/tmux.png | Bin 0 -> 933 bytes logo/icons/48x48/tmux.png | Bin 0 -> 1110 bytes logo/icons/64x64/tmux.png | Bin 0 -> 1292 bytes logo/icons/96x96/tmux.png | Bin 0 -> 1601 bytes 7 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 logo/icons/128x128/tmux.png create mode 100644 logo/icons/16x16/tmux.png create mode 100644 logo/icons/24x24/tmux.png create mode 100644 logo/icons/32x32/tmux.png create mode 100644 logo/icons/48x48/tmux.png create mode 100644 logo/icons/64x64/tmux.png create mode 100644 logo/icons/96x96/tmux.png diff --git a/logo/icons/128x128/tmux.png b/logo/icons/128x128/tmux.png new file mode 100644 index 0000000000000000000000000000000000000000..1999ee9cf3c857a28341e2fea691174aca04dfb1 GIT binary patch literal 1853 zcmeAS@N?(olHy`uVBq!ia0y~yU}ykg4rT@hhA$5${$yZaU`coMb!1@J*w6hZk(Ggg zK_S^A$d`ekN{xY`p@o6r7fAgJ28L1t28LG&3=CE?7#PG0=IjczVPIf-9pDq<%D}*o zoSdAWpI=#7Syon-nVFfDl@%Ks8y6Sn;^JasV`FV?ZDnN@7#O%^%a%89-u(Uh_vq21 zsi~Z(d$rUU+!8gM&kOcz9l3UVndoetv#PNJw5@UPwsD;>C*t0|NsB0#>Y8k&uw! z>FG(lXJtX2wU^ncP;s?NeUJ962XoWUXPOJ`?f%UF|NeU(^QphywB+&f?%TcI z$NfBy`?lZe+WKPqqK8WhE)}~U^Kw7t-Ey;i$IIQ5?@h0}))0Fp!Q+@u(~Z_cZ;tML zwXf%Pujerzm!lrdH(JksxcK+q?=?@?PP;dw^H#U_aewta+R{7arFO_E?pD>?XQZ%8 zCH+Fy*>o;%S{Qv*|&!0a>jvP69^ysNmr@FejAaR$M zmuF&PVr^w@Wnty*>AiOK+B>)JJbLix#IX~F`Gpo{7B*HkM$sll(Z+_6Ms{iTZR^|T zpI9*W*u0sCW<@nc8$=iyN1K>Ln?h2N%qDs1P4Y6E6%==jN$rqxIqELIOIdn{ywWbUpp#(|+ht^SDA*iwP~NRBxkJ`qpNZT~CE1;dGCLKt z_UakzH?cTqt-eRw^nk^`SF_a^7#L=i1o;Is5DgevDrV0_0loZ;F=syizF05F$`t+S z`^y-{n9s3qnHMc$j{BRA3DV;JE)`~8bcR3n?XLzV#+c848`zklKmDoa0Gq;sY(XzS zqrR|I90LObW0JSKi&_-NjI9g|4D2PIzOL*~IoY|)_Jg9p( zXQplXy~47-qIv(HS3m!IcJA)e1{{hl0;dARzGq#{zOrWN=MNW7t}L6o>+37?j2j-; zlIDK6a?H2HceZKy2Fv>sGcRts`b<}F&A|hwPVOkZ{q4jG*^8URHeXx5E${NS*lPEe zKmF#(TD=pQc2dTl?T@0w4Zpd&E37=3_Ux%$o0|GOgjT zkMFFWur+(T#I2g84A%rM?{zm5RM=hL5UAZ?c)-4$a@oUJ z&z#U4^Ut;++n{t`5kodd*;FgWw+v=>j2IF7XL&>H?%pp|&q8PD<*IRRD8Icene>geh;szpIQ_5y#G2|(bc74k@L=!Dx34#H;(R= z4fA2qu(N4>ZqjMKzuJCL3r9fFlNJ28IDYicck9v+?$6Zt)Ml z?^J%)Hn{irap_dg+EqHy`;wlny85uHcE_)mU$d{R zi{793c-bl*Cl19Hqi@m&{%FP+eEV6>z`&qd;u=wsl30>zm0XmXSdz+MWME{dYhbBs zU>;&cF6*2UngDD+AHVQUNSs54@ z6p}rHd>I(3)EF2VS{N99F)%PRykKA`HDF+PmB7GYHG_dcykO3*KpO@IhW`ORA+8Jz z44$5zE-o(Y?CcEZ85q_vxSsUzI^*l-=jY(yU~O$}V`F1!X<1ZMl#q~MXJ_Z-<>lbu zU}Iy0CgtGZU~6j&5r;^5fI%}D*nxqKm5q&+jg^&^Ww@njsF``FgEH^hd z4-b#Bva%U7W;8W5H8(dWCMMe1+1XgxSXx*X6&5ALCs2FsElKTvQofB=t<9tdc0*S~uU0=3Mm;gESXGxO@z%wU<qmhwh$Y=9n_&A=9=O zX%0CPC#41ieq~|O&zFxESi9=jf^`p{zke?;AR)rDzH)3F5P%UwdC`m~yNwrEYN=+lx~qh>B><0Y!_atDnm{r-UW|`Fr_R literal 0 HcmV?d00001 diff --git a/logo/icons/24x24/tmux.png b/logo/icons/24x24/tmux.png new file mode 100644 index 0000000000000000000000000000000000000000..a9a8c99993176fcb3f20599b95ef6089d07dd3ac GIT binary patch literal 901 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4rT@hhU+WOo?>8NU`coMb!1@J*w6hZk(Ggg zK_S^A$d`ekN{xY`p@o6r7Xt%B!wUw6QUeBtR|yOZRx=nF#0%!^3bbKhV6+SH32|j$ zU|6+kmAALIv$M0GpP#+Gy|uNqjg5_^rDaJ;NoQxLjkS%nt+h^{zUw4+`_sI+#V&V-EtB; zWhHmXO6`_Y*sEf8)M~=}DQV|3jSiSe?U0k+DQ|YrGW%j4rZcj#vdqlPCQX_&ZQ8V= zqN0+LlF5@NFI~E{y}cdoaZ78F666=mfCccIn1g{K5BJ@3f z-L0rKJULdo{=E?Tx63Mr=S|hG8A89R-iUa$w6HlhG%B)ha*3#LQDKpgELQS%aXo&- zsi9L-JkTxh@=gt*J891f1sEnZ7@a-4L$X3g=dq%KqM@ebsV!R`BtF=>$*-~L%9aui z<-^CO^*7!>ckiGe)0{bqTe?@z-p$awU_$%y>E=csK7IQrs%G13z`zjIR(*9=!_5>1 z1_sp<*NBpo#FA92cptHiA#sPCRW0|SF7$cEtjw370~qEv>6fW*wa5(Q&DLp>8w5v@6(i1c*zb6Mw< G&;$V0*fuHv literal 0 HcmV?d00001 diff --git a/logo/icons/32x32/tmux.png b/logo/icons/32x32/tmux.png new file mode 100644 index 0000000000000000000000000000000000000000..2fa700cc564328d23fe4af5024833e8a09d505b0 GIT binary patch literal 933 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tGSkfJR9T^xl_H+M9WCckk zdj$D1FjT2AFf_C({R@E-tpTw6w9Zv9YuFDSf8X6iF7Ut&WW@l%Isle9O z*45S3$;rvq))uFw3C5O5R+>9?WVb1*?$Go;?B{ekdpwMQGo8X6~D666=mfDH)QIDmmUkNDbe-@)M1vbcYla1i(J zn?G3UQ!gh(u{n3PjKE9=1_s6?Z+Dm6-E0hN85kJYOFVsD*`IQ$EQQ_$V?97~=nwgS?8qrQ+eVi)ib=EIfv1APk+bOM8S+BeTwry%# zW@8+{*gb9A^y%g-6+eIc`dNLrVfr>P?f}Nsed10Wu8rHL^Jxb#&R%D($9iv9PIHK3 zgE&*dXV(SW`I%m9T&&H(#mC9(%uvh6!@BUH;6#pu1D%NA7rhgm9$4nr#e*#syQi_Uf7? zq`!Zl!E#FCkl#7Ib$Jp8uHHQy+@fbG&X68?_KD*h9ZLoV2GtVRh?11Vl2ohYqSVBa zR0bmhBST#SOI-u=5JLkiQwu92Lu~^CD+2@N%W0EQH00)|WTsW())3TpPoIH-K@?;| oaDG}zd16s2LqtGgW?qScv7VuxiKvLy98lzXy85}Sb4q9e0DFTs7ytkO literal 0 HcmV?d00001 diff --git a/logo/icons/48x48/tmux.png b/logo/icons/48x48/tmux.png new file mode 100644 index 0000000000000000000000000000000000000000..dc19bcdd5dea0706fc6fdca0a4058a9a6db45546 GIT binary patch literal 1110 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_SkfJR9T^xl_H+M9WMyDr zP)PO&@?~JCQe$9fXklRZ#lXPO@PdJ%)PRBERRRNp)eHs(@q#(K0&N%=80Q7}gt#&= zF#P}jUt3#SMnFZ@(w-<^ra-X7Q8XPAGvSbV#5*X^FTxVXT;KnDj0h;}P0tI*KUn>TNEc6M4? zTH4syxVgC{B_+ki#(H{sIygAk*w|QGTcatnv$Jz|caH@FcXxMZXJ=bmTWc_|0fTv9 za1IPS!NA7S2JS+=90TKA6Wc;N&AqyEI~7e1Sma(RNIsWpe85a)w?@dRi2wipN1usv zIO43nM@wd>g47N<>7DYjI~8^J8G0V~X}-}m>F(t4(@~1MR3TCtdvp@drA)s+6RT@2 zEG$AoLT=rX+F?b~E#&cX6xHk+qWEQi=G*yo24hULr!X^oXk!I>7DYDJ7oX=|DSYCHcNI67~hsPXUk*T4i-`fa)Z z*n|)#Zl=w-Ees3{j7i?^E{y+~bnh`RFtC?+`ns||)0NE|R%K^oWo9cf%EsE(GN&*u*s^e< zp*D8}^X$${udFOCBM9peV{4@u+{tn~)%rCr_R|6<@$C9j$jqimSWJ`$FSf zTXQ+}snez|W@dMnd-XGMgWH`IZ3ovjUO%~$i8mxH^!k-W++h~GKQx+t*uCrO!p2{1 z$Cjm)9JDIuXWYQ%=)ZniSjmMZ*AuthIo3GdxW0bv(xtMZTedCw7;W{3naTBll*`ek zCTFL^hdwrL_|S1;MMh!7QVl_2Y4Q0D{72MP)KtCsO5WNgFfjbz8`-NL8wtv#swJ)w zB`Jv|saDBFsfi`23`PbQc{wyudj`bjkUFPYisL=4;5o{^9 zUNQVYl+9uLj@#XN7Yp2vdC$B*=jx|x_J^H1Z}rT1Fn7Y;$uEDry8rcIVPRo@e!ho? zhn1C;jg5_^rDc76{f7@9=FXjKZEbC5XXo$lUr|w!n3xzG9PH)gWp8h9ZEcOF(#FQd z(b3W0-#;ZKB_kul&(F`^-rm>OHzOk>J3HIg*Vo6#$KKxF1`MphzzPgn!QcZJOalWO zD;tO}OoPnKgDoI_kzJ>#zr`r*K&16yJEh%fuE#vO?({U@Xth7=WVGL`;Bs-sg>2`e z?uPqKBhSRFdAe@ygL!G^GaZgN%k5H<+94;sQ(ktbqVjHa@8kYOmrEz#o%Z(Uy9Eyy zY3$LF+#xHyQ(kI^oYHPJ^MlswpKbc__ZP&e*4Ea@p-uD=Vw_@89p;ySJ^a zt+loF>KZ=0|1fXPJkuaE;{cQFj-0KRw>8ddlwGGNyG}uA zi<-tR9hI#bGV2ticFIZakdxV^AiGl$;z-FIvTA#@$j+E~=yoRq0|R4{x4X--?Hi=0GB7Z(mw5WRvOncy=Q80~^ryv( zfq^l{)5S3);&5_;1Zy*!!6|JGEzM*G(NNRUR9>G#USHi@wUobStaerXVr!E)aN@?1 zD+1j+8fWfoRppMc)}B4JwA9x(mzQnD>imo&YJ4na+sbm*1Tf!tD?2M|qt@YEhuqxB z&9>H882H@P-)wtO?C868vNHP>2ETOiBA$Z6XDw0(p1ulm-o_j!ovk?e=yF@sjTknD*7}acOnb z)pMws=(ds3*;cVv${_2&3dRzXhV%9d#I0Yvo^P;GT%6;>^Jb|7@)9PT)-!BUUv2T+ z;ZV@QaE0OEg$*xn9PF97IqmU;S$66+P8?Yc1u0izViM&1X8ycrs1(5LlKfaqXU^^p zl??@jHBB=tt}t+(JhDW!TTC#YCE)3$CsRT+UD}Zovf)(MW==q@hncbz`!6|8g3@+F{OxsfkCyzHKHUXu_VnC}Q!>*kacfwiE0oQ^z#s~;Aviy+q&%@G hl_4S^F*C13!C22w&qP#2YYr%3db;|#taD0e0sw!-?g;<@ literal 0 HcmV?d00001 diff --git a/logo/icons/96x96/tmux.png b/logo/icons/96x96/tmux.png new file mode 100644 index 0000000000000000000000000000000000000000..5707b808e80ec15e207892a11a8c1f408a88b101 GIT binary patch literal 1601 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4rT@hhO2JvAPJUqM_)$E)e-c?47#I|i zJ%W507^>757#dm_7=AG@Ff_biU???UV0e|lz+g3lfkC`r&aOZk1_q}30G|+71_p+> zxVZTE_^PU^f`WpgqN2dSz<_`NhHwUtW8TZ3tekRpTIJQ6(km4i7jpjp|G)In^7IQ? z^Byjkc7G;BV^C0#ot>SvwY80njise!TwL6_bLW2l{=IwmZeL$tD=RBoTU&pB|FpEU z;^N|%n3$lTATKX37Z(>B8yjnDYphyqY;2sJo&Ej&V`5_J>gt-Cn-dZeJUl$y+}sip z5~`}I;^X68U0tK2qut!xqN1W|YHIxa{Az1!ot&KP?Cfm7z#0rJ!5|F`E`h;sFxUbH z9$;Vv1~yhUHdZz!;ie|xrWO&FX5r>?8x&AKy!;NhZ{o9&hdZDn^V zO6`!7-YKuROI2&Hp2HEB>Gx+%y*ICKxrFJ8QO>C&Zt|NiaRv17}YEqnLwjf{+RcX#*j@bK~Rv9Pf4_4Bo{vavL` zjE{{!d;09>kDvGK+T-WrXJuh!5^iD?YV2PWF!#XRWv7-;-ZsT4)!8`Q#4OxgW`lzK zCMDIKnrgeWRCZ{{Zd6p+q#?Ca4&r|0J?b($uM z|Nr-9Js8ixz%ZdC$S;@y3sAAMMFM8*yj9o!f};;i{Q0=hf|G0FpLY{@CjIgJ?a8u@ zh4nB9f(Z}X>E{2;73=Hfgp1!W^PdVAS%$V6F>>U^wn3z0W978hhB_~L*E=~~P(LSJ| zq@|{(*vanUdHTeuliCl0l{6QwSh3_x%h8H7(NNP;USC~b8;5B3aR2)U1m+7ZV01gC zwk++;i)Wi>L`V3|uqb?_Qj_p$%F4^j&d=x!Kf9!JvYOw9O=jtirt_~JyVlq6@<F^K%Ii3c`g_JE{QNGoV1y{i*;R|%MAY}*2!wS zb}ou|@XoPEPe^CRlZSmtjmyQi>VEjNWIex|uEf@aD@}piaK;6l8K%|eQj!|B^H++U znQLBu&?%{5z5Gluov1x2nuZ7L8*I3zot^6@!@;9;ncaKZdIc_)_6d?FwZqrOIrDJv zDx1$-7i&IQLO{+i;`6~3tc%5Zq71T-8HbNGeNBlJklfnHv7zwtGu`Ozx$15&+g5mp za4$+O6JRWRcWZC@`+aj=R@|vqFylMg^rNNuNP}~Gzl^Qhlv9s8JvGEsmd{b(nq%WR zXIJp@;L8Cz9_KBJKZ$%jaB}m^NEr>Wg0$yz7SH2VKR@^U*~d;B8?5&)_;BZ>@D+o< zhfYp@t|un?*nIv>^HVa@DsgLA yp(~Wlz`!61vLQG>t)x7$D3u{1ATcwqM8R0kP|rkEL~9Nx4|%%!xvX Date: Fri, 10 Sep 2021 14:22:24 +0000 Subject: [PATCH 0982/1006] Get rid of the last two warnings by turning them off around the problem statements, if the compiler supports it. --- log.c | 4 ++-- tmux.h | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/log.c b/log.c index ec54711a..d4808427 100644 --- a/log.c +++ b/log.c @@ -147,7 +147,7 @@ fatal(const char *msg, ...) va_start(ap, msg); if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) exit(1); - log_vwrite(fmt, ap); + no_format_nonliteral(log_vwrite(fmt, ap)); va_end(ap); exit(1); } @@ -162,7 +162,7 @@ fatalx(const char *msg, ...) va_start(ap, msg); if (asprintf(&fmt, "fatal: %s", msg) == -1) exit(1); - log_vwrite(fmt, ap); + no_format_nonliteral(log_vwrite(fmt, ap)); va_end(ap); exit(1); } diff --git a/tmux.h b/tmux.h index 7a77cdae..6c7cf046 100644 --- a/tmux.h +++ b/tmux.h @@ -93,6 +93,20 @@ struct winlink; #define DEFAULT_XPIXEL 16 #define DEFAULT_YPIXEL 32 +/* Don't complain about format arguments. */ +#if __clang__ || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#define no_format_nonliteral(x) do { \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wformat-nonliteral\"") \ + x; \ + _Pragma ("GCC diagnostic pop") \ +} while (0) +#else +#define no_format_nonliteral(x) do { \ + x; \ +} while (0) +#endif + /* Attribute to make GCC check printf-like arguments. */ #define printflike(a, b) __attribute__ ((format (printf, a, b))) From e6b40cb339e06b9084a9139a75d62fb7a6005448 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Sep 2021 15:03:18 +0000 Subject: [PATCH 0983/1006] Do fatal/fatalx a different way so the compiler trick to avoid warnings becomes unnecessary, prompted by theo. --- log.c | 35 +++++++++++++++++------------------ tmux.h | 14 -------------- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/log.c b/log.c index d4808427..abc097dc 100644 --- a/log.c +++ b/log.c @@ -99,28 +99,27 @@ log_close(void) /* Write a log message. */ static void printflike(1, 0) -log_vwrite(const char *msg, va_list ap) +log_vwrite(const char *msg, va_list ap, const char *prefix) { - char *fmt, *out; + char *s, *out; struct timeval tv; if (log_file == NULL) return; - if (vasprintf(&fmt, msg, ap) == -1) + if (vasprintf(&s, msg, ap) == -1) return; - if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) { - free(fmt); + if (stravis(&out, s, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) { + free(s); return; } + free(s); gettimeofday(&tv, NULL); - if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec, - (int)tv.tv_usec, out) != -1) + if (fprintf(log_file, "%lld.%06d %s%s\n", (long long)tv.tv_sec, + (int)tv.tv_usec, prefix, out) != -1) fflush(log_file); - free(out); - free(fmt); } /* Log a debug message. */ @@ -133,7 +132,7 @@ log_debug(const char *msg, ...) return; va_start(ap, msg); - log_vwrite(msg, ap); + log_vwrite(msg, ap, ""); va_end(ap); } @@ -141,14 +140,16 @@ log_debug(const char *msg, ...) __dead void fatal(const char *msg, ...) { - char *fmt; + char tmp[256]; va_list ap; + if (snprintf(tmp, sizeof tmp, "fatal: %s: ", strerror(errno)) < 0) + exit (1); + va_start(ap, msg); - if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) - exit(1); - no_format_nonliteral(log_vwrite(fmt, ap)); + log_vwrite(msg, ap, tmp); va_end(ap); + exit(1); } @@ -156,13 +157,11 @@ fatal(const char *msg, ...) __dead void fatalx(const char *msg, ...) { - char *fmt; va_list ap; va_start(ap, msg); - if (asprintf(&fmt, "fatal: %s", msg) == -1) - exit(1); - no_format_nonliteral(log_vwrite(fmt, ap)); + log_vwrite(msg, ap, "fatal: "); va_end(ap); + exit(1); } diff --git a/tmux.h b/tmux.h index 6c7cf046..7a77cdae 100644 --- a/tmux.h +++ b/tmux.h @@ -93,20 +93,6 @@ struct winlink; #define DEFAULT_XPIXEL 16 #define DEFAULT_YPIXEL 32 -/* Don't complain about format arguments. */ -#if __clang__ || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#define no_format_nonliteral(x) do { \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wformat-nonliteral\"") \ - x; \ - _Pragma ("GCC diagnostic pop") \ -} while (0) -#else -#define no_format_nonliteral(x) do { \ - x; \ -} while (0) -#endif - /* Attribute to make GCC check printf-like arguments. */ #define printflike(a, b) __attribute__ ((format (printf, a, b))) From ddcf5b801a50ecaf061e8921bad5bde83ee81b9e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 13 Sep 2021 07:57:15 +0100 Subject: [PATCH 0984/1006] Mention FAQ, from Illia Bobyr. --- README | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README b/README index 730b9a30..8732a027 100644 --- a/README +++ b/README @@ -55,6 +55,14 @@ source tree with: A small example configuration is in example_tmux.conf. +Other documentation is available in the wiki: + + https://github.com/tmux/tmux/wiki + +Also see the tmux FAQ at: + + https://github.com/tmux/tmux/wiki/FAQ + A bash(1) completion file is at: https://github.com/imomaliev/tmux-bash-completion From a19cac5c46189c7c6eb4a6f4ccc7336d54676dba Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Sep 2021 07:38:30 +0000 Subject: [PATCH 0985/1006] For the moment, restore if-shell and run-shell to parsing at the last moment (when the shell command completes) rather than when first invoked, GitHub issue 2872. --- cmd-if-shell.c | 54 ++++++++++++++++++++++++++++--------------------- cmd-run-shell.c | 54 +++++++++++++++++++++++++++---------------------- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 9cdcfc51..211e08d6 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -52,11 +53,11 @@ const struct cmd_entry cmd_if_shell_entry = { }; struct cmd_if_shell_data { - struct cmd_list *cmd_if; - struct cmd_list *cmd_else; + struct args_command_state *cmd_if; + struct args_command_state *cmd_else; - struct client *client; - struct cmdq_item *item; + struct client *client; + struct cmdq_item *item; }; static enum args_parse_type @@ -78,8 +79,9 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) char *shellcmd; struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; - struct cmd_list *cmdlist = NULL; + struct cmd_list *cmdlist; u_int count = args_count(args); + int wait = !args_has(args, 'b'); shellcmd = format_single_from_target(item, args_string(args, 0)); if (args_has(args, 'F')) { @@ -101,25 +103,21 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata = xcalloc(1, sizeof *cdata); - cdata->cmd_if = args_make_commands_now(self, item, 1, 0); - if (cdata->cmd_if == NULL) - return (CMD_RETURN_ERROR); + cdata->cmd_if = args_make_commands_prepare(self, item, 1, NULL, wait, + 0); if (count == 3) { - cdata->cmd_else = args_make_commands_now(self, item, 2, 0); - if (cdata->cmd_else == NULL) - return (CMD_RETURN_ERROR); + cdata->cmd_else = args_make_commands_prepare(self, item, 2, + NULL, wait, 0); } - if (!args_has(args, 'b')) + if (wait) { cdata->client = cmdq_get_client(item); - else + cdata->item = item; + } else cdata->client = tc; if (cdata->client != NULL) cdata->client->references++; - if (!args_has(args, 'b')) - cdata->item = item; - if (job_run(shellcmd, 0, NULL, s, server_client_get_cwd(cmdq_get_client(item), s), NULL, cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1, @@ -131,7 +129,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) } free(shellcmd); - if (args_has(args, 'b')) + if (!wait) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } @@ -142,18 +140,28 @@ cmd_if_shell_callback(struct job *job) struct cmd_if_shell_data *cdata = job_get_data(job); struct client *c = cdata->client; struct cmdq_item *item = cdata->item, *new_item; + struct args_command_state *state; struct cmd_list *cmdlist; + char *error; int status; status = job_get_status(job); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - cmdlist = cdata->cmd_else; + state = cdata->cmd_else; else - cmdlist = cdata->cmd_if; - if (cmdlist == NULL) + state = cdata->cmd_if; + if (state == NULL) goto out; - if (item == NULL) { + cmdlist = args_make_commands(state, 0, NULL, &error); + if (cmdlist == NULL) { + if (cdata->item == NULL) { + *error = toupper((u_char)*error); + status_message_set(c, -1, 1, 0, "%s", error); + } else + cmdq_error(cdata->item, "%s", error); + free(error); + } else if (item == NULL) { new_item = cmdq_get_command(cmdlist, NULL); cmdq_append(c, new_item); } else { @@ -175,8 +183,8 @@ cmd_if_shell_free(void *data) server_client_unref(cdata->client); if (cdata->cmd_else != NULL) - cmd_list_free(cdata->cmd_else); - cmd_list_free(cdata->cmd_if); + args_make_commands_free(cdata->cmd_else); + args_make_commands_free(cdata->cmd_if); free(cdata); } diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 7d672f85..537a5e5f 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -54,15 +54,15 @@ const struct cmd_entry cmd_run_shell_entry = { }; struct cmd_run_shell_data { - struct client *client; - char *cmd; - struct cmd_list *cmdlist; - char *cwd; - struct cmdq_item *item; - struct session *s; - int wp_id; - struct event timer; - int flags; + struct client *client; + char *cmd; + struct args_command_state *state; + char *cwd; + struct cmdq_item *item; + struct session *s; + int wp_id; + struct event timer; + int flags; }; static enum args_parse_type @@ -132,9 +132,8 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) if (cmd != NULL) cdata->cmd = format_single_from_target(item, cmd); } else { - cdata->cmdlist = args_make_commands_now(self, item, 0, 1); - if (cdata->cmdlist == NULL) - return (CMD_RETURN_ERROR); + cdata->state = args_make_commands_prepare(self, item, 0, NULL, + wait, 1); } if (args_has(args, 't') && wp != NULL) @@ -179,8 +178,10 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) struct client *c = cdata->client; const char *cmd = cdata->cmd; struct cmdq_item *item = cdata->item, *new_item; + struct cmd_list *cmdlist; + char *error; - if (cdata->cmdlist == NULL && cmd != NULL) { + if (cdata->state == NULL && cmd != NULL) { if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL, cmd_run_shell_callback, cmd_run_shell_free, cdata, cdata->flags, -1, -1) == NULL) @@ -188,15 +189,20 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) return; } - if (cdata->cmdlist != NULL) { - if (item == NULL) { - new_item = cmdq_get_command(cdata->cmdlist, NULL); - cmdq_append(c, new_item); - } else { - new_item = cmdq_get_command(cdata->cmdlist, - cmdq_get_state(item)); - cmdq_insert_after(item, new_item); - } + cmdlist = args_make_commands(cdata->state, 0, NULL, &error); + if (cmdlist == NULL) { + if (cdata->item == NULL) { + *error = toupper((u_char)*error); + status_message_set(c, -1, 1, 0, "%s", error); + } else + cmdq_error(cdata->item, "%s", error); + free(error); + } else if (item == NULL) { + new_item = cmdq_get_command(cmdlist, NULL); + cmdq_append(c, new_item); + } else { + new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } if (cdata->item != NULL) @@ -264,8 +270,8 @@ cmd_run_shell_free(void *data) session_remove_ref(cdata->s, __func__); if (cdata->client != NULL) server_client_unref(cdata->client); - if (cdata->cmdlist != NULL) - cmd_list_free(cdata->cmdlist); + if (cdata->state != NULL) + args_make_commands_free(cdata->state); free(cdata->cwd); free(cdata->cmd); free(cdata); From adb620c4e4f11d1d63242192b21f764a118397e0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 Sep 2021 13:35:08 +0100 Subject: [PATCH 0986/1006] Update CHANGES. --- CHANGES | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 40ecee72..ceefc215 100644 --- a/CHANGES +++ b/CHANGES @@ -1,10 +1,15 @@ CHANGES FROM 3.2a TO 3.3 +* Change so that {} is converted to tmux commands immediately when parsed. This + means it must contain valid tmux commands. For commands which expand %% and + %%%, this now only happens within string arguments. Use of aliases inside {} + is now forbidden. Processing of commands given in quotes remains the same. + * Disable evports on SunOS since they are broken. * Do not expand the file given with tmux -f so it can contain :s. -* Bump FORMAT_LOOOP_LIMIT and add a log message when hit. +* Bump FORMAT_LOOP_LIMIT and add a log message when hit. * Add a terminal feature for the mouse (since FreeBSD termcap does not have kmous). @@ -50,8 +55,8 @@ CHANGES FROM 3.2a TO 3.3 * Add display-popup -B to open a popup without a border. -* Add a menu for popups that can be opened with button 3 outside the popup or - on the left or top border. Resizing now only works on the right and bottom +* Add a menu for popups that can be opened with button three outside the popup + or on the left or top border. Resizing now only works on the right and bottom borders or when using Meta. The menu allows a popup to be closed, expanded to the full size of the client, centered in the client or changed into a pane. From a049ebd6f77c0e6d2140edea94fdd08b33c5368f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 Sep 2021 13:37:39 +0100 Subject: [PATCH 0987/1006] And more CHANGES. --- CHANGES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index ceefc215..6a7b87c5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,12 @@ CHANGES FROM 3.2a TO 3.3 +* The configure script now sets the default for default-terminal to the best + available terminfo(5) entry on the compiling platform. A new --with-TERM + argument can be used to override this. + +* Configuring on macOS will fail unless either --enable-utf8proc or + --disable-utf8proc is given. + * Change so that {} is converted to tmux commands immediately when parsed. This means it must contain valid tmux commands. For commands which expand %% and %%%, this now only happens within string arguments. Use of aliases inside {} From 7186ab25c9159ce54b2011240b2e3a476e685921 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 Sep 2021 13:40:51 +0100 Subject: [PATCH 0988/1006] Those were already there... --- CHANGES | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 6a7b87c5..4e1d167e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,12 +1,5 @@ CHANGES FROM 3.2a TO 3.3 -* The configure script now sets the default for default-terminal to the best - available terminfo(5) entry on the compiling platform. A new --with-TERM - argument can be used to override this. - -* Configuring on macOS will fail unless either --enable-utf8proc or - --disable-utf8proc is given. - * Change so that {} is converted to tmux commands immediately when parsed. This means it must contain valid tmux commands. For commands which expand %% and %%%, this now only happens within string arguments. Use of aliases inside {} @@ -29,7 +22,8 @@ CHANGES FROM 3.2a TO 3.3 telling the user they aren't doing anything ("not ready"). * When building, pick default-terminal from the first of tmux-256color, tmux, - screen-256color, screen that is available on the build system. + screen-256color, screen that is available on the build system (--with-TERM + can override). * Do not close popups on resize, instead adjust them to fit. From 86d505af46abfba2c5eed77ef26fa6ce2071cad6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 Sep 2021 13:42:50 +0100 Subject: [PATCH 0989/1006] 3.3-rc. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 6df455b3..2321508a 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], next-3.3) +AC_INIT([tmux], 3.3-rc) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From ed280e14c814f5252d41656066856c2a45fd1493 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 Sep 2021 14:03:19 +0100 Subject: [PATCH 0990/1006] Missing header. --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index eb08990a..68494932 100644 --- a/Makefile.am +++ b/Makefile.am @@ -181,6 +181,7 @@ dist_tmux_SOURCES = \ style.c \ tmux.c \ tmux.h \ + tmux-protocol.h \ tty-acs.c \ tty-features.c \ tty-keys.c \ From 3be44313aa3bc6df3a4884eec16fdd561fd59271 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 Sep 2021 14:07:42 +0100 Subject: [PATCH 0991/1006] Next is 3.4. --- Makefile.am | 1 + configure.ac | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index eb08990a..68494932 100644 --- a/Makefile.am +++ b/Makefile.am @@ -181,6 +181,7 @@ dist_tmux_SOURCES = \ style.c \ tmux.c \ tmux.h \ + tmux-protocol.h \ tty-acs.c \ tty-features.c \ tty-keys.c \ diff --git a/configure.ac b/configure.ac index 2321508a..ba0e5fb8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.3-rc) +AC_INIT([tmux], next-3.4) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 3c3d371f996b71380547a56db4412c8ca8a31469 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 16 Sep 2021 06:39:22 +0000 Subject: [PATCH 0992/1006] Fix run-shell -d with no command, GitHub issue 2885. --- cmd-run-shell.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 537a5e5f..bf43d313 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -181,7 +181,13 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) struct cmd_list *cmdlist; char *error; - if (cdata->state == NULL && cmd != NULL) { + if (cdata->state == NULL) { + if (cmd == NULL) { + if (cdata->item != NULL) + cmdq_continue(cdata->item); + cmd_run_shell_free(cdata); + return; + } if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL, cmd_run_shell_callback, cmd_run_shell_free, cdata, cdata->flags, -1, -1) == NULL) From c4b969ca62a6f0b66f97c9bfe88022a91d162038 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Sep 2021 07:20:49 +0000 Subject: [PATCH 0993/1006] Do not destroy sessions twice, GitHub issue 2889. --- session.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/session.c b/session.c index 73f44e18..fd567926 100644 --- a/session.c +++ b/session.c @@ -205,6 +205,9 @@ session_destroy(struct session *s, int notify, const char *from) struct winlink *wl; log_debug("session %s destroyed (%s)", s->name, from); + + if (s->curw == NULL) + return; s->curw = NULL; RB_REMOVE(sessions, &sessions, s); From 097a792f9de8184cc3db89049811f3669dfae8f4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 21 Sep 2021 09:50:23 +0100 Subject: [PATCH 0994/1006] Update obsolete autoconf macros, from "kylo252" GitHub issue 2900. --- configure.ac | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/configure.ac b/configure.ac index ba0e5fb8..f75367eb 100644 --- a/configure.ac +++ b/configure.ac @@ -24,11 +24,11 @@ SAVED_LDFLAGS="$LDFLAGS" # Is this oss-fuzz build? AC_ARG_ENABLE( fuzzing, - AC_HELP_STRING(--enable-fuzzing, build fuzzers) + AS_HELP_STRING(--enable-fuzzing, build fuzzers) ) AC_ARG_VAR( FUZZING_LIBS, - AC_HELP_STRING(libraries to link fuzzing targets with) + AS_HELP_STRING(libraries to link fuzzing targets with) ) # Set up convenient fuzzing defaults before initializing compiler. @@ -59,14 +59,14 @@ test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc case "x$VERSION" in xnext*) enable_debug=yes;; esac AC_ARG_ENABLE( debug, - AC_HELP_STRING(--enable-debug, enable debug build flags), + AS_HELP_STRING(--enable-debug, enable debug build flags), ) AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes) # Is this a static build? AC_ARG_ENABLE( static, - AC_HELP_STRING(--enable-static, create a static build) + AS_HELP_STRING(--enable-static, create a static build) ) if test "x$enable_static" = xyes; then test "x$PKG_CONFIG" != x && PKG_CONFIG="$PKG_CONFIG --static" @@ -77,7 +77,7 @@ fi # Allow default TERM to be set. AC_ARG_WITH( TERM, - AC_HELP_STRING(--with-TERM, set default TERM), + AS_HELP_STRING(--with-TERM, set default TERM), [DEFAULT_TERM=$withval], [DEFAULT_TERM=] ) @@ -340,7 +340,7 @@ fi # Look for utempter. AC_ARG_ENABLE( utempter, - AC_HELP_STRING(--enable-utempter, use utempter if it is installed) + AS_HELP_STRING(--enable-utempter, use utempter if it is installed) ) if test "x$enable_utempter" = xyes; then AC_CHECK_HEADER(utempter.h, enable_utempter=yes, enable_utempter=no) @@ -362,7 +362,7 @@ fi # Look for utf8proc. AC_ARG_ENABLE( utf8proc, - AC_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed) + AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed) ) if test "x$enable_utf8proc" = xyes; then AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no) @@ -384,13 +384,15 @@ AM_CONDITIONAL(HAVE_UTF8PROC, [test "x$enable_utf8proc" = xyes]) # Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well. AC_MSG_CHECKING(for b64_ntop) -AC_TRY_LINK( + AC_LINK_IFELSE([AC_LANG_PROGRAM( [ #include #include #include ], - [b64_ntop(NULL, 0, NULL, 0);], + [ + b64_ntop(NULL, 0, NULL, 0); + ])], found_b64_ntop=yes, found_b64_ntop=no ) @@ -399,13 +401,15 @@ OLD_LIBS="$LIBS" if test "x$found_b64_ntop" = xno; then AC_MSG_CHECKING(for b64_ntop with -lresolv) LIBS="$OLD_LIBS -lresolv" - AC_TRY_LINK( + AC_LINK_IFELSE([AC_LANG_PROGRAM( [ #include #include #include ], - [b64_ntop(NULL, 0, NULL, 0);], + [ + b64_ntop(NULL, 0, NULL, 0); + ])], found_b64_ntop=yes, found_b64_ntop=no ) @@ -414,13 +418,15 @@ fi if test "x$found_b64_ntop" = xno; then AC_MSG_CHECKING(for b64_ntop with -lnetwork) LIBS="$OLD_LIBS -lnetwork" - AC_TRY_LINK( + AC_LINK_IFELSE([AC_LANG_PROGRAM( [ #include #include #include ], - [b64_ntop(NULL, 0, NULL, 0);], + [ + b64_ntop(NULL, 0, NULL, 0); + ])], found_b64_ntop=yes, found_b64_ntop=no ) @@ -875,4 +881,5 @@ AC_SUBST(AM_LDFLAGS) LDFLAGS="$SAVED_LDFLAGS" # autoconf should create a Makefile. -AC_OUTPUT(Makefile) +AC_CONFIG_FILES(Makefile) +AC_OUTPUT From b2482535d0665390c75df62a959e3bed39724de0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 21 Sep 2021 09:54:50 +0100 Subject: [PATCH 0995/1006] Crosscompiling defaults from Romain Francoise. --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index f75367eb..26bd1a98 100644 --- a/configure.ac +++ b/configure.ac @@ -718,6 +718,7 @@ if test "x$DEFAULT_TERM" = x; then ])], [DEFAULT_TERM=screen-256color], , + [DEFAULT_TERM=screen] ) AC_RUN_IFELSE([AC_LANG_SOURCE( [ @@ -737,6 +738,7 @@ if test "x$DEFAULT_TERM" = x; then ])], [DEFAULT_TERM=tmux], , + [DEFAULT_TERM=screen] ) AC_RUN_IFELSE([AC_LANG_SOURCE( [ @@ -756,6 +758,7 @@ if test "x$DEFAULT_TERM" = x; then ])], [DEFAULT_TERM=tmux-256color], , + [DEFAULT_TERM=screen] ) AC_MSG_RESULT($DEFAULT_TERM) fi From 9c77a5b9f05f91d745705b2739c83519674fe92d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 21 Sep 2021 10:03:38 +0100 Subject: [PATCH 0996/1006] Remove duplicate. --- CHANGES | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 4e1d167e..d1dbb6ea 100644 --- a/CHANGES +++ b/CHANGES @@ -2,8 +2,9 @@ CHANGES FROM 3.2a TO 3.3 * Change so that {} is converted to tmux commands immediately when parsed. This means it must contain valid tmux commands. For commands which expand %% and - %%%, this now only happens within string arguments. Use of aliases inside {} - is now forbidden. Processing of commands given in quotes remains the same. + %%%, this now only happens within string arguments. Use of nested aliases + inside {} is now forbidden. Processing of commands given in quotes remains + the same. * Disable evports on SunOS since they are broken. @@ -65,9 +66,6 @@ CHANGES FROM 3.2a TO 3.3 new -b flags runs them in the background as before. Also set return code for confirm-before. -* Parse {} as actual commands immediately, this means the contents of {} has to - be valid tmux commands as well as matching the syntax. - * Change cursor style handling so tmux understands which sequences contain blinking and sets the flag appropriately, means that it works whether cnorm disables blinking or not. This now matches xterm's behaviour. From 257e9ba69a06c37c3b970db06c50e93f91955b11 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Sep 2021 15:21:44 +0000 Subject: [PATCH 0997/1006] Fix command prompt with multiple prompts (add the result onto the list again as we go along). ok deraadt --- cmd-command-prompt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 820053ec..a7a02702 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -183,6 +183,7 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, if (cdata->flags & PROMPT_INCREMENTAL) goto out; + cmd_append_argv(&cdata->argc, &cdata->argv, s); if (++cdata->current != cdata->count) { prompt = &cdata->prompts[cdata->current]; status_prompt_update(c, prompt->prompt, prompt->input); From 68c890585991c1114690d43a179eef0a7f207871 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Sep 2021 19:12:00 +0000 Subject: [PATCH 0998/1006] Do not call recalculate_sizes while clearing a client session because it needs to loop over the clients, instead do it after all clients are cleared. Fixes a crash reported by martijn@ when a session with multiple clients attached is destroyed, but there are other sessions so tmux does not entirely exit. ok deraadt --- server-client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index a7cad0a5..51988799 100644 --- a/server-client.c +++ b/server-client.c @@ -321,11 +321,11 @@ server_client_set_session(struct client *c, struct session *s) c->last_session = NULL; c->session = s; c->flags |= CLIENT_FOCUSED; - recalculate_sizes(); if (old != NULL && old->curw != NULL) window_update_focus(old->curw->window); if (s != NULL) { + recalculate_sizes(); window_update_focus(s->curw->window); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); @@ -2057,6 +2057,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) if (datalen != 0) fatalx("bad MSG_EXITING size"); server_client_set_session(c, NULL); + recalculate_sizes(); tty_close(&c->tty); proc_send(c->peer, MSG_EXITED, -1, NULL, 0); break; From 3d5a02bf45f03f9dc37b2178ac7964f3f2b3748c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 5 Oct 2021 12:45:02 +0000 Subject: [PATCH 0999/1006] Do not try to use NULL time values. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index 8202a256..55b40ec0 100644 --- a/format.c +++ b/format.c @@ -3327,7 +3327,7 @@ format_find(struct format_tree *ft, const char *key, int modifiers, fte = format_table_get(key); if (fte != NULL) { value = fte->cb(ft); - if (fte->type == FORMAT_TABLE_TIME) + if (fte->type == FORMAT_TABLE_TIME && value != NULL) t = ((struct timeval *)value)->tv_sec; else found = value; From 9b1fdb291ee8e940311a51cf41f97b07930b4688 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 5 Oct 2021 12:46:02 +0000 Subject: [PATCH 1000/1006] Separate "very visible" flag from blinking flag, it should not affect DECSCUSR. GitHub issue 2891. --- input.c | 8 +-- screen.c | 18 ++++--- tmux.h | 4 +- tty.c | 159 ++++++++++++++++++++++++++----------------------------- 4 files changed, 93 insertions(+), 96 deletions(-) diff --git a/input.c b/input.c index 7a320c56..8a16281c 100644 --- a/input.c +++ b/input.c @@ -1646,7 +1646,7 @@ input_csi_dispatch_rm(struct input_ctx *ictx) screen_write_mode_clear(sctx, MODE_INSERT); break; case 34: - screen_write_mode_set(sctx, MODE_BLINKING); + screen_write_mode_set(sctx, MODE_CURSOR_VERY_VISIBLE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -1682,7 +1682,7 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) screen_write_mode_clear(sctx, MODE_WRAP); break; case 12: - screen_write_mode_clear(sctx, MODE_BLINKING); + screen_write_mode_clear(sctx, MODE_CURSOR_BLINKING); break; case 25: /* TCEM */ screen_write_mode_clear(sctx, MODE_CURSOR); @@ -1734,7 +1734,7 @@ input_csi_dispatch_sm(struct input_ctx *ictx) screen_write_mode_set(sctx, MODE_INSERT); break; case 34: - screen_write_mode_clear(sctx, MODE_BLINKING); + screen_write_mode_clear(sctx, MODE_CURSOR_VERY_VISIBLE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -1771,7 +1771,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) screen_write_mode_set(sctx, MODE_WRAP); break; case 12: - screen_write_mode_set(sctx, MODE_BLINKING); + screen_write_mode_set(sctx, MODE_CURSOR_BLINKING); break; case 25: /* TCEM */ screen_write_mode_set(sctx, MODE_CURSOR); diff --git a/screen.c b/screen.c index dcbae278..4c13b693 100644 --- a/screen.c +++ b/screen.c @@ -163,27 +163,27 @@ screen_set_cursor_style(struct screen *s, u_int style) break; case 1: s->cstyle = SCREEN_CURSOR_BLOCK; - s->mode |= MODE_BLINKING; + s->mode |= MODE_CURSOR_BLINKING; break; case 2: s->cstyle = SCREEN_CURSOR_BLOCK; - s->mode &= ~MODE_BLINKING; + s->mode &= ~MODE_CURSOR_BLINKING; break; case 3: s->cstyle = SCREEN_CURSOR_UNDERLINE; - s->mode |= MODE_BLINKING; + s->mode |= MODE_CURSOR_BLINKING; break; case 4: s->cstyle = SCREEN_CURSOR_UNDERLINE; - s->mode &= ~MODE_BLINKING; + s->mode &= ~MODE_CURSOR_BLINKING; break; case 5: s->cstyle = SCREEN_CURSOR_BAR; - s->mode |= MODE_BLINKING; + s->mode |= MODE_CURSOR_BLINKING; break; case 6: s->cstyle = SCREEN_CURSOR_BAR; - s->mode &= ~MODE_BLINKING; + s->mode &= ~MODE_CURSOR_BLINKING; break; } } @@ -680,8 +680,10 @@ screen_mode_to_string(int mode) strlcat(tmp, "MOUSE_STANDARD,", sizeof tmp); if (mode & MODE_MOUSE_BUTTON) strlcat(tmp, "MOUSE_BUTTON,", sizeof tmp); - if (mode & MODE_BLINKING) - strlcat(tmp, "BLINKING,", sizeof tmp); + if (mode & MODE_CURSOR_BLINKING) + strlcat(tmp, "CURSOR_BLINKING,", sizeof tmp); + if (mode & MODE_CURSOR_VERY_VISIBLE) + strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp); if (mode & MODE_MOUSE_UTF8) strlcat(tmp, "UTF8,", sizeof tmp); if (mode & MODE_MOUSE_SGR) diff --git a/tmux.h b/tmux.h index 7a77cdae..29a532aa 100644 --- a/tmux.h +++ b/tmux.h @@ -520,7 +520,7 @@ enum tty_code_code { #define MODE_WRAP 0x10 #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 -#define MODE_BLINKING 0x80 +#define MODE_CURSOR_BLINKING 0x80 #define MODE_MOUSE_UTF8 0x100 #define MODE_MOUSE_SGR 0x200 #define MODE_BRACKETPASTE 0x400 @@ -529,10 +529,12 @@ enum tty_code_code { #define MODE_ORIGIN 0x2000 #define MODE_CRLF 0x4000 #define MODE_KEXTENDED 0x8000 +#define MODE_CURSOR_VERY_VISIBLE 0x10000 #define ALL_MODES 0xffffff #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) #define MOTION_MOUSE_MODES (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) +#define CURSOR_MODES (MODE_CURSOR|MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE) /* A single UTF-8 character. */ typedef u_int utf8_char; diff --git a/tty.c b/tty.c index ba58aa2b..e4800ec2 100644 --- a/tty.c +++ b/tty.c @@ -314,7 +314,7 @@ tty_start_tty(struct tty *tty) tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; if (tcsetattr(c->fd, TCSANOW, &tio) == 0) - tcflush(c->fd, TCIOFLUSH); + tcflush(c->fd, TCOFLUSH); tty_putcode(tty, TTYC_SMCUP); @@ -658,12 +658,83 @@ tty_force_cursor_colour(struct tty *tty, const char *ccolour) tty->ccolour = xstrdup(ccolour); } +static void +tty_update_cursor(struct tty *tty, int mode, int changed, struct screen *s) +{ + enum screen_cursor_style cstyle; + + /* Set cursor colour if changed. */ + if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) + tty_force_cursor_colour(tty, s->ccolour); + + /* If cursor is off, set as invisible. */ + if (~mode & MODE_CURSOR) { + if (changed & MODE_CURSOR) + tty_putcode(tty, TTYC_CIVIS); + return; + } + + /* Check if blinking or very visible flag changed or style changed. */ + if (s == NULL) + cstyle = tty->cstyle; + else + cstyle = s->cstyle; + if ((changed & CURSOR_MODES) == 0 && cstyle == tty->cstyle) + return; + + /* + * Set cursor style. If an explicit style has been set with DECSCUSR, + * set it if supported, otherwise send cvvis for blinking styles. + * + * If no style, has been set (SCREEN_CURSOR_DEFAULT), then send cvvis + * if either the blinking or very visible flags are set. + */ + tty_putcode(tty, TTYC_CNORM); + switch (cstyle) { + case SCREEN_CURSOR_DEFAULT: + if (tty_term_has(tty->term, TTYC_SE)) + tty_putcode(tty, TTYC_SE); + else + tty_putcode1(tty, TTYC_SS, 0); + if (mode & (MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE)) + tty_putcode(tty, TTYC_CVVIS); + break; + case SCREEN_CURSOR_BLOCK: + if (tty_term_has(tty->term, TTYC_SS)) { + if (mode & MODE_CURSOR_BLINKING) + tty_putcode1(tty, TTYC_SS, 1); + else + tty_putcode1(tty, TTYC_SS, 2); + } else if (mode & MODE_CURSOR_BLINKING) + tty_putcode(tty, TTYC_CVVIS); + break; + case SCREEN_CURSOR_UNDERLINE: + if (tty_term_has(tty->term, TTYC_SS)) { + if (mode & MODE_CURSOR_BLINKING) + tty_putcode1(tty, TTYC_SS, 3); + else + tty_putcode1(tty, TTYC_SS, 4); + } else if (mode & MODE_CURSOR_BLINKING) + tty_putcode(tty, TTYC_CVVIS); + break; + case SCREEN_CURSOR_BAR: + if (tty_term_has(tty->term, TTYC_SS)) { + if (mode & MODE_CURSOR_BLINKING) + tty_putcode1(tty, TTYC_SS, 5); + else + tty_putcode1(tty, TTYC_SS, 6); + } else if (mode & MODE_CURSOR_BLINKING) + tty_putcode(tty, TTYC_CVVIS); + break; + } + tty->cstyle = cstyle; + } + void tty_update_mode(struct tty *tty, int mode, struct screen *s) { - struct client *c = tty->client; - int changed; - enum screen_cursor_style cstyle = tty->cstyle; + struct client *c = tty->client; + int changed; if (tty->flags & TTY_NOCURSOR) mode &= ~MODE_CURSOR; @@ -676,85 +747,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) screen_mode_to_string(mode)); } - if (s != NULL) { - if (strcmp(s->ccolour, tty->ccolour) != 0) - tty_force_cursor_colour(tty, s->ccolour); - cstyle = s->cstyle; - } - if (~mode & MODE_CURSOR) { - /* Cursor now off - set as invisible. */ - if (changed & MODE_CURSOR) - tty_putcode(tty, TTYC_CIVIS); - } else if ((changed & (MODE_CURSOR|MODE_BLINKING)) || - cstyle != tty->cstyle) { - /* - * Cursor now on, blinking flag changed or style changed. Start - * by setting the cursor to normal. - */ - tty_putcode(tty, TTYC_CNORM); - switch (cstyle) { - case SCREEN_CURSOR_DEFAULT: - /* - * If the old style wasn't default, then reset it to - * default. - */ - if (tty->cstyle != SCREEN_CURSOR_DEFAULT) { - if (tty_term_has(tty->term, TTYC_SE)) - tty_putcode(tty, TTYC_SE); - else - tty_putcode1(tty, TTYC_SS, 0); - } - - /* Set the cursor as very visible if necessary. */ - if (mode & MODE_BLINKING) - tty_putcode(tty, TTYC_CVVIS); - break; - case SCREEN_CURSOR_BLOCK: - /* - * Set style to either block blinking (1) or steady (2) - * if supported, otherwise just check the blinking - * flag. - */ - if (tty_term_has(tty->term, TTYC_SS)) { - if (mode & MODE_BLINKING) - tty_putcode1(tty, TTYC_SS, 1); - else - tty_putcode1(tty, TTYC_SS, 2); - } else if (mode & MODE_BLINKING) - tty_putcode(tty, TTYC_CVVIS); - break; - case SCREEN_CURSOR_UNDERLINE: - /* - * Set style to either underline blinking (3) or steady - * (4) if supported, otherwise just check the blinking - * flag. - */ - if (tty_term_has(tty->term, TTYC_SS)) { - if (mode & MODE_BLINKING) - tty_putcode1(tty, TTYC_SS, 3); - else - tty_putcode1(tty, TTYC_SS, 4); - } else if (mode & MODE_BLINKING) - tty_putcode(tty, TTYC_CVVIS); - break; - case SCREEN_CURSOR_BAR: - /* - * Set style to either bar blinking (5) or steady (6) - * if supported, otherwise just check the blinking - * flag. - */ - if (tty_term_has(tty->term, TTYC_SS)) { - if (mode & MODE_BLINKING) - tty_putcode1(tty, TTYC_SS, 5); - else - tty_putcode1(tty, TTYC_SS, 6); - } else if (mode & MODE_BLINKING) - tty_putcode(tty, TTYC_CVVIS); - break; - } - tty->cstyle = cstyle; - } - + tty_update_cursor(tty, mode, changed, s); if ((changed & ALL_MOUSE_MODES) && tty_term_has(tty->term, TTYC_KMOUS)) { /* From 9f6164a05cc0de372f6859127f72c8b242596bf5 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 5 Oct 2021 12:49:37 +0000 Subject: [PATCH 1001/1006] Make send-keys without any arguments send the key it is bound to (if any). GitHub issue 2904. --- cmd-send-keys.c | 6 ++++++ tmux.1 | 1 + 2 files changed, 7 insertions(+) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 44b796ba..47fa1caa 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -202,6 +202,12 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW); } + if (count == 0) { + for (; np != 0; np--) + cmd_send_keys_inject_key(item, NULL, event->key); + return (CMD_RETURN_NORMAL); + } + for (; np != 0; np--) { for (i = 0; i < count; i++) { after = cmd_send_keys_inject_string(item, after, args, diff --git a/tmux.1 b/tmux.1 index d48415fe..46611df2 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3123,6 +3123,7 @@ or to send; if the string is not recognised as a key, it is sent as a series of characters. All arguments are sent sequentially from first to last. +If no keys are given and the command is bound to a key, then that key is used. .Pp The .Fl l From e06a4e041c68cabe448e6680505b4690e2d7169d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 5 Oct 2021 17:23:13 +0000 Subject: [PATCH 1002/1006] Set mouse_x and mouse_y on the status line, GitHub issue 2913. --- format.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/format.c b/format.c index 55b40ec0..8d6c8a21 100644 --- a/format.c +++ b/format.c @@ -1614,11 +1614,16 @@ format_cb_mouse_x(struct format_tree *ft) struct window_pane *wp; u_int x, y; - if (ft->m.valid) { - wp = cmd_mouse_pane(&ft->m, NULL, NULL); - if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0) - return (format_printf("%u", x)); + if (!ft->m.valid) return (NULL); + wp = cmd_mouse_pane(&ft->m, NULL, NULL); + if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0) + return (format_printf("%u", x)); + if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) { + if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) + return (format_printf("%u", ft->m.x)); + if (ft->m.statusat > 0 && ft->m.y >= ft->m.statusat) + return (format_printf("%u", ft->m.x)); } return (NULL); } @@ -1628,13 +1633,18 @@ static void * format_cb_mouse_y(struct format_tree *ft) { struct window_pane *wp; - u_int x, y; + u_int x, y, top; - if (ft->m.valid) { - wp = cmd_mouse_pane(&ft->m, NULL, NULL); - if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0) - return (format_printf("%u", y)); + if (!ft->m.valid) return (NULL); + wp = cmd_mouse_pane(&ft->m, NULL, NULL); + if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0) + return (format_printf("%u", y)); + if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) { + if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) + return (format_printf("%u", ft->m.y)); + if (ft->m.statusat > 0 && ft->m.y >= ft->m.statusat) + return (format_printf("%u", ft->m.y - ft->m.statusat)); } return (NULL); } From da05d0582400d25f44b84c014b446e6685497761 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 5 Oct 2021 20:15:16 +0000 Subject: [PATCH 1003/1006] Fix some warnings. --- format.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/format.c b/format.c index 8d6c8a21..37b9123b 100644 --- a/format.c +++ b/format.c @@ -1622,7 +1622,7 @@ format_cb_mouse_x(struct format_tree *ft) if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) { if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) return (format_printf("%u", ft->m.x)); - if (ft->m.statusat > 0 && ft->m.y >= ft->m.statusat) + if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) return (format_printf("%u", ft->m.x)); } return (NULL); @@ -1633,7 +1633,7 @@ static void * format_cb_mouse_y(struct format_tree *ft) { struct window_pane *wp; - u_int x, y, top; + u_int x, y; if (!ft->m.valid) return (NULL); @@ -1643,7 +1643,7 @@ format_cb_mouse_y(struct format_tree *ft) if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) { if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) return (format_printf("%u", ft->m.y)); - if (ft->m.statusat > 0 && ft->m.y >= ft->m.statusat) + if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) return (format_printf("%u", ft->m.y - ft->m.statusat)); } return (NULL); From 5359b766195528b9a0763c7f5e4ce5f2797a9b09 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 Oct 2021 10:33:12 +0000 Subject: [PATCH 1004/1006] Do not reset cursor to default if it has never been changed, fixes problem reported by naddy. --- tty.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tty.c b/tty.c index e4800ec2..0f295f6c 100644 --- a/tty.c +++ b/tty.c @@ -692,10 +692,12 @@ tty_update_cursor(struct tty *tty, int mode, int changed, struct screen *s) tty_putcode(tty, TTYC_CNORM); switch (cstyle) { case SCREEN_CURSOR_DEFAULT: - if (tty_term_has(tty->term, TTYC_SE)) - tty_putcode(tty, TTYC_SE); - else - tty_putcode1(tty, TTYC_SS, 0); + if (tty->cstyle != SCREEN_CURSOR_DEFAULT) { + if (tty_term_has(tty->term, TTYC_SE)) + tty_putcode(tty, TTYC_SE); + else + tty_putcode1(tty, TTYC_SS, 0); + } if (mode & (MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE)) tty_putcode(tty, TTYC_CVVIS); break; From 95744963335389d670997fe943c0859449eddc3e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 Oct 2021 07:52:13 +0000 Subject: [PATCH 1005/1006] Handle splitw -I correctly when used from an attached client, GitHub issue 2917. --- cmd-display-message.c | 8 ++++++-- cmd-split-window.c | 26 ++++++++++++++++---------- window.c | 6 +++++- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index 596f0b5c..7828f694 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -75,12 +75,16 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'I')) { if (wp == NULL) return (CMD_RETURN_NORMAL); - if (window_pane_start_input(wp, item, &cause) != 0) { + switch (window_pane_start_input(wp, item, &cause)) { + case -1: cmdq_error(item, "%s", cause); free(cause); return (CMD_RETURN_ERROR); + case 1: + return (CMD_RETURN_NORMAL); + case 0: + return (CMD_RETURN_WAIT); } - return (CMD_RETURN_WAIT); } if (args_has(args, 'F') && count != 0) { diff --git a/cmd-split-window.c b/cmd-split-window.c index 92f515ca..0f82e648 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -163,16 +163,22 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) environ_free(sc.environ); return (CMD_RETURN_ERROR); } - if (input && window_pane_start_input(new_wp, item, &cause) != 0) { - server_client_remove_pane(new_wp); - layout_close_pane(new_wp); - window_remove_pane(wp->window, new_wp); - cmdq_error(item, "%s", cause); - free(cause); - if (sc.argv != NULL) - cmd_free_argv(sc.argc, sc.argv); - environ_free(sc.environ); - return (CMD_RETURN_ERROR); + if (input) { + switch (window_pane_start_input(new_wp, item, &cause)) { + case -1: + server_client_remove_pane(new_wp); + layout_close_pane(new_wp); + window_remove_pane(wp->window, new_wp); + cmdq_error(item, "%s", cause); + free(cause); + if (sc.argv != NULL) + cmd_free_argv(sc.argc, sc.argv); + environ_free(sc.environ); + return (CMD_RETURN_ERROR); + case 1: + input = 0; + break; + } } if (!args_has(args, 'd')) cmd_find_from_winlink_pane(current, wl, new_wp, 0); diff --git a/window.c b/window.c index 64d75d43..d7fc3bf4 100644 --- a/window.c +++ b/window.c @@ -1527,7 +1527,7 @@ window_pane_input_callback(struct client *c, __unused const char *path, size_t len = EVBUFFER_LENGTH(buffer); wp = window_pane_find_by_id(cdata->wp); - if (wp == NULL || closed || error != 0 || c->flags & CLIENT_DEAD) { + if (wp == NULL || closed || error != 0 || (c->flags & CLIENT_DEAD)) { if (wp == NULL) c->flags |= CLIENT_EXIT; @@ -1553,6 +1553,10 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item, *cause = xstrdup("pane is not empty"); return (-1); } + if (c->flags & (CLIENT_DEAD|CLIENT_EXITED)) + return (1); + if (c->session != NULL) + return (1); cdata = xmalloc(sizeof *cdata); cdata->item = item; From 5f63181ed5a9d409fcb0955217fbe4c1a40dd9ff Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 Oct 2021 07:53:31 +0000 Subject: [PATCH 1006/1006] Add a missing El, from Alexis Hildebrandt in GitHub issue 2918. --- tmux.1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.1 b/tmux.1 index 46611df2..daa2b316 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3996,6 +3996,7 @@ If set to both, a bell and a message are produced. Sets the session's conception of what characters are considered word separators, for the purposes of the next and previous word commands in copy mode. +.El .Pp Available window options are: .Pp