From e7a530fe4c0f8e0f807daf6a1b80d86c67c93e1b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 28 May 2019 15:48:31 +0100 Subject: Next will be 3.1. --- CHANGES | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 097d7228..56bf28b0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +CHANGES FROM 3.0 to X.X + +XXX + CHANGES FROM 2.9 to 3.0 * Add an addition form of string syntax in the configuration file - {}. This diff --git a/configure.ac b/configure.ac index 2c1a8945..5fba1eaf 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.0-rc) +AC_INIT([tmux], next-3.1) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) -- cgit From 4382538e4bac2849262710c447e4b630e7f34bd7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 May 2019 18:30:30 +0000 Subject: Do not read past the end of the argument string if it is empty. --- arguments.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arguments.c b/arguments.c index 68690deb..54bc3593 100644 --- a/arguments.c +++ b/arguments.c @@ -211,6 +211,8 @@ args_escape(const char *s) char *escaped, *result; int flags; + if (*s == '\0') + return (xstrdup(s)); if ((strchr(quoted, s[0]) != NULL || s[0] == '~') && s[1] == '\0') { xasprintf(&escaped, "\\%c", s[0]); return (escaped); -- cgit From 580cd16f4c3474d7cae867b9b24f01665a268e9d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 May 2019 18:53:36 +0000 Subject: Redraw status line if size changes, GitHub issue 1762. Also fix length of target buffer when pasting into status line. --- status.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/status.c b/status.c index ce36cc3c..0f96f0d3 100644 --- a/status.c +++ b/status.c @@ -348,10 +348,8 @@ status_redraw(struct client *c) /* Resize the target screen. */ if (screen_size_x(&sl->screen) != width || screen_size_y(&sl->screen) != lines) { - if (screen_size_x(&sl->screen) != width) - force = 1; screen_resize(&sl->screen, width, lines, 0); - changed = 1; + changed = force = 1; } screen_write_start(&ctx, NULL, &sl->screen); @@ -865,7 +863,7 @@ status_prompt_paste(struct client *c) if ((pb = paste_get_top(NULL)) == NULL) return (0); bufdata = paste_buffer_data(pb, &bufsize); - ud = xreallocarray(NULL, bufsize, sizeof *ud); + ud = xreallocarray(NULL, bufsize + 1, sizeof *ud); udp = ud; for (i = 0; i != bufsize; /* nothing */) { more = utf8_open(udp, bufdata[i]); -- cgit From a4424fbebf929f4ad742200365fc330a26a65d7f Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 May 2019 10:08:36 +0000 Subject: Support \ooo escapes, from Avi Halachmi. --- cmd-parse.y | 26 ++++++++++++++++++++++++-- tmux.1 | 6 +++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index f347280b..739ca56c 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -1083,12 +1083,34 @@ error: static int yylex_token_escape(char **buf, size_t *len) { - int ch, type; + int ch, type, o2, o3; u_int size, i, tmp; char s[9]; struct utf8_data ud; - switch (ch = yylex_getc()) { + ch = yylex_getc(); + + if (ch >= '4' && ch <= '7') { + yyerror("invalid octal escape"); + return (0); + } + if (ch >= '0' && ch <= '3') { + o2 = yylex_getc(); + if (o2 >= '0' && o2 <= '7') { + o3 = yylex_getc(); + if (o3 >= '0' && o3 <= '7') { + ch = 64 * (ch - '0') + + 8 * (o2 - '0') + + (o3 - '0'); + yylex_append1(buf, len, ch); + return (1); + } + } + yyerror("invalid octal escape"); + return (0); + } + + switch (ch) { case EOF: return (0); case 'e': diff --git a/tmux.1 b/tmux.1 index b4e27c40..dfdd96b4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -515,7 +515,11 @@ the given four or eight digit hexadecimal number. When preceded (escaped) by a \e, the following characters are replaced: \ee by the escape character; \er by a carriage return; \en by a newline; and \et by a tab. -.Pp +.It +\eooo is replaced by a character of the octal value ooo. +Three octal digits are required, for example \e001. +The largest valid character is \e377. +.It Any other characters preceded by \e are replaced by themselves (that is, the \e is removed) and are not treated as having any special meaning - so for example \e; will not mark a command sequence and \e$ will not expand an environment -- cgit From 409d52ed411cbc4f8e52ff3dfd02199c9f998a51 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 May 2019 14:19:26 +0100 Subject: Try OS X. --- .travis.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2188eec0..ab333aa4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,18 @@ +os: + - linux + - osx + language: c + matrix: include: - compiler: gcc - compiler: clang - env: CFLAGS="-g -O2" + before_install: - - sudo apt-get update -qq - - sudo apt-get -y install bison debhelper autotools-dev dh-autoreconf file libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential -script: (CFLAGS= ./autogen.sh) && ./configure --enable-debug && make + - 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 + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; fi + +script: + - ./autogen.sh && ./configure && make -- cgit From 4425c1e1b337b8b72896ba6ecbc5402ea08e4e8a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 May 2019 14:24:09 +0100 Subject: Tweak again. --- .travis.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index ab333aa4..578c7f43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,12 @@ +language: c + os: - linux - osx -language: c - -matrix: - include: - - compiler: gcc - - compiler: clang +compiler: + - gcc + - clang before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq; fi -- cgit From 2ce8e0110cfbc2505cd369895d5bb153c3f4e3d8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 29 May 2019 14:32:49 +0100 Subject: Don't even need this? --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 578c7f43..fd85bf61 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ compiler: 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 - - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; fi script: - ./autogen.sh && ./configure && make -- cgit From c17edd594e8088d2355102a8ca58f4b256c59397 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 May 2019 19:34:42 +0000 Subject: The line number needs to be updated only after the \n is processed by the parser, so store a flag and update it next time around. Also each new line needs its own shared data. --- cmd-parse.y | 7 ++++++- cmd-queue.c | 28 ++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 739ca56c..a235fde1 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -58,6 +58,7 @@ struct cmd_parse_state { size_t len; size_t off; + int eol; int eof; struct cmd_parse_input *input; u_int escapes; @@ -933,6 +934,10 @@ yylex(void) char *token, *cp; int ch, next; + if (ps->eol) + ps->input->line++; + ps->eol = 0; + for (;;) { ch = yylex_getc(); @@ -959,7 +964,7 @@ yylex(void) /* * End of line. Update the line number. */ - ps->input->line++; + ps->eol = 1; return ('\n'); } diff --git a/cmd-queue.c b/cmd-queue.c index f08d7c02..93dcaa53 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -203,16 +203,20 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, struct cmdq_item *item, *first = NULL, *last = NULL; struct cmd *cmd; struct cmdq_shared *shared; - - shared = xcalloc(1, sizeof *shared); - if (current != NULL) - 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); + u_int group = 0; TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { + if (cmd->group != group) { + shared = xcalloc(1, sizeof *shared); + if (current != NULL) + 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); + group = cmd->group; + } + item = xcalloc(1, sizeof *item); xasprintf(&item->name, "[%s/%p]", cmd->entry->name, item); item->type = CMDQ_COMMAND; @@ -263,12 +267,20 @@ static enum cmd_retval 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 cmd *cmd = item->cmd; const struct cmd_entry *entry = cmd->entry; enum cmd_retval retval; struct cmd_find_state *fsp, fs; int flags; + char *tmp; + + if (log_get_level() > 1) { + tmp = cmd_print(cmd); + log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp); + free(tmp); + } flags = !!(shared->flags & CMDQ_SHARED_CONTROL); cmdq_guard(item, "begin", flags); -- cgit From 7dced376737ef685e09fd5a49161ca2bf423e91b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 May 2019 20:05:14 +0000 Subject: Use VIS_CSTYLE for the arguments and add the missing escapes it can generate to the parser. --- arguments.c | 2 +- cmd-parse.y | 15 +++++++++++++++ tty-term.c | 2 +- window-buffer.c | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/arguments.c b/arguments.c index 54bc3593..8e049aab 100644 --- a/arguments.c +++ b/arguments.c @@ -218,7 +218,7 @@ args_escape(const char *s) return (escaped); } - flags = VIS_OCTAL|VIS_TAB|VIS_NL; + flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; if (s[strcspn(s, quoted)] != '\0') flags |= VIS_DQ; utf8_stravis(&escaped, s, flags); diff --git a/cmd-parse.y b/cmd-parse.y index a235fde1..a623caa5 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -1118,9 +1118,24 @@ yylex_token_escape(char **buf, size_t *len) switch (ch) { case EOF: return (0); + case 'a': + ch = '\a'; + break; + case 'b': + ch = '\b'; + break; case 'e': ch = '\033'; break; + case 'f': + ch = '\f'; + break; + case 's': + ch = ' '; + break; + case 'v': + ch = '\v'; + break; case 'r': ch = '\r'; break; diff --git a/tty-term.c b/tty-term.c index 45934afa..83bfcdc1 100644 --- a/tty-term.c +++ b/tty-term.c @@ -685,7 +685,7 @@ tty_term_describe(struct tty_term *term, enum tty_code_code code) break; case TTYCODE_STRING: strnvis(out, term->codes[code].value.string, sizeof out, - VIS_OCTAL|VIS_TAB|VIS_NL); + VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); xsnprintf(s, sizeof s, "%4u: %s: (string) %s", code, tty_term_codes[code].name, out); diff --git a/window-buffer.c b/window-buffer.c index 79dacf97..983b1680 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -246,7 +246,7 @@ window_buffer_draw(__unused void *modedata, void *itemdata, at = 0; while (end != pdata + psize && *end != '\n') { if ((sizeof line) - at > 5) { - cp = vis(line + at, *end, VIS_TAB|VIS_OCTAL, 0); + cp = vis(line + at, *end, VIS_OCTAL|VIS_TAB, 0); at = cp - line; } end++; -- cgit From 026450c1a8ed64e49521bec676e2e1faba64c1aa Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 30 May 2019 07:52:24 +0100 Subject: Add test for xenl terminals. --- regress/command-order.sh | 6 ++---- regress/xenl-terminal.sh | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 regress/xenl-terminal.sh diff --git a/regress/command-order.sh b/regress/command-order.sh index fb4e0324..04046f0d 100644 --- a/regress/command-order.sh +++ b/regress/command-order.sh @@ -1,7 +1,5 @@ #!/bin/sh -# new-session without clients should be the right size - PATH=/bin:/usr/bin TERM=screen @@ -19,6 +17,7 @@ EOF $TMUX -f$TMP start $TMP || exit 1 +$TMUX kill-server 2>/dev/null cat </dev/null cat <$TMP new -sfoo -nfoo0 @@ -40,6 +38,7 @@ EOF $TMUX -f$TMP start $TMP || exit 1 +$TMUX kill-server 2>/dev/null cat </dev/null exit 0 diff --git a/regress/xenl-terminal.sh b/regress/xenl-terminal.sh new file mode 100644 index 00000000..07469ceb --- /dev/null +++ b/regress/xenl-terminal.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest" +$TMUX kill-server 2>/dev/null +TMUX2="$TEST_TMUX -Ltest2" +$TMUX2 kill-server 2>/dev/null + +TMP=$(mktemp) +trap "rm -f $TMP" 0 1 15 + +$TMUX2 -f/dev/null new -d || exit 1 +$TMUX2 set -as terminal-overrides ',*:xenl@' || 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 +$TMUX -f/dev/null new -x20 -y2 -d "$TMUX2 attach" || exit 1 +sleep 1 +$TMUX capturep -p|tail -1 >$TMP || exit 1 +$TMUX kill-server 2>/dev/null +$TMUX2 kill-server 2>/dev/null +cat < Date: Thu, 30 May 2019 07:42:41 +0000 Subject: I had hoped that non-xenl terminals had died out, at least in fairly modern OSs, but no - DragonFly BSD's console returns to haunt us. Fix it at least somewhat. GitHub issue 1763. --- tmux.1 | 2 +- tty.c | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index dfdd96b4..d9b37c0e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4325,7 +4325,7 @@ Align text to the left, centre or right of the available space if appropriate. .It Xo Ic list=on , .Ic list=focus , .Ic list=left-marker , -.Ic list=right=marker , +.Ic list=right-marker , .Ic nolist .Xc Mark the position of the various window list components in the diff --git a/tty.c b/tty.c index 34403a1f..05df4f0f 100644 --- a/tty.c +++ b/tty.c @@ -527,6 +527,12 @@ tty_putc(struct tty *tty, u_char ch) { const char *acs; + if ((tty->term->flags & TERM_EARLYWRAP) && + ch >= 0x20 && ch != 0x7f && + tty->cy == tty->sy - 1 && + tty->cx + 1 >= tty->sx) + return; + if (tty->cell.attr & GRID_ATTR_CHARSET) { acs = tty_acs_get(tty, ch); if (acs != NULL) @@ -557,6 +563,11 @@ tty_putc(struct tty *tty, u_char ch) void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { + if ((tty->term->flags & TERM_EARLYWRAP) && + tty->cy == tty->sy - 1 && + tty->cx + len >= tty->sx) + len = tty->sx - tty->cx - 1; + tty_add(tty, buf, len); if (tty->cx + width > tty->sx) { tty->cx = (tty->cx + width) - tty->sx; -- cgit From 8fb796b5b3fbd7ff430d1b6ad2a49f97feea8f3c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 30 May 2019 10:04:33 +0000 Subject: No longer need to reduce line number by one. --- cmd-parse.y | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index a623caa5..d5d12d95 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -361,7 +361,7 @@ command : assignment TOKEN $$ = xcalloc(1, sizeof *$$); $$->name = $2; - $$->line = ps->input->line - 1; + $$->line = ps->input->line; } | assignment TOKEN arguments @@ -370,7 +370,7 @@ command : assignment TOKEN $$ = xcalloc(1, sizeof *$$); $$->name = $2; - $$->line = ps->input->line - 1; + $$->line = ps->input->line; $$->argc = $3.argc; $$->argv = $3.argv; -- cgit From b26523c26dc7cf0a24a1adb787aa1816deb40693 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 30 May 2019 20:54:03 +0000 Subject: Remove a leftover abort and some fixes from cppcheck. --- cmd-display-message.c | 3 +-- input.c | 1 - mode-tree.c | 2 +- options.c | 3 ++- window.c | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index 8c1ad5f6..4d9bccb6 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -109,8 +109,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) format_defaults(ft, target_c, s, wl, wp); if (args_has(args, 'a')) { - if (item != NULL) - format_each(ft, cmd_display_message_each, item); + format_each(ft, cmd_display_message_each, item); return (CMD_RETURN_NORMAL); } diff --git a/input.c b/input.c index 4095c05d..e54b5ac0 100644 --- a/input.c +++ b/input.c @@ -2409,7 +2409,6 @@ input_osc_52(struct input_ctx *ictx, const char *p) outlen = 4 * ((len + 2) / 3) + 1; out = xmalloc(outlen); if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { - abort(); free(out); return; } diff --git a/mode-tree.c b/mode-tree.c index 75034675..20ac3226 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -480,7 +480,7 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, saved = mode_tree_find_item(&mtd->saved, tag); if (saved != NULL) { - if (parent == NULL || (parent != NULL && parent->expanded)) + if (parent == NULL || parent->expanded) mti->tagged = saved->tagged; mti->expanded = saved->expanded; } else if (expanded == -1) diff --git a/options.c b/options.c index 7d79cf30..7e12544d 100644 --- a/options.c +++ b/options.c @@ -365,7 +365,8 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, pr = cmd_parse_from_string(value, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: - *cause = xstrdup("empty command"); + if (cause != NULL) + *cause = xstrdup("empty command"); return (-1); case CMD_PARSE_ERROR: if (cause != NULL) diff --git a/window.c b/window.c index b08a5d35..1d83c247 100644 --- a/window.c +++ b/window.c @@ -1136,7 +1136,7 @@ window_pane_reset_mode(struct window_pane *wp) } else { log_debug("%s: next mode is %s", __func__, next->mode->name); wp->screen = next->screen; - if (next != NULL && next->mode->resize != NULL) + if (next->mode->resize != NULL) next->mode->resize(next, wp->sx, wp->sy); } wp->flags |= (PANE_REDRAW|PANE_CHANGED); -- cgit From 82e47403c6a8d6fff90f77e9262840050b8e6b2e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 31 May 2019 11:34:09 +0000 Subject: Allow % strings that are all numbers or %s, and fix a double free. Both reported by George Nachman, GitHub issues 1765 and 1766. --- cmd-parse.y | 10 +++++++--- control.c | 1 - 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index d5d12d95..0a627268 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -998,11 +998,15 @@ yylex(void) if (ch == '%') { /* - * % is a condition unless it is alone, then it is a - * token. + * % is a condition unless it is all % or all numbers, + * then it is a token. */ yylval.token = yylex_get_word('%'); - if (strcmp(yylval.token, "%") == 0) + for (cp = yylval.token; *cp != '\0'; cp++) { + if (*cp != '%' && !isdigit((u_char)*cp)) + break; + } + if (*cp == '\0') return (TOKEN); if (strcmp(yylval.token, "%if") == 0) { free(yylval.token); diff --git a/control.c b/control.c index b7ac3f62..16fa71bb 100644 --- a/control.c +++ b/control.c @@ -91,7 +91,6 @@ control_callback(struct client *c, int closed, __unused void *data) case CMD_PARSE_ERROR: item = cmdq_get_callback(control_error, pr->error); cmdq_append(c, item); - free(pr->error); break; case CMD_PARSE_SUCCESS: item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); -- cgit From 2c5f3074bcd3c4d1399a5cf3691430027452266f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 31 May 2019 21:41:17 +0000 Subject: Fix warnings, from Ben Boeckel. --- cmd-queue.c | 2 +- format.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 93dcaa53..1ee43508 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -202,7 +202,7 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, { struct cmdq_item *item, *first = NULL, *last = NULL; struct cmd *cmd; - struct cmdq_shared *shared; + struct cmdq_shared *shared = NULL; u_int group = 0; TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { diff --git a/format.c b/format.c index 0cf7ee64..a2174e8c 100644 --- a/format.c +++ b/format.c @@ -1502,7 +1502,7 @@ format_replace(struct format_tree *ft, 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; + const char *errptr, *copy, *cp, *marker = NULL; char *copy0, *condition, *found, *new; char *value, *left, *right; size_t valuelen; @@ -1550,8 +1550,6 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, limit = 0; if (fm->argc == 2 && fm->argv[1] != NULL) marker = fm->argv[1]; - else - marker = NULL; break; case 'l': modifiers |= FORMAT_LITERAL; -- cgit From 88ee5a1a00b475fd2b93ef00e71f527fe2e9520e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 31 May 2019 22:50:30 +0100 Subject: Add a crashing config. --- regress/conf/21867280ff7e99631046f9cc669b80d2.conf | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 regress/conf/21867280ff7e99631046f9cc669b80d2.conf diff --git a/regress/conf/21867280ff7e99631046f9cc669b80d2.conf b/regress/conf/21867280ff7e99631046f9cc669b80d2.conf new file mode 100644 index 00000000..43b142b4 --- /dev/null +++ b/regress/conf/21867280ff7e99631046f9cc669b80d2.conf @@ -0,0 +1,8 @@ +%if #{l:1} +set -g status-style fg=cyan,bg='#001040' +%elif #{l:1} +set -g status-style fg=white,bg='#400040' +%else +set -g status-style fg=white,bg='#800000' +%endif +bind ^X last-window -- cgit From 39ea8a2787a29265970d0ef47a879527bc30d1f8 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 1 Jun 2019 06:20:22 +0000 Subject: Need stdlib.h, from Ben Boeckel. --- cmd-parse.y | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd-parse.y b/cmd-parse.y index 0a627268..402ec806 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -23,6 +23,7 @@ #include #include #include +#include #include #include -- cgit From 3d29b97768530f55aab2d7466124a038bc72e1ce Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 1 Jun 2019 07:24:33 +0100 Subject: Link to file. --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 6e262ef5..3e892d7c 100644 --- a/.github/README.md +++ b/.github/README.md @@ -57,7 +57,7 @@ explains some ideas for tmux not yet developed. Please feel free to ask for clarifications on the mailing list if you're thinking of working on these or need further information. -Please read the CONTRIBUTING file before opening an issue. +Please read [this document](Contributing.md) before opening an issue. ## Documentation -- cgit From feff55b92fe5a8382aa0cfd56f961330fe13741a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 1 Jun 2019 07:26:39 +0100 Subject: Try this instead. --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 3e892d7c..1e7e9a42 100644 --- a/.github/README.md +++ b/.github/README.md @@ -57,7 +57,7 @@ explains some ideas for tmux not yet developed. Please feel free to ask for clarifications on the mailing list if you're thinking of working on these or need further information. -Please read [this document](Contributing.md) before opening an issue. +Please read [this document](./Contributing.md) before opening an issue. ## Documentation -- cgit From c2d79add3179c5062e642b604809a3d87b6ff360 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 1 Jun 2019 07:28:31 +0100 Subject: Should be caps. --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 1e7e9a42..9a95772c 100644 --- a/.github/README.md +++ b/.github/README.md @@ -57,7 +57,7 @@ explains some ideas for tmux not yet developed. Please feel free to ask for clarifications on the mailing list if you're thinking of working on these or need further information. -Please read [this document](./Contributing.md) before opening an issue. +Please read [this document](CONTRIBUTING.md) before opening an issue. ## Documentation -- cgit From b28b8312f2a40d3fe7321375b91329eca4efb698 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 1 Jun 2019 08:05:55 +0100 Subject: Mention CHANGES. --- .github/CONTRIBUTING.md | 9 ++++++++- .github/ISSUE_TEMPLATE.md | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 49e2b90b..3a589484 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,13 +2,20 @@ Before opening an issue, please ensure that: +- Your problem is a specific problem or question or suggestion, not a general + complaint. + - `$TERM` inside tmux is screen, screen-256color, tmux or tmux-256color. Check by running `echo $TERM` inside tmux. - You can reproduce the problem with the latest tmux release, or a build from Git master. -- Your question or issue is not covered in the manual (run man tmux). +- Your question or issue is not covered [in the + manual](https://man.openbsd.org/tmux.1) (run `man tmux`). + +- Your problem is not mentioned in the [CHANGES + file](https://raw.githubusercontent.com/tmux/tmux/master/CHANGES) file. - Nobody else has opened the same issue recently. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index ee457237..8bf1e66a 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,6 +3,9 @@ Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md before opening an issue. +If you have upgraded, make sure your issue is not covered in the CHANGES file +for your version: https://raw.githubusercontent.com/tmux/tmux/master/CHANGES + Describe the problem and the steps to reproduce. Add a minimal tmux config if necessary. Screenshots can be helpful, but no more than one or two. -- cgit From ec690208a3b416ce2b3713e3e882ba414afd8c0f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 1 Jun 2019 08:07:22 +0100 Subject: Move link. --- .github/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/README.md b/.github/README.md index 9a95772c..cfff69da 100644 --- a/.github/README.md +++ b/.github/README.md @@ -50,15 +50,14 @@ welcome. Please send by email to: tmux-users@googlegroups.com -Or open a GitHub issue or pull request. +Or open a GitHub issue or pull request. **Please read [this +document](CONTRIBUTING.md) before opening an issue.** There is [a TODO list](https://github.com/tmux/tmux/wiki/Contributing) which explains some ideas for tmux not yet developed. Please feel free to ask for clarifications on the mailing list if you're thinking of working on these or need further information. -Please read [this document](CONTRIBUTING.md) before opening an issue. - ## Documentation For documentation on using tmux, see the tmux.1 manpage. View it from the -- cgit From 2c6c3a1d27d3665a47d037965955d2bad1f81bcc Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 1 Jun 2019 16:21:37 +0000 Subject: If only one of -x or -y is given, use the calculated size for the other. Pointed out by Ben Boeckel. --- cmd-new-session.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 2fd13420..559c268c 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -242,8 +242,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) /* Create the new session. */ oo = options_create(global_s_options); - if (args_has(args, 'x') || args_has(args, 'y')) + if (args_has(args, 'x') || args_has(args, 'y')) { + if (!args_has(args, 'x')) + dsx = sx; + if (!args_has(args, 'y')) + dsy = sy; options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy); + } env = environ_create(); if (c != NULL && !args_has(args, 'E')) environ_update(global_s_options, c->environ, env); -- cgit From 900238a30657a477f3c62ba344fcc73fc0948ac7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 2 Jun 2019 07:10:15 +0000 Subject: yacc(1) copies its union so it is not a good place to store TAILQ_HEADs. Allocate them instead. Found from a problem reported by sthen@. --- cmd-parse.y | 263 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 140 insertions(+), 123 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 402ec806..e27cdcd8 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -65,7 +65,7 @@ struct cmd_parse_state { u_int escapes; char *error; - struct cmd_parse_commands commands; + struct cmd_parse_commands *commands; struct cmd_parse_scope *scope; TAILQ_HEAD(, cmd_parse_scope) stack; @@ -74,6 +74,7 @@ static struct cmd_parse_state parse_state; 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 *); %} @@ -88,9 +89,9 @@ static void cmd_parse_free_commands(struct cmd_parse_commands *); int flag; struct { int flag; - struct cmd_parse_commands commands; + struct cmd_parse_commands *commands; } elif; - struct cmd_parse_commands commands; + struct cmd_parse_commands *commands; struct cmd_parse_command *command; } @@ -115,45 +116,46 @@ lines : /* empty */ { struct cmd_parse_state *ps = &parse_state; - TAILQ_CONCAT(&ps->commands, &$1, entry); + ps->commands = $1; } statements : statement '\n' { - TAILQ_INIT(&$$); - TAILQ_CONCAT(&$$, &$1, entry); + $$ = $1; } | statements statement '\n' { - TAILQ_INIT(&$$); - TAILQ_CONCAT(&$$, &$1, entry); - TAILQ_CONCAT(&$$, &$2, entry); + $$ = $1; + TAILQ_CONCAT($$, $2, entry); + free($2); } - statement : condition { struct cmd_parse_state *ps = &parse_state; - TAILQ_INIT(&$$); if (ps->scope == NULL || ps->scope->flag) - TAILQ_CONCAT(&$$, &$1, entry); - else - cmd_parse_free_commands(&$1); + $$ = $1; + else { + $$ = cmd_parse_new_commands(); + cmd_parse_free_commands($1); + } } | assignment { - TAILQ_INIT(&$$); + $$ = xmalloc (sizeof *$$); + TAILQ_INIT($$); } | commands { struct cmd_parse_state *ps = &parse_state; - TAILQ_INIT(&$$); if (ps->scope == NULL || ps->scope->flag) - TAILQ_CONCAT(&$$, &$1, entry); - else - cmd_parse_free_commands(&$1); + $$ = $1; + else { + $$ = cmd_parse_new_commands(); + cmd_parse_free_commands($1); + } } expanded : FORMAT @@ -243,117 +245,119 @@ if_close : ENDIF condition : if_open '\n' statements if_close { - TAILQ_INIT(&$$); if ($1) - TAILQ_CONCAT(&$$, &$3, entry); - else - cmd_parse_free_commands(&$3); + $$ = $3; + else { + $$ = cmd_parse_new_commands(); + cmd_parse_free_commands($3); + } } | if_open '\n' statements if_else '\n' statements if_close { - TAILQ_INIT(&$$); if ($1) { - TAILQ_CONCAT(&$$, &$3, entry); - cmd_parse_free_commands(&$6); + $$ = $3; + cmd_parse_free_commands($6); } else { - TAILQ_CONCAT(&$$, &$6, entry); - cmd_parse_free_commands(&$3); + $$ = $6; + cmd_parse_free_commands($3); } } | if_open '\n' statements elif if_close { - TAILQ_INIT(&$$); if ($1) { - TAILQ_CONCAT(&$$, &$3, entry); - cmd_parse_free_commands(&$4.commands); + $$ = $3; + cmd_parse_free_commands($4.commands); } else if ($4.flag) { - TAILQ_CONCAT(&$$, &$4.commands, entry); - cmd_parse_free_commands(&$3); + $$ = $4.commands; + cmd_parse_free_commands($3); } else { - cmd_parse_free_commands(&$3); - cmd_parse_free_commands(&$4.commands); + $$ = cmd_parse_new_commands(); + cmd_parse_free_commands($3); + cmd_parse_free_commands($4.commands); } } | if_open '\n' statements elif if_else '\n' statements if_close { - TAILQ_INIT(&$$); if ($1) { - TAILQ_CONCAT(&$$, &$3, entry); - cmd_parse_free_commands(&$4.commands); - cmd_parse_free_commands(&$7); + $$ = $3; + cmd_parse_free_commands($4.commands); + cmd_parse_free_commands($7); } else if ($4.flag) { - TAILQ_CONCAT(&$$, &$4.commands, entry); - cmd_parse_free_commands(&$3); - cmd_parse_free_commands(&$7); + $$ = $4.commands; + cmd_parse_free_commands($3); + cmd_parse_free_commands($7); } else { - TAILQ_CONCAT(&$$, &$7, entry); - cmd_parse_free_commands(&$3); - cmd_parse_free_commands(&$4.commands); + $$ = $7; + cmd_parse_free_commands($3); + cmd_parse_free_commands($4.commands); } } elif : if_elif '\n' statements { - TAILQ_INIT(&$$.commands); - if ($1) - TAILQ_CONCAT(&$$.commands, &$3, entry); - else - cmd_parse_free_commands(&$3); - $$.flag = $1; + if ($1) { + $$.flag = 1; + $$.commands = $3; + } else { + $$.flag = 0; + $$.commands = cmd_parse_new_commands(); + cmd_parse_free_commands($3); + } } | if_elif '\n' statements elif { - TAILQ_INIT(&$$.commands); if ($1) { $$.flag = 1; - TAILQ_CONCAT(&$$.commands, &$3, entry); - cmd_parse_free_commands(&$4.commands); + $$.commands = $3; + cmd_parse_free_commands($4.commands); + } else if ($4.flag) { + $$.flag = 1; + $$.commands = $4.commands; + cmd_parse_free_commands($3); } else { - $$.flag = $4.flag; - TAILQ_CONCAT(&$$.commands, &$4.commands, entry); - cmd_parse_free_commands(&$3); + $$.flag = 0; + $$.commands = cmd_parse_new_commands(); + cmd_parse_free_commands($3); + cmd_parse_free_commands($4.commands); } } - commands : command { struct cmd_parse_state *ps = &parse_state; - TAILQ_INIT(&$$); + $$ = cmd_parse_new_commands(); if (ps->scope == NULL || ps->scope->flag) - TAILQ_INSERT_TAIL(&$$, $1, entry); + TAILQ_INSERT_TAIL($$, $1, entry); else cmd_parse_free_command($1); } | commands ';' { - TAILQ_INIT(&$$); - TAILQ_CONCAT(&$$, &$1, entry); + $$ = $1; } | commands ';' condition1 { - TAILQ_INIT(&$$); - TAILQ_CONCAT(&$$, &$1, entry); - TAILQ_CONCAT(&$$, &$3, entry); + $$ = $1; + TAILQ_CONCAT($$, $3, entry); + free($3); } | commands ';' command { struct cmd_parse_state *ps = &parse_state; - TAILQ_INIT(&$$); if (ps->scope == NULL || ps->scope->flag) { - TAILQ_CONCAT(&$$, &$1, entry); - TAILQ_INSERT_TAIL(&$$, $3, entry); + $$ = $1; + TAILQ_INSERT_TAIL($$, $3, entry); } else { - cmd_parse_free_commands(&$1); + $$ = cmd_parse_new_commands(); + cmd_parse_free_commands($1); cmd_parse_free_command($3); } } | condition1 { - TAILQ_INIT(&$$); - TAILQ_CONCAT(&$$, &$1, entry); + $$ = $1; } command : assignment TOKEN @@ -379,76 +383,80 @@ command : assignment TOKEN condition1 : if_open commands if_close { - TAILQ_INIT(&$$); if ($1) - TAILQ_CONCAT(&$$, &$2, entry); - else - cmd_parse_free_commands(&$2); + $$ = $2; + else { + $$ = cmd_parse_new_commands(); + cmd_parse_free_commands($2); + } } | if_open commands if_else commands if_close { - TAILQ_INIT(&$$); if ($1) { - TAILQ_CONCAT(&$$, &$2, entry); - cmd_parse_free_commands(&$4); + $$ = $2; + cmd_parse_free_commands($4); } else { - TAILQ_CONCAT(&$$, &$4, entry); - cmd_parse_free_commands(&$2); + $$ = $4; + cmd_parse_free_commands($2); } } | if_open commands elif1 if_close { - TAILQ_INIT(&$$); if ($1) { - TAILQ_CONCAT(&$$, &$2, entry); - cmd_parse_free_commands(&$3.commands); + $$ = $2; + cmd_parse_free_commands($3.commands); } else if ($3.flag) { - TAILQ_CONCAT(&$$, &$3.commands, entry); - cmd_parse_free_commands(&$2); + $$ = $3.commands; + cmd_parse_free_commands($2); } else { - cmd_parse_free_commands(&$2); - cmd_parse_free_commands(&$3.commands); + $$ = cmd_parse_new_commands(); + cmd_parse_free_commands($2); + cmd_parse_free_commands($3.commands); } } | if_open commands elif1 if_else commands if_close { - TAILQ_INIT(&$$); if ($1) { - TAILQ_CONCAT(&$$, &$2, entry); - cmd_parse_free_commands(&$3.commands); - cmd_parse_free_commands(&$5); + $$ = $2; + cmd_parse_free_commands($3.commands); + cmd_parse_free_commands($5); } else if ($3.flag) { - TAILQ_CONCAT(&$$, &$3.commands, entry); - cmd_parse_free_commands(&$2); - cmd_parse_free_commands(&$5); + $$ = $3.commands; + cmd_parse_free_commands($2); + cmd_parse_free_commands($5); } else { - TAILQ_CONCAT(&$$, &$5, entry); - cmd_parse_free_commands(&$2); - cmd_parse_free_commands(&$3.commands); + $$ = $5; + cmd_parse_free_commands($2); + cmd_parse_free_commands($3.commands); } - } elif1 : if_elif commands { - TAILQ_INIT(&$$.commands); - if ($1) - TAILQ_CONCAT(&$$.commands, &$2, entry); - else - cmd_parse_free_commands(&$2); - $$.flag = $1; + if ($1) { + $$.flag = 1; + $$.commands = $2; + } else { + $$.flag = 0; + $$.commands = cmd_parse_new_commands(); + cmd_parse_free_commands($2); + } } | if_elif commands elif1 { - TAILQ_INIT(&$$.commands); if ($1) { $$.flag = 1; - TAILQ_CONCAT(&$$.commands, &$2, entry); - cmd_parse_free_commands(&$3.commands); + $$.commands = $2; + cmd_parse_free_commands($3.commands); + } else if ($3.flag) { + $$.flag = 1; + $$.commands = $3.commands; + cmd_parse_free_commands($2); } else { - $$.flag = $3.flag; - TAILQ_CONCAT(&$$.commands, &$3.commands, entry); - cmd_parse_free_commands(&$2); + $$.flag = 0; + $$.commands = cmd_parse_new_commands(); + cmd_parse_free_commands($2); + cmd_parse_free_commands($3.commands); } } @@ -497,6 +505,16 @@ cmd_parse_free_command(struct cmd_parse_command *cmd) free(cmd); } +static struct cmd_parse_commands * +cmd_parse_new_commands(void) +{ + struct cmd_parse_commands *cmds; + + cmds = xmalloc(sizeof *cmds); + TAILQ_INIT (cmds); + return (cmds); +} + static void cmd_parse_free_commands(struct cmd_parse_commands *cmds) { @@ -506,17 +524,17 @@ cmd_parse_free_commands(struct cmd_parse_commands *cmds) TAILQ_REMOVE(cmds, cmd, entry); cmd_parse_free_command(cmd); } + free(cmds); } static struct cmd_parse_commands * cmd_parse_run_parser(char **cause) { - struct cmd_parse_state *ps = &parse_state; - struct cmd_parse_commands *cmds; - struct cmd_parse_scope *scope, *scope1; - int retval; + struct cmd_parse_state *ps = &parse_state; + struct cmd_parse_scope *scope, *scope1; + int retval; - TAILQ_INIT(&ps->commands); + ps->commands = NULL; TAILQ_INIT(&ps->stack); retval = yyparse(); @@ -529,10 +547,9 @@ cmd_parse_run_parser(char **cause) return (NULL); } - cmds = xmalloc(sizeof *cmds); - TAILQ_INIT(cmds); - TAILQ_CONCAT(cmds, &ps->commands, entry); - return (cmds); + if (ps->commands == NULL) + return (cmd_parse_new_commands()); + return (ps->commands); } static struct cmd_parse_commands * @@ -574,7 +591,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, /* Check for an empty list. */ if (TAILQ_EMPTY(cmds)) { - free(cmds); + cmd_parse_free_commands(cmds); pr.status = CMD_PARSE_EMPTY; return (&pr); } @@ -668,7 +685,6 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, out: cmd_parse_free_commands(cmds); - free(cmds); return (&pr); } @@ -747,8 +763,7 @@ cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) } cmd_log_argv(argc, argv, "%s", __func__); - cmds = xmalloc(sizeof *cmds); - TAILQ_INIT(cmds); + cmds = cmd_parse_new_commands(); copy = cmd_copy_argv(argc, argv); last = 0; @@ -801,6 +816,8 @@ cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) TAILQ_INSERT_TAIL(cmds, cmd, entry); } } + + cmd_free_argv(argc, copy); return (cmd_parse_build_commands(cmds, pi)); } -- cgit From 4ca1de1b8bc10dca47d2cf7399df41845c902c9e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 3 Jun 2019 18:28:37 +0000 Subject: Add new-session -X and attach-session -x to send SIGHUP to parent when detaching (like detach-client -P). From Colin Watson in GitHub issue 1773. --- cmd-attach-session.c | 27 ++++++++++++++++++--------- cmd-new-session.c | 7 ++++--- tmux.1 | 14 ++++++++++++-- tmux.h | 2 +- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index bcd6d895..6de734e5 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -37,8 +37,8 @@ const struct cmd_entry cmd_attach_session_entry = { .name = "attach-session", .alias = "attach", - .args = { "c:dErt:", 0, 0 }, - .usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, + .args = { "c:dErt:x", 0, 0 }, + .usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE, /* -t is special */ @@ -48,7 +48,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 rflag, const char *cflag, int Eflag) + int xflag, int rflag, const char *cflag, int Eflag) { struct cmd_find_state *current = &item->shared->current; enum cmd_find_type type; @@ -58,6 +58,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, struct winlink *wl; struct window_pane *wp; char *cause; + enum msgtype msgtype; if (RB_EMPTY(&sessions)) { cmdq_error(item, "no sessions"); @@ -102,11 +103,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, c->last_session = c->session; if (c->session != NULL) { - if (dflag) { + if (dflag || xflag) { + if (xflag) + msgtype = MSG_DETACHKILL; + else + msgtype = MSG_DETACH; TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - server_client_detach(c_loop, MSG_DETACH); + server_client_detach(c_loop, msgtype); } } if (!Eflag) @@ -131,11 +136,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (rflag) c->flags |= CLIENT_READONLY; - if (dflag) { + if (dflag || xflag) { + if (xflag) + msgtype = MSG_DETACHKILL; + else + msgtype = MSG_DETACH; TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - server_client_detach(c_loop, MSG_DETACH); + server_client_detach(c_loop, msgtype); } } if (!Eflag) @@ -169,6 +178,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item) struct args *args = self->args; return (cmd_attach_session(item, args_get(args, 't'), - args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'), - args_has(args, 'E'))); + args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'), + args_get(args, 'c'), args_has(args, 'E'))); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 559c268c..e0540815 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -39,8 +39,8 @@ const struct cmd_entry cmd_new_session_entry = { .name = "new-session", .alias = "new", - .args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 }, - .usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " + .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]", @@ -105,7 +105,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'A')) { retval = cmd_attach_session(item, newname, args_has(args, 'D'), - 0, NULL, args_has(args, 'E')); + args_has(args, 'X'), 0, NULL, + args_has(args, 'E')); free(newname); return (retval); } diff --git a/tmux.1 b/tmux.1 index d9b37c0e..6917e8b0 100644 --- a/tmux.1 +++ b/tmux.1 @@ -918,7 +918,7 @@ section. The following commands are available to manage clients and sessions: .Bl -tag -width Ds .It Xo Ic attach-session -.Op Fl dEr +.Op Fl dErx .Op Fl c Ar working-directory .Op Fl t Ar target-session .Xc @@ -931,6 +931,10 @@ If used from inside, switch the current client. If .Fl d is specified, any other clients attached to the session are detached. +If +.Fl x +is given, send SIGHUP to the parent process of the client as well as +detaching the client, typically causing it to exit. .Fl r signifies the client is read-only (only keys bound to the .Ic detach-client @@ -1048,7 +1052,7 @@ command. Lock all clients attached to .Ar target-session . .It Xo Ic new-session -.Op Fl AdDEP +.Op Fl AdDEPX .Op Fl c Ar start-directory .Op Fl F Ar format .Op Fl n Ar window-name @@ -1105,6 +1109,12 @@ already exists; in this case, behaves like .Fl d to +.Ic attach-session , +and +.Fl X +behaves like +.Fl x +to .Ic attach-session . .Pp If diff --git a/tmux.h b/tmux.h index 8eb1e65a..3849790c 100644 --- a/tmux.h +++ b/tmux.h @@ -2009,7 +2009,7 @@ extern const struct cmd_entry *cmd_table[]; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int, - const char *, int); + int, const char *, int); /* cmd-parse.c */ void cmd_parse_empty(struct cmd_parse_input *); -- cgit From 09e90c1645a8ecd1e467b13a743bb46d48febd37 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 5 Jun 2019 19:00:36 +0000 Subject: Need to increment the argument to skip the prefix earlier, fixes repeated incremental search in copy mode, reported by Kaushal Modi in GitHub issue 1780. --- window-copy.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/window-copy.c b/window-copy.c index 36ad6a83..d81073bf 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1668,9 +1668,11 @@ 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]; - enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; const char *ss = data->searchstr; + char prefix; + enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; + prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; data->searchy = data->cy; @@ -1681,13 +1683,11 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->oy = data->searcho; action = WINDOW_COPY_CMD_REDRAW; } - if (*argument == '\0') { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } - - switch (*argument++) { + switch (prefix) { case '=': case '-': data->searchtype = WINDOW_COPY_SEARCHUP; @@ -1717,9 +1717,11 @@ 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]; - enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; const char *ss = data->searchstr; + char prefix; + enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; + prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; data->searchy = data->cy; @@ -1730,13 +1732,11 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->oy = data->searcho; action = WINDOW_COPY_CMD_REDRAW; } - if (*argument == '\0') { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } - - switch (*argument++) { + switch (prefix) { case '=': case '+': data->searchtype = WINDOW_COPY_SEARCHDOWN; -- cgit From 8f40796f05f2db0ff8b2c9231054b62b511a7ba0 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 5 Jun 2019 20:00:53 +0000 Subject: Add a -v flag to source-file to show the commands and line numbers. --- cfg.c | 1 + cmd-parse.y | 17 +++++++++++++++++ cmd-source-file.c | 6 ++++-- tmux.1 | 4 +++- tmux.h | 1 + 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/cfg.c b/cfg.c index d6435980..c79150de 100644 --- a/cfg.c +++ b/cfg.c @@ -127,6 +127,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, pi.flags = flags; pi.file = path; pi.line = 1; + pi.item = item; pr = cmd_parse_from_file(f, &pi); fclose(f); diff --git a/cmd-parse.y b/cmd-parse.y index e27cdcd8..a7c12f62 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -76,6 +76,8 @@ 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 void cmd_parse_print_commands(struct cmd_parse_input *, u_int, + struct cmd_list *); %} @@ -497,6 +499,19 @@ cmd_parse_get_error(const char *file, u_int line, const char *error) return (s); } +static void +cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line, + struct cmd_list *cmdlist) +{ + char *s; + + if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) { + s = cmd_list_print(cmdlist, 0); + cmdq_print(pi->item, "%u: %s", line, s); + free(s); + } +} + static void cmd_parse_free_command(struct cmd_parse_command *cmd) { @@ -653,6 +668,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, if (cmdlist == NULL || cmd->line != line) { if (cmdlist != NULL) { + cmd_parse_print_commands(pi, line, cmdlist); cmd_list_move(result, cmdlist); cmd_list_free(cmdlist); } @@ -672,6 +688,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, cmd_list_append(cmdlist, add); } if (cmdlist != NULL) { + cmd_parse_print_commands(pi, line, cmdlist); cmd_list_move(result, cmdlist); cmd_list_free(cmdlist); } diff --git a/cmd-source-file.c b/cmd-source-file.c index d88f74cf..b867db67 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -38,8 +38,8 @@ const struct cmd_entry cmd_source_file_entry = { .name = "source-file", .alias = "source", - .args = { "nq", 1, -1 }, - .usage = "[-nq] path ...", + .args = { "nqv", 1, -1 }, + .usage = "[-nqv] path ...", .flags = 0, .exec = cmd_source_file_exec @@ -63,6 +63,8 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) flags |= CMD_PARSE_QUIET; if (args_has(args, 'n')) flags |= CMD_PARSE_PARSEONLY; + if (args_has(args, 'v')) + flags |= CMD_PARSE_VERBOSE; utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB); retval = CMD_RETURN_NORMAL; diff --git a/tmux.1 b/tmux.1 index 6917e8b0..9f97422c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1251,7 +1251,7 @@ and .Fl T show debugging information about jobs and terminals. .It Xo Ic source-file -.Op Fl nq +.Op Fl nqv .Ar path .Ar ... .Xc @@ -1269,6 +1269,8 @@ does not exist. With .Fl n , the file is parsed but no commands are executed. +.Fl v +shows the parsed commands and line numbers if possible. .It Ic start-server .D1 (alias: Ic start ) Start the diff --git a/tmux.h b/tmux.h index 3849790c..f8ceb95d 100644 --- a/tmux.h +++ b/tmux.h @@ -1324,6 +1324,7 @@ struct cmd_parse_input { #define CMD_PARSE_QUIET 0x1 #define CMD_PARSE_PARSEONLY 0x2 #define CMD_PARSE_NOALIAS 0x4 +#define CMD_PARSE_VERBOSE 0x8 const char *file; u_int line; -- cgit From e37f34facc05c3ba146d4158cc7af23f6886fecd Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 7 Jun 2019 20:09:17 +0000 Subject: Do not load the config file if the server is exiting because it failed to start, otherwise commands like lsk which start the server again can end up looping infinitely. Also make the first client exit correctly. Problem reported by Wael M Nasreddine. --- server-client.c | 6 ++++-- server.c | 10 ++++++---- tmux.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/server-client.c b/server-client.c index 52842e38..fa21fa79 100644 --- a/server-client.c +++ b/server-client.c @@ -1511,7 +1511,9 @@ server_client_click_timer(__unused int fd, __unused short events, void *data) static void server_client_check_exit(struct client *c) { - if (!(c->flags & CLIENT_EXIT)) + if (~c->flags & CLIENT_EXIT) + return; + if (c->flags & CLIENT_EXITED) return; if (EVBUFFER_LENGTH(c->stdin_data) != 0) @@ -1524,7 +1526,7 @@ 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_EXIT; + c->flags |= CLIENT_EXITED; } /* Redraw timer callback. */ diff --git a/server.c b/server.c index 18818c38..fc3730ad 100644 --- a/server.c +++ b/server.c @@ -44,7 +44,7 @@ struct clients clients; struct tmuxproc *server_proc; -static int server_fd; +static int server_fd = -1; static int server_exit; static struct event server_ev_accept; @@ -208,11 +208,10 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd, cmdq_append(c, cmdq_get_error(cause)); free(cause); c->flags |= CLIENT_EXIT; - } + } else + start_cfg(); - start_cfg(); server_add_accept(0); - proc_loop(server_proc, server_loop); job_kill_all(); @@ -364,6 +363,9 @@ server_add_accept(int timeout) { struct timeval tv = { timeout, 0 }; + if (server_fd == -1) + return; + if (event_initialized(&server_ev_accept)) event_del(&server_ev_accept); diff --git a/tmux.h b/tmux.h index f8ceb95d..76bf8303 100644 --- a/tmux.h +++ b/tmux.h @@ -1490,7 +1490,7 @@ struct client { #define CLIENT_REPEAT 0x20 #define CLIENT_SUSPENDED 0x40 #define CLIENT_ATTACHED 0x80 -/* 0x100 unused */ +#define CLIENT_EXITED 0x100 #define CLIENT_DEAD 0x200 #define CLIENT_REDRAWBORDERS 0x400 #define CLIENT_READONLY 0x800 -- cgit From 43796bf131c4d7d3a44185c10e5d75860869af0d Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 8 Jun 2019 21:48:29 +0000 Subject: Do not try to parse command when unsetting, GitHub issue 1788. --- options.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options.c b/options.c index 7e12544d..619c27ed 100644 --- a/options.c +++ b/options.c @@ -361,7 +361,7 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, return (-1); } - if (OPTIONS_IS_COMMAND(o)) { + if (OPTIONS_IS_COMMAND(o) && value != NULL) { pr = cmd_parse_from_string(value, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: -- cgit From 915097d312319a463fa4333c9bdf728b8dd6d39d Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 9 Jun 2019 06:50:24 +0000 Subject: Exiting alternate screen mode should restore cursor position and attributes even if already outside alternate screen mode. GitHub issue 1789. --- window.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/window.c b/window.c index 1d83c247..781ff30c 100644 --- a/window.c +++ b/window.c @@ -804,6 +804,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->pipe_event = NULL; wp->saved_grid = NULL; + wp->saved_cx = UINT_MAX; + wp->saved_cy = UINT_MAX; style_set(&wp->style, &grid_default_cell); @@ -962,10 +964,25 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, struct screen *s = &wp->base; u_int sx, sy; - if (wp->saved_grid == NULL) - return; if (!options_get_number(wp->window->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); @@ -976,17 +993,8 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, if (sy > wp->saved_grid->sy) screen_resize(s, sx, wp->saved_grid->sy, 1); - /* Restore the grid, cursor position and cell. */ + /* Restore the saved grid. */ grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); - if (cursor) - s->cx = wp->saved_cx; - if (s->cx > screen_size_x(s) - 1) - s->cx = screen_size_x(s) - 1; - if (cursor) - 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); /* * Turn history back on (so resize can use it) and then resize back to -- cgit From 1a9f9c09b4bcc9f99f10190ab91f1aea5206809b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 11 Jun 2019 13:09:00 +0000 Subject: Do not resize panes unless they are in an attached, active window. From Morten M Neergaard in GitHub issue 1782. --- server-client.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index fa21fa79..dc57e523 100644 --- a/server-client.c +++ b/server-client.c @@ -1241,6 +1241,8 @@ server_client_loop(void) struct client *c; struct window *w; struct window_pane *wp; + struct winlink *wl; + struct session *s; int focus; TAILQ_FOREACH(c, &clients, entry) { @@ -1257,8 +1259,13 @@ server_client_loop(void) */ focus = options_get_number(global_options, "focus-events"); RB_FOREACH(w, windows, &windows) { + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + s = wl->session; + if (s->attached != 0 && s->curw == wl) + break; + } TAILQ_FOREACH(wp, &w->panes, entry) { - if (wp->fd != -1) { + if (wl != NULL && wp->fd != -1) { if (focus) server_client_check_focus(wp); server_client_check_resize(wp); -- cgit From e8814a7e9308dfd4617ce01bc0aa3dba15f177c0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 11 Jun 2019 14:23:47 +0100 Subject: Add to CHANGES. --- CHANGES | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index edd4751b..200e68c8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ CHANGES FROM 3.0 to X.X -XXX +* Add new-session -X and attach-session -x to send SIGHUP to parent when + detaching (like detach-client -P). + +* Support for octal escapes in strings (such as \007). CHANGES FROM 2.9 to 3.0 -- cgit From 8e971f187ab97ff3f22d8e2f7f5050702c99e627 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 11 Jun 2019 14:27:40 +0100 Subject: Update README.md --- .github/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/README.md b/.github/README.md index cfff69da..ce12f6aa 100644 --- a/.github/README.md +++ b/.github/README.md @@ -53,10 +53,9 @@ tmux-users@googlegroups.com Or open a GitHub issue or pull request. **Please read [this document](CONTRIBUTING.md) before opening an issue.** -There is [a TODO list](https://github.com/tmux/tmux/wiki/Contributing) which -explains some ideas for tmux not yet developed. Please feel free to ask for -clarifications on the mailing list if you're thinking of working on these or -need further information. +There is [a list of suggestions for contributions](https://github.com/tmux/tmux/wiki/Contributing). +Please feel free to ask on the mailing list if you're thinking of working on something or need +further information. ## Documentation -- cgit From 9d42bd328c094c679415c623f7a87c200b64984e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 12 Jun 2019 08:08:33 +0000 Subject: Do not always resize the window back to its original size after applying a layout, let the normal window resize process do it. This means windows are not resized at all if window-size is manual, and are not resized multiple times if later attached to a different size terminal. --- layout-custom.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/layout-custom.c b/layout-custom.c index 9886afe1..4d9e818b 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -168,10 +168,7 @@ layout_parse(struct window *w, const char *layout) /* Update pane offsets and sizes. */ layout_fix_offsets(lc); layout_fix_panes(w); - - /* Then resize the layout back to the original window size. */ - layout_resize(w, sx, sy); - window_resize(w, sx, sy); + recalculate_sizes(); layout_print_cell(lc, __func__, 0); -- cgit From b12df0186139bb4761292629871205dc46d4c302 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 12 Jun 2019 08:47:07 +0000 Subject: Pass target on to new commands with if -F. --- cmd-if-shell.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 84f66657..f795575a 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -67,10 +67,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) struct cmd_if_shell_data *cdata; char *shellcmd, *cmd; struct cmdq_item *new_item; + struct cmd_find_state *fs = &item->target; 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 = fs->s; + struct winlink *wl = fs->wl; + struct window_pane *wp = fs->wp; struct cmd_parse_input pi; struct cmd_parse_result *pr; @@ -92,7 +93,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) pi.line = self->line; pi.item = item; pi.c = c; - cmd_find_copy_state(&pi.fs, &item->target); + cmd_find_copy_state(&pi.fs, fs); pr = cmd_parse_from_string(cmd, &pi); switch (pr->status) { @@ -103,7 +104,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, NULL, m, 0); + new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); cmdq_insert_after(item, new_item); cmd_list_free(pr->cmdlist); break; @@ -137,7 +138,7 @@ 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, &item->target); + 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) { -- cgit From 7e6a26cc9d857ffc53fdb2395aa5413ff1865f9e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 12 Jun 2019 09:10:29 +0000 Subject: Do not use $TMUX to find the session because for windows in multiple sessions it is wrong as often as it is right, and for windows in one session it is pointless. Instead check TMUX_PANE to find the pane and look for the MRU session as usual. GitHub issue 1793. --- cmd-find.c | 73 ++++++++------------------------------------------------------ 1 file changed, 9 insertions(+), 64 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index e5ef8b99..e3e14f55 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -76,38 +76,12 @@ static const char *cmd_find_pane_table[][2] = { { NULL, NULL } }; -/* Get session from TMUX if present. */ -static struct session * -cmd_find_try_TMUX(struct client *c) -{ - struct environ_entry *envent; - char tmp[256]; - long long pid; - u_int session; - struct session *s; - - envent = environ_find(c->environ, "TMUX"); - if (envent == NULL) - return (NULL); - - if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3) - return (NULL); - if (pid != getpid()) - return (NULL); - log_debug("%s: client %p TMUX %s (session $%u)", __func__, c, - envent->value, session); - - s = session_find_by_id(session); - if (s != NULL) - log_debug("%s: session $%u still exists", __func__, s->id); - return (s); -} - /* Find pane containing client if any. */ static struct window_pane * cmd_find_inside_pane(struct client *c) { struct window_pane *wp; + struct environ_entry *envent; if (c == NULL) return (NULL); @@ -116,6 +90,11 @@ cmd_find_inside_pane(struct client *c) if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0) break; } + if (wp == NULL) { + envent = environ_find(c->environ, "TMUX_PANE"); + if (envent != NULL) + wp = window_pane_find_by_id_str(envent->value); + } if (wp != NULL) log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty); return (wp); @@ -880,8 +859,6 @@ cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m, int flags) int cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) { - struct session *s; - struct winlink *wl; struct window_pane *wp; /* If no client, treat as from nothing. */ @@ -903,30 +880,6 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) if (wp == NULL) goto unknown_pane; - /* If we have a session in TMUX, see if it has this pane. */ - s = cmd_find_try_TMUX(c); - if (s != NULL) { - RB_FOREACH(wl, winlinks, &s->windows) { - if (window_has_pane(wl->window, wp)) - break; - } - if (wl != NULL) { - log_debug("%s: session $%u has pane %%%u", __func__, - s->id, wp->id); - - fs->s = s; - fs->wl = s->curw; /* use current session */ - fs->w = fs->wl->window; - fs->wp = fs->w->active; /* use active pane */ - - cmd_find_log_state(__func__, fs); - return (0); - } else { - log_debug("%s: session $%u does not have pane %%%u", - __func__, s->id, wp->id); - } - } - /* * Don't have a session, or it doesn't have this pane. Try all * sessions. @@ -948,17 +901,7 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) return (0); unknown_pane: - /* - * We're not running in a known pane, but maybe this client has TMUX - * in the environment. That'd give us a session. - */ - s = cmd_find_try_TMUX(c); - if (s != NULL) { - cmd_find_from_session(fs, s, flags); - return (0); - } - - /* Otherwise we need to guess. */ + /* We can't find the pane so need to guess. */ return (cmd_find_from_nothing(fs, flags)); } @@ -1006,6 +949,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, strlcat(tmp, "CANFAIL,", sizeof tmp); if (*tmp != '\0') tmp[strlen(tmp) - 1] = '\0'; + else + strlcat(tmp, "NONE", sizeof tmp); log_debug("%s: target %s, type %s, item %p, flags %s", __func__, target == NULL ? "none" : target, s, item, tmp); -- cgit From 3e72e98e3bdf3e814d6d20060247bb5f5dc859bb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 13 Jun 2019 19:46:00 +0000 Subject: Add regular expression support for the format search, match and substitute modifiers. --- Makefile | 2 +- format.c | 110 ++++++++++++++++++++++++++++++++++++++------------------------- regsub.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tmux.1 | 46 ++++++++++++++++++++------ tmux.h | 6 +++- window.c | 34 +++++++++++++++----- 6 files changed, 234 insertions(+), 62 deletions(-) create mode 100644 regsub.c diff --git a/Makefile b/Makefile index 07d0b450..4efeb3f7 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,6 @@ SRCS= alerts.c \ cmd-list-panes.c \ cmd-list-sessions.c \ cmd-list-windows.c \ - cmd-list.c \ cmd-load-buffer.c \ cmd-lock-server.c \ cmd-move-window.c \ @@ -96,6 +95,7 @@ SRCS= alerts.c \ paste.c \ proc.c \ procname.c \ + regsub.c \ resize.c \ screen-redraw.c \ screen-write.c \ diff --git a/format.c b/format.c index a2174e8c..c2a911f8 100644 --- a/format.c +++ b/format.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1247,7 +1248,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lmCbdtqETSWP<>", cp[0]) != NULL && + if (strchr("lbdtqETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -1268,7 +1269,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) } /* Now try single character with arguments. */ - if (strchr("s=", cp[0]) == NULL) + if (strchr("mCs=", cp[0]) == NULL) break; c = cp[0]; @@ -1329,39 +1330,67 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) return list; } -/* Perform substitution in string. */ +/* Match against an fnmatch(3) pattern or regular expression. */ static char * -format_substitute(const char *source, const char *from, const char *to) +format_match(struct format_modifier *fm, const char *pattern, const char *text) { - char *copy, *new; - const char *cp; - size_t fromlen, tolen, newlen, used; - - fromlen = strlen(from); - tolen = strlen(to); - - newlen = strlen(source) + 1; - copy = new = xmalloc(newlen); - - for (cp = source; *cp != '\0'; /* nothing */) { - if (strncmp(cp, from, fromlen) != 0) { - *new++ = *cp++; - continue; + const char *s = ""; + regex_t r; + int flags = 0; + + if (fm->argc >= 1) + s = fm->argv[0]; + if (strchr(s, 'r') == NULL) { + if (strchr(s, 'i') != NULL) + flags |= FNM_CASEFOLD; + if (fnmatch(pattern, text, flags) != 0) + return (xstrdup("0")); + } else { + flags = REG_EXTENDED|REG_NOSUB; + if (strchr(s, 'i') != NULL) + flags |= REG_ICASE; + if (regcomp(&r, pattern, flags) != 0) + return (xstrdup("0")); + if (regexec(&r, text, 0, NULL, 0) != 0) { + regfree(&r); + return (xstrdup("0")); } - used = new - copy; - - newlen += tolen; - copy = xrealloc(copy, newlen); + regfree(&r); + } + return (xstrdup("1")); +} - new = copy + used; - memcpy(new, to, tolen); +/* Perform substitution in string. */ +static char * +format_sub(struct format_modifier *fm, const char *text, const char *pattern, + const char *with) +{ + char *value; + int flags = REG_EXTENDED; + + if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL) + flags |= REG_ICASE; + value = regsub(pattern, with, text, flags); + if (value == NULL) + return (xstrdup(text)); + return (value); +} - new += tolen; - cp += fromlen; +/* Search inside pane. */ +static char * +format_search(struct format_modifier *fm, struct window_pane *wp, const char *s) +{ + int ignore = 0, regex = 0; + char *value; + + if (fm->argc >= 1) { + if (strchr(fm->argv[0], 'i') != NULL) + ignore = 1; + if (strchr(fm->argv[0], 'r') != NULL) + regex = 1; } - - *new = '\0'; - return (copy); + xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore)); + return (value); } /* Loop over sessions. */ @@ -1506,11 +1535,10 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, char *copy0, *condition, *found, *new; char *value, *left, *right; size_t valuelen; - int modifiers = 0, limit = 0; + int modifiers = 0, limit = 0, j; struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; struct format_modifier *sub = NULL; u_int i, count; - int j; /* Make a copy of the key. */ copy = copy0 = xstrndup(key, keylen); @@ -1537,18 +1565,18 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, search = fm; break; case 's': - if (fm->argc != 2) + if (fm->argc < 2) break; sub = fm; break; case '=': - if (fm->argc != 1 && fm->argc != 2) + if (fm->argc < 1) break; limit = strtonum(fm->argv[0], INT_MIN, INT_MAX, &errptr); if (errptr != NULL) limit = 0; - if (fm->argc == 2 && fm->argv[1] != NULL) + if (fm->argc >= 2 && fm->argv[1] != NULL) marker = fm->argv[1]; break; case 'l': @@ -1619,7 +1647,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, value = xstrdup("0"); } else { format_log(ft, "search '%s' pane %%%u", copy, wp->id); - xasprintf(&value, "%u", window_pane_search(wp, copy)); + value = format_search(fm, wp, copy); } } else if (cmp != NULL) { /* Comparison of left and right. */ @@ -1671,12 +1699,8 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, value = xstrdup("1"); else value = xstrdup("0"); - } else if (strcmp(cmp->modifier, "m") == 0) { - if (fnmatch(left, right, 0) == 0) - value = xstrdup("1"); - else - value = xstrdup("0"); - } + } else if (strcmp(cmp->modifier, "m") == 0) + value = format_match(fm, left, right); free(right); free(left); @@ -1754,8 +1778,8 @@ done: /* Perform substitution if any. */ if (sub != NULL) { - new = format_substitute(value, sub->argv[0], sub->argv[1]); - format_log(ft, "substituted '%s' to '%s: %s", sub->argv[0], + new = format_sub(sub, value, sub->argv[0], sub->argv[1]); + format_log(ft, "substituted '%s' to '%s': %s", sub->argv[0], sub->argv[1], new); free(value); value = new; diff --git a/regsub.c b/regsub.c new file mode 100644 index 00000000..53c83a8f --- /dev/null +++ b/regsub.c @@ -0,0 +1,98 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2019 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" + +static void +regsub_copy(char **buf, size_t *len, const char *text, size_t start, + size_t end) +{ + size_t add = end - start; + + *buf = xrealloc(*buf, (*len) + add + 1); + memcpy((*buf) + *len, text + start, add); + (*len) += add; +} + +static void +regsub_expand(char **buf, size_t *len, const char *with, const char *text, + regmatch_t *m, u_int n) +{ + const char *cp; + u_int i; + + for (cp = with; *cp != '\0'; cp++) { + if (*cp == '\\') { + cp++; + if (*cp >= '0' && *cp <= '9') { + i = *cp - '0'; + if (i < n && m[i].rm_so != m[i].rm_eo) { + regsub_copy(buf, len, text, m[i].rm_so, + m[i].rm_eo); + continue; + } + } + } + *buf = xrealloc(*buf, (*len) + 2); + (*buf)[(*len)++] = *cp; + } +} + +char * +regsub(const char *pattern, const char *with, const char *text, int flags) +{ + regex_t r; + regmatch_t m[10]; + size_t start, end, len = 0; + char *buf = NULL; + + if (*text == '\0') + return (xstrdup("")); + if (regcomp(&r, pattern, flags) != 0) + return (NULL); + + start = 0; + end = strlen(text); + + while (start != end) { + m[0].rm_so = start; + m[0].rm_eo = end; + + if (regexec(&r, text, nitems(m), m, REG_STARTEND) != 0) { + regsub_copy(&buf, &len, text, start, end); + break; + } + if (m[0].rm_so == m[0].rm_eo) { + regsub_copy(&buf, &len, text, start, end); + break; + } + + regsub_copy(&buf, &len, text, start, m[0].rm_so); + regsub_expand(&buf, &len, with, text, m, nitems(m)); + start = m[0].rm_eo; + } + buf[len] = '\0'; + + regfree(&r); + return (buf); +} diff --git a/tmux.1 b/tmux.1 index 9f97422c..21a30fff 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4012,25 +4012,45 @@ if running on .Ql myhost , otherwise by .Ql 0 . -An -.Ql m -specifies an -.Xr fnmatch 3 -comparison where the first argument is the pattern and the second the string to -compare, for example -.Ql #{m:*foo*,#{host}} . .Ql || and .Ql && evaluate to true if either or both of two comma-separated alternatives are true, for example .Ql #{||:#{pane_in_mode},#{alternate_on}} . +.Pp +An +.Ql m +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: +.Ql r +means the pattern is a regular expression instead of the default +.Xr fnmatch 3 +pattern, and +.Ql i +means to ignore case. +For example: +.Ql #{m:*foo*,#{host}} +or +.Ql #{m/ri:^A,MYVAR} . A .Ql C performs a search for an .Xr fnmatch 3 -pattern in the pane content and evaluates to zero if not found, or a line -number if found. +pattern or regular expression in the pane content and evaluates to zero if not +found, or a line number if found. +Like +.Ql m , +a +.Ql r +flag means search for a regular expression and +.Ql i +ignores case. +For example: +.Ql #{C/r:^Start} .Pp A limit may be placed on the length of the resultant string by prefixing it by an @@ -4103,6 +4123,14 @@ will substitute with .Ql bar throughout. +The first argument may be an extended regular expression and a final argument may be +.Ql i +to ignore case, for example +.Ql s/a(.)/\1x/i: +would change +.Ql abABab +into +.Ql bxBxbx . .Pp In addition, the first line of a shell command's output may be inserted using .Ql #() . diff --git a/tmux.h b/tmux.h index 76bf8303..3324acba 100644 --- a/tmux.h +++ b/tmux.h @@ -2396,7 +2396,8 @@ void window_pane_key(struct window_pane *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); -u_int window_pane_search(struct window_pane *, const char *); +u_int window_pane_search(struct window_pane *, const char *, int, + int); const char *window_printable_flags(struct winlink *); struct window_pane *window_pane_find_up(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *); @@ -2631,4 +2632,7 @@ int style_is_default(struct style *); struct winlink *spawn_window(struct spawn_context *, char **); struct window_pane *spawn_pane(struct spawn_context *, char **); +/* regsub.c */ +char *regsub(const char *, const char *, const char *, int); + #endif /* TMUX_H */ diff --git a/window.c b/window.c index 781ff30c..e89cf96c 100644 --- a/window.c +++ b/window.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1206,24 +1207,41 @@ window_pane_visible(struct window_pane *wp) } u_int -window_pane_search(struct window_pane *wp, const char *searchstr) +window_pane_search(struct window_pane *wp, const char *term, int regex, + int ignore) { struct screen *s = &wp->base; - char *newsearchstr, *line; + regex_t r; + char *new = NULL, *line; u_int i; + int flags = 0, found; - xasprintf(&newsearchstr, "*%s*", searchstr); + if (!regex) { + if (ignore) + flags |= FNM_CASEFOLD; + xasprintf(&new, "*%s*", term); + } else { + if (ignore) + flags |= REG_ICASE; + if (regcomp(&r, term, flags|REG_EXTENDED) != 0) + return (0); + } for (i = 0; i < screen_size_y(s); i++) { line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); - if (fnmatch(newsearchstr, line, 0) == 0) { - free(line); - break; - } + if (!regex) + found = (fnmatch(new, line, 0) == 0); + else + found = (regexec(&r, line, 0, NULL, 0) == 0); free(line); + if (found) + break; } + if (!regex) + free(new); + else + regfree(&r); - free(newsearchstr); if (i == screen_size_y(s)) return (0); return (i + 1); -- cgit From 26b9a8e49b7eee7b7c05f85c731f5829e64c507c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 13 Jun 2019 20:38:05 +0000 Subject: Set the cursor x at the same time as changing the y or the end of line marker may not be redrawn. --- window-copy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/window-copy.c b/window-copy.c index d81073bf..a7cb5dfa 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3027,8 +3027,8 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) window_copy_other_end(wme); - data->cx = data->lastcx; if (scroll_only || data->cy == 0) { + data->cx = data->lastcx; window_copy_scroll_down(wme, 1); if (scroll_only) { if (data->cy == screen_size_y(s) - 1) @@ -3037,7 +3037,7 @@ 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->cx, data->cy - 1); + window_copy_update_cursor(wme, data->lastcx, data->cy - 1); if (window_copy_update_selection(wme, 1)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wme, data->cy, 1); @@ -3077,13 +3077,13 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) window_copy_other_end(wme); - data->cx = data->lastcx; if (scroll_only || data->cy == screen_size_y(s) - 1) { + 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->cx, data->cy + 1); + window_copy_update_cursor(wme, data->lastcx, data->cy + 1); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy - 1, 2); } -- cgit From b8a9c740bbb0f28ce7cbfeeec584e0719950b8a8 Mon Sep 17 00:00:00 2001 From: jmc Date: Thu, 13 Jun 2019 21:04:21 +0000 Subject: tweak previous; --- tmux.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 21a30fff..e5d837da 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4044,7 +4044,7 @@ pattern or regular expression in the pane content and evaluates to zero if not found, or a line number if found. Like .Ql m , -a +an .Ql r flag means search for a regular expression and .Ql i @@ -4126,7 +4126,7 @@ throughout. The first argument may be an extended regular expression and a final argument may be .Ql i to ignore case, for example -.Ql s/a(.)/\1x/i: +.Ql s/a(.)/\e1x/i: would change .Ql abABab into -- cgit From eef11b64e1a41db349b1a8a54b2f61e460483b07 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 13 Jun 2019 21:24:09 +0000 Subject: Do not crash if the environment variable is present but empty. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index c2a911f8..e1a6360a 100644 --- a/format.c +++ b/format.c @@ -1097,7 +1097,7 @@ format_find(struct format_tree *ft, const char *key, int modifiers) envent = environ_find(ft->s->environ, key); if (envent == NULL) envent = environ_find(global_environ, key); - if (envent != NULL) { + if (envent != NULL && envent->value != NULL) { found = xstrdup(envent->value); goto found; } -- cgit From f35f15b1077eea1d16037d2b226bf7fceb58b8fa Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 13 Jun 2019 21:44:13 +0000 Subject: Use the right client when working out where to save or load the buffer, reported by kn@. --- cmd-load-buffer.c | 2 +- cmd-save-buffer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 47cb0ca2..3e669093 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -93,7 +93,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_WAIT); } - file = server_client_get_path(c, path); + file = server_client_get_path(item->client, path); free(path); f = fopen(file, "rb"); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index f6ac93a6..a8d43f87 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) if (args_has(self->args, 'a')) flags = "ab"; - file = server_client_get_path(c, path); + file = server_client_get_path(item->client, path); free(path); f = fopen(file, flags); -- cgit From 45203582ff8708042c5f4f7134ad7d4ec71d2050 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 14 Jun 2019 12:04:11 +0000 Subject: A couple of minor parser changes around conditions: 1) only treat #{ specially after a condition, otherwise as a comment (which is more as most people expect) 2) allow formats to be quoted after a condition. --- cmd-parse.y | 26 ++++++++++++++++++++------ tmux.1 | 4 ++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index a7c12f62..1a6af3e7 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -59,6 +59,7 @@ struct cmd_parse_state { size_t len; size_t off; + int condition; int eol; int eof; struct cmd_parse_input *input; @@ -104,7 +105,7 @@ static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, %token ENDIF %token FORMAT TOKEN EQUALS -%type argument expanded +%type argument expanded format %type arguments %type if_open if_elif %type elif elif1 @@ -160,7 +161,16 @@ statement : condition } } -expanded : FORMAT +format : FORMAT + { + $$ = $1; + } + | TOKEN + { + $$ = $1; + } + +expanded : format { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_input *pi = ps->input; @@ -967,12 +977,15 @@ yylex(void) { struct cmd_parse_state *ps = &parse_state; char *token, *cp; - int ch, next; + int ch, next, condition; if (ps->eol) ps->input->line++; ps->eol = 0; + condition = ps->condition; + ps->condition = 0; + for (;;) { ch = yylex_getc(); @@ -1012,11 +1025,11 @@ yylex(void) if (ch == '#') { /* - * #{ opens a format; anything else is a comment, - * ignore up to the end of the line. + * #{ after a condition opens a format; anything else + * is a comment, ignore up to the end of the line. */ next = yylex_getc(); - if (next == '{') { + if (condition && next == '{') { yylval.token = yylex_format(); if (yylval.token == NULL) return (ERROR); @@ -1043,6 +1056,7 @@ yylex(void) } if (*cp == '\0') return (TOKEN); + ps->condition = 1; if (strcmp(yylval.token, "%if") == 0) { free(yylval.token); return (IF); diff --git a/tmux.1 b/tmux.1 index e5d837da..041097b6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -582,9 +582,9 @@ or .Ql %endif . For example: .Bd -literal -offset indent -%if #{==:#{host},myhost} +%if "#{==:#{host},myhost}" set -g status-style bg=red -%elif #{==:#{host},myotherhost} +%elif "#{==:#{host},myotherhost}" set -g status-style bg=green %else set -g status-style bg=blue -- cgit From d1d3bbb458b50ec455d65774d5c6669546b3b4ca Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 14 Jun 2019 13:34:45 +0000 Subject: Show filename with -v for source-file. --- cmd-parse.y | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd-parse.y b/cmd-parse.y index 1a6af3e7..1dbc27a7 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -517,7 +517,10 @@ cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line, if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) { s = cmd_list_print(cmdlist, 0); - cmdq_print(pi->item, "%u: %s", line, s); + 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); } } -- cgit From 7bb8ab1c0efefebcaeb25c5694bc8ac6506a7396 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 14 Jun 2019 12:40:35 +0100 Subject: Add a bit to {}. --- CHANGES | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index abd91a80..1d033877 100644 --- a/CHANGES +++ b/CHANGES @@ -12,7 +12,9 @@ CHANGES FROM 2.9 to 3.0 take other commands as string arguments to be expressed more clearly and without additional escaping. - A literal { and } must now be escaped or quoted, for example '{' and '}'. + A literal { and } or a string containing { or } must now be escaped or + quoted, for example '{' and '}' instead of { or }, or 'X#{foo}' instead of + X#{foo}. * New <, >, <= and >= comparison operators for formats. -- cgit From 8da756c4f09d436c130e702b5e66d853fe390d67 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 14 Jun 2019 16:02:34 +0100 Subject: Restore missing functions. --- cmd.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/cmd.c b/cmd.c index 81073ec1..74d2f5ef 100644 --- a/cmd.c +++ b/cmd.c @@ -204,6 +204,8 @@ const struct cmd_entry *cmd_table[] = { NULL }; +static u_int cmd_list_next_group = 1; + void printflike(3, 4) cmd_log_argv(int argc, char **argv, const char *fmt, ...) { @@ -501,6 +503,83 @@ cmd_print(struct cmd *cmd) return (out); } +struct cmd_list * +cmd_list_new(void) +{ + struct cmd_list *cmdlist; + + cmdlist = xcalloc(1, sizeof *cmdlist); + cmdlist->references = 1; + cmdlist->group = cmd_list_next_group++; + TAILQ_INIT(&cmdlist->list); + return (cmdlist); +} + +void +cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) +{ + cmd->group = cmdlist->group; + TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); +} + +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); + } + cmdlist->group = cmd_list_next_group++; +} + +void +cmd_list_free(struct cmd_list *cmdlist) +{ + struct cmd *cmd, *cmd1; + + if (--cmdlist->references != 0) + return; + + TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) { + TAILQ_REMOVE(&cmdlist->list, cmd, qentry); + cmd_free(cmd); + } + + free(cmdlist); +} + +char * +cmd_list_print(struct cmd_list *cmdlist, int escaped) +{ + struct cmd *cmd; + char *buf, *this; + size_t len; + + len = 1; + buf = xcalloc(1, len); + + TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { + this = cmd_print(cmd); + + len += strlen(this) + 4; + buf = xrealloc(buf, len); + + strlcat(buf, this, len); + if (TAILQ_NEXT(cmd, qentry) != NULL) { + if (escaped) + strlcat(buf, " \\; ", len); + else + strlcat(buf, " ; ", len); + } + + free(this); + } + + return (buf); +} + /* Adjust current mouse position for a pane. */ int cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, -- cgit From ae83a5b0105942abdb154502745897cdc8a15d4d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 14 Jun 2019 16:09:49 +0100 Subject: Add to CHANGES. --- CHANGES | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 1d033877..59c11157 100644 --- a/CHANGES +++ b/CHANGES @@ -1,9 +1,24 @@ CHANGES FROM 3.0 to X.X +* Resize panes lazily - do not resize unless they are in an attached, active + window. + +* Add regular expression support for the format search, match and substitute + modifiers and make them able to ignore case. + +* Do not use $TMUX to find the session because for windows in multiple sessions + it is wrong as often as it is right, and for windows in one session it is + pointless. Instead use TMUX_PANE if it is present. + +* Do not always resize the window back to its original size after applying a + layout, keep it at the layout size until it must be resized (for example when + attached and window-size is not manual). + * Add new-session -X and attach-session -x to send SIGHUP to parent when detaching (like detach-client -P). -* Support for octal escapes in strings (such as \007). +* Support for octal escapes in strings (such as \007) and improve list-keys + output so it parses correctly if copied into a configuration file. CHANGES FROM 2.9 to 3.0 -- cgit From c95cd9ed5e5e89e1ed467dbd5b7d8c80f672a5a1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 14 Jun 2019 15:35:58 +0000 Subject: Do not loop forever if a menu item contains invisible characters, reported by Thomas Sattler. --- format-draw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/format-draw.c b/format-draw.c index f8882ac2..b589ca5e 100644 --- a/format-draw.c +++ b/format-draw.c @@ -791,7 +791,8 @@ format_width(const char *expanded) } else if (*cp > 0x1f && *cp < 0x7f) { width++; cp++; - } + } else + cp++; } return (width); } -- cgit From 03da0ced46e0ddcfbdcc6d580d906f47279cabcd Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 15 Jun 2019 06:33:48 +0000 Subject: Use the right format modifier when comparing, and remove a couple of unused variables. --- format.c | 2 +- layout-custom.c | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index e1a6360a..75521fd0 100644 --- a/format.c +++ b/format.c @@ -1700,7 +1700,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, else value = xstrdup("0"); } else if (strcmp(cmp->modifier, "m") == 0) - value = format_match(fm, left, right); + value = format_match(cmp, left, right); free(right); free(left); diff --git a/layout-custom.c b/layout-custom.c index 4d9e818b..bedcaf10 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -122,7 +122,7 @@ layout_parse(struct window *w, const char *layout) { struct layout_cell *lc, *lcchild; struct window_pane *wp; - u_int npanes, ncells, sx, sy; + u_int npanes, ncells; u_short csum; /* Check validity. */ @@ -153,8 +153,7 @@ layout_parse(struct window *w, const char *layout) layout_destroy_cell(w, lcchild, &lc); } - /* Save the old window size and resize to the layout size. */ - sx = w->sx; sy = w->sy; + /* Resize to the layout size. */ window_resize(w, lc->sx, lc->sy); /* Destroy the old layout and swap to the new. */ -- cgit From 9272fe36e2e36789342337d81914008826499941 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 18 Jun 2019 11:08:42 +0000 Subject: Add a cmdq_continue function rather than twiddling the flag directly. --- cfg.c | 2 +- cmd-display-panes.c | 2 +- cmd-if-shell.c | 2 +- cmd-load-buffer.c | 2 +- cmd-queue.c | 7 +++++++ cmd-run-shell.c | 2 +- cmd-wait-for.c | 8 ++++---- menu.c | 2 +- tmux.h | 1 + window.c | 2 +- 10 files changed, 19 insertions(+), 11 deletions(-) diff --git a/cfg.c b/cfg.c index c79150de..286ed460 100644 --- a/cfg.c +++ b/cfg.c @@ -53,7 +53,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data) cfg_show_causes(RB_MIN(sessions, &sessions)); if (cfg_item != NULL) - cfg_item->flags &= ~CMDQ_WAITING; + cmdq_continue(cfg_item); status_prompt_load_history(); diff --git a/cmd-display-panes.c b/cmd-display-panes.c index aeeb6936..aa665f08 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -188,7 +188,7 @@ cmd_display_panes_free(struct client *c) struct cmd_display_panes_data *cdata = c->overlay_data; if (cdata->item != NULL) - cdata->item->flags &= ~CMDQ_WAITING; + cmdq_continue(cdata->item); free(cdata->command); free(cdata); } diff --git a/cmd-if-shell.c b/cmd-if-shell.c index f795575a..a992602c 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -196,7 +196,7 @@ cmd_if_shell_callback(struct job *job) out: if (cdata->item != NULL) - cdata->item->flags &= ~CMDQ_WAITING; + cmdq_continue(cdata->item); } static void diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 3e669093..cdf44bf7 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -176,7 +176,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) free(cause); } out: - cdata->item->flags &= ~CMDQ_WAITING; + cmdq_continue(cdata->item); free(cdata->bufname); free(cdata); diff --git a/cmd-queue.c b/cmd-queue.c index 1ee43508..ef68d5d5 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -156,6 +156,13 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, free(name); } +/* Continue processing command queue. */ +void +cmdq_continue(struct cmdq_item *item) +{ + item->flags &= ~CMDQ_WAITING; +} + /* Remove an item. */ static void cmdq_remove(struct cmdq_item *item) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index c9a478c7..2f45f492 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -155,7 +155,7 @@ cmd_run_shell_callback(struct job *job) free(msg); if (cdata->item != NULL) - cdata->item->flags &= ~CMDQ_WAITING; + cmdq_continue(cdata->item); } static void diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 33600eda..4f438a7f 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -153,7 +153,7 @@ cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name, log_debug("signal wait channel %s, with waiters", wc->name); TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { - wi->item->flags &= ~CMDQ_WAITING; + cmdq_continue(wi->item); TAILQ_REMOVE(&wc->waiters, wi, entry); free(wi); @@ -229,7 +229,7 @@ cmd_wait_for_unlock(struct cmdq_item *item, const char *name, } if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) { - wi->item->flags &= ~CMDQ_WAITING; + cmdq_continue(wi->item); TAILQ_REMOVE(&wc->lockers, wi, entry); free(wi); } else { @@ -248,13 +248,13 @@ cmd_wait_for_flush(void) RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { - wi->item->flags &= ~CMDQ_WAITING; + cmdq_continue(wi->item); TAILQ_REMOVE(&wc->waiters, wi, entry); free(wi); } wc->woken = 1; TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) { - wi->item->flags &= ~CMDQ_WAITING; + cmdq_continue(wi->item); TAILQ_REMOVE(&wc->lockers, wi, entry); free(wi); } diff --git a/menu.c b/menu.c index fd35399b..cc21c796 100644 --- a/menu.c +++ b/menu.c @@ -161,7 +161,7 @@ menu_free_cb(struct client *c) struct menu_data *md = c->overlay_data; if (md->item != NULL) - md->item->flags &= ~CMDQ_WAITING; + cmdq_continue(md->item); if (md->cb != NULL) md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); diff --git a/tmux.h b/tmux.h index 3324acba..cc5e55c0 100644 --- a/tmux.h +++ b/tmux.h @@ -2037,6 +2037,7 @@ void cmdq_insert_after(struct cmdq_item *, struct cmdq_item *); void 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 *); diff --git a/window.c b/window.c index e89cf96c..e7941c70 100644 --- a/window.c +++ b/window.c @@ -1512,7 +1512,7 @@ window_pane_input_callback(struct client *c, int closed, void *data) c->stdin_callback = NULL; server_client_unref(c); - cdata->item->flags &= ~CMDQ_WAITING; + cmdq_continue(cdata->item); free(cdata); return; -- cgit From 250fdd08bea74f47fc9a8962d5688db353896921 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 18 Jun 2019 11:17:40 +0000 Subject: Handle comments more correctly inside {}, from Avi Halachmi. --- cmd-parse.y | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 1dbc27a7..6d2b970c 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -1338,8 +1338,8 @@ static int yylex_token_brace(char **buf, size_t *len) { struct cmd_parse_state *ps = &parse_state; - int ch, nesting = 1, escape = 0, quote = '\0'; - int lines = 0; + int ch, lines = 0, nesting = 1, escape = 0; + int quote = '\0', token = 0; /* * Extract a string up to the matching unquoted '}', including newlines @@ -1349,6 +1349,10 @@ yylex_token_brace(char **buf, size_t *len) * 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. */ @@ -1368,6 +1372,8 @@ yylex_token_brace(char **buf, size_t *len) ch == '\n' || ch == '\\')) { escape = 0; + if (ch != '\n') + token = 1; continue; } @@ -1383,7 +1389,7 @@ yylex_token_brace(char **buf, size_t *len) /* A newline always resets to unquoted. */ if (ch == '\n') { - quote = 0; + quote = token = 0; continue; } @@ -1394,33 +1400,47 @@ yylex_token_brace(char **buf, size_t *len) */ if (ch == quote && quote != '#') quote = 0; - } else { + token = 1; /* token continues regardless */ + } else { /* Not inside quotes or comment. */ switch (ch) { case '"': case '\'': case '#': - /* Beginning of quote or comment. */ - quote = ch; + /* 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. + * Update line count after error as reporting the opening line is more + * useful than EOF. */ yyerror("unterminated brace string"); ps->input->line += lines; -- cgit From df07723e2093762d1cffc1a3f542e20853c42101 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 18 Jun 2019 19:42:52 +0100 Subject: Add a config. --- regress/conf/99749670b62bcb99a9b2e3d59708e357.conf | 93 ++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 regress/conf/99749670b62bcb99a9b2e3d59708e357.conf diff --git a/regress/conf/99749670b62bcb99a9b2e3d59708e357.conf b/regress/conf/99749670b62bcb99a9b2e3d59708e357.conf new file mode 100644 index 00000000..dd1700b0 --- /dev/null +++ b/regress/conf/99749670b62bcb99a9b2e3d59708e357.conf @@ -0,0 +1,93 @@ +# ----------------------------------------------------------------------------- +# This config is targeted for tmux 2.1+ and should be placed in $HOME. +# +# Read the "Plugin Manager" section (bottom) before trying to use this config! +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# Global options +# ----------------------------------------------------------------------------- + +# Set a new prefix / leader key. +set -g prefix ` +bind ` send-prefix + +# Allow opening multiple terminals to view the same session at different sizes. +setw -g aggressive-resize on + +# Remove delay when switching between Vim modes. +set -s escape-time 0 + +# Allow Vim's FocusGained to work when your terminal gains focus. +# Requires Vim plugin: https://github.com/tmux-plugins/vim-tmux-focus-events +set -g focus-events on + +# Add a bit more scroll history in the buffer. +set -g history-limit 50000 + +# Enable color support inside of tmux. +set -g default-terminal "screen-256color" + +# Ensure window titles get renamed automatically. +setw -g automatic-rename + +# Start windows and panes index at 1, not 0. +set -g base-index 1 +setw -g pane-base-index 1 + +# Enable full mouse support. +set -g mouse on + +# Status bar optimized for Gruvbox. +set -g status-fg colour244 +set -g status-bg default +set -g status-left '' +set -g status-right-length 0 +#set -g status-right-length 20 +#set -g status-right '%a %Y-%m-%d %H:%M' + +set -g pane-border-fg default +set -g pane-border-bg default +set -g pane-active-border-fg colour250 +set -g pane-active-border-bg default + +set-window-option -g window-status-current-attr bold +set-window-option -g window-status-current-fg colour223 + +# ----------------------------------------------------------------------------- +# Key bindings +# ----------------------------------------------------------------------------- + +# Unbind default keys +unbind C-b +unbind '"' +unbind % + +# Reload the tmux config. +bind-key r source-file ~/.tmux.conf + +# Split panes. +bind-key h split-window -v +bind-key v split-window -h + +# Move around panes with ALT + arrow keys. +bind-key -n M-Up select-pane -U +bind-key -n M-Left select-pane -L +bind-key -n M-Down select-pane -D +bind-key -n M-Right select-pane -R + +# ----------------------------------------------------------------------------- +# Plugin Manager - https://github.com/tmux-plugins/tpm +# In order to use the plugins below you need to install TPM and the plugins. +# Step 1) git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm +# Step 2) Reload tmux if it's already started with `r +# Step 3) Launch tmux and hit `I (capital i) to fetch any plugins +# ----------------------------------------------------------------------------- + +# List of plugins. +set -g @plugin 'tmux-plugins/tpm' +set -g @plugin 'tmux-plugins/tmux-resurrect' +set -g @plugin 'tmux-plugins/tmux-yank' + +# Initialize TPM (keep this line at the very bottom of your tmux.conf). +run -b '~/.tmux/plugins/tpm/tpm' -- cgit From b3ca410bc398e9aa7a045cf1337ca3f08c005e6e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 20 Jun 2019 06:57:37 +0100 Subject: Use KERN_PROC_CWD on NetBSD, from Leonardo Taccari. --- osdep-netbsd.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osdep-netbsd.c b/osdep-netbsd.c index daca1abb..67894175 100644 --- a/osdep-netbsd.c +++ b/osdep-netbsd.c @@ -133,13 +133,27 @@ char * osdep_get_cwd(int fd) { static char target[PATH_MAX + 1]; - char *path; pid_t pgrp; +#ifdef KERN_PROC_CWD + int mib[4]; + size_t len; +#else + char *path; ssize_t n; +#endif if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); +#ifdef KERN_PROC_CWD + mib[0] = CTL_KERN; + mib[1] = KERN_PROC_ARGS; + mib[2] = pgrp; + mib[3] = KERN_PROC_CWD; + len = sizeof(target); + if (sysctl(mib, __arraycount(mib), target, &len, NULL, 0) == 0) + return (target); +#else xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); n = readlink(path, target, sizeof(target) - 1); free(path); @@ -147,6 +161,7 @@ osdep_get_cwd(int fd) target[n] = '\0'; return (target); } +#endif return (NULL); } -- cgit From ae541287d303fb253b3cd75341c318dcc6a8db4a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Jun 2019 06:51:36 +0000 Subject: Expand command formats in %if and move the config file loading later (to when the first client has identified) so all the client formats are available, fixes problems reported by Thomas Sattler. --- cfg.c | 7 ++++--- format.c | 4 ++-- server-client.c | 39 +++++++++++++++++++++------------------ server.c | 3 +-- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/cfg.c b/cfg.c index 286ed460..a47621a4 100644 --- a/cfg.c +++ b/cfg.c @@ -91,14 +91,14 @@ start_cfg(void) } if (cfg_file == NULL) - load_cfg(TMUX_CONF, NULL, NULL, CMD_PARSE_QUIET, NULL); + load_cfg(TMUX_CONF, c, NULL, CMD_PARSE_QUIET, NULL); if (cfg_file == NULL && (home = find_home()) != NULL) { xasprintf(&cfg_file, "%s/.tmux.conf", home); flags = CMD_PARSE_QUIET; } if (cfg_file != NULL) - load_cfg(cfg_file, NULL, NULL, flags, NULL); + load_cfg(cfg_file, c, NULL, flags, NULL); cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); } @@ -128,6 +128,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, pi.file = path; pi.line = 1; pi.item = item; + pi.c = c; pr = cmd_parse_from_file(f, &pi); fclose(f); @@ -147,7 +148,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, if (item != NULL) cmdq_insert_after(item, new_item0); else - cmdq_append(c, new_item0); + cmdq_append(NULL, new_item0); cmd_list_free(pr->cmdlist); if (new_item != NULL) diff --git a/format.c b/format.c index 75521fd0..50b537fa 100644 --- a/format.c +++ b/format.c @@ -2006,10 +2006,10 @@ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp) { - if (c != NULL) + if (c != NULL && c->name != NULL) log_debug("%s: c=%s", __func__, c->name); else - log_debug("%s: s=none", __func__); + log_debug("%s: c=none", __func__); if (s != NULL) log_debug("%s: s=$%u", __func__, s->id); else diff --git a/server-client.c b/server-client.c index dc57e523..14c20617 100644 --- a/server-client.c +++ b/server-client.c @@ -1934,26 +1934,29 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) close(c->fd); c->fd = -1; - - return; - } - - if (c->fd == -1) - return; - if (tty_init(&c->tty, c, c->fd, c->term) != 0) { - close(c->fd); - c->fd = -1; - return; + } else if (c->fd != -1) { + if (tty_init(&c->tty, c, c->fd, c->term) != 0) { + close(c->fd); + c->fd = -1; + } else { + if (c->flags & CLIENT_UTF8) + c->tty.flags |= TTY_UTF8; + if (c->flags & CLIENT_256COLOURS) + c->tty.term_flags |= TERM_256COLOURS; + tty_resize(&c->tty); + c->flags |= CLIENT_TERMINAL; + } } - if (c->flags & CLIENT_UTF8) - c->tty.flags |= TTY_UTF8; - if (c->flags & CLIENT_256COLOURS) - c->tty.term_flags |= TERM_256COLOURS; - tty_resize(&c->tty); - - if (!(c->flags & CLIENT_CONTROL)) - c->flags |= CLIENT_TERMINAL; + /* + * If this is the first client that has finished identifying, load + * configuration files. + */ + if ((~c->flags & CLIENT_EXIT) && + !cfg_finished && + c == TAILQ_FIRST(&clients) && + TAILQ_NEXT(c, entry) == NULL) + start_cfg(); } /* Handle shell message. */ diff --git a/server.c b/server.c index fc3730ad..e75b02da 100644 --- a/server.c +++ b/server.c @@ -208,8 +208,7 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd, cmdq_append(c, cmdq_get_error(cause)); free(cause); c->flags |= CLIENT_EXIT; - } else - start_cfg(); + } server_add_accept(0); proc_loop(server_proc, server_loop); -- cgit From cd1fc42df6d1bacac4f617e031c279ba31bc0632 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Jun 2019 07:10:56 +0000 Subject: Add a -A flag to show-options to show parent options as well. --- arguments.c | 4 ++- cmd-show-options.c | 78 +++++++++++++++++++++++++++++++++++++----------------- tmux.1 | 5 +++- 3 files changed, 60 insertions(+), 27 deletions(-) diff --git a/arguments.c b/arguments.c index 8e049aab..751d0607 100644 --- a/arguments.c +++ b/arguments.c @@ -213,7 +213,9 @@ args_escape(const char *s) if (*s == '\0') return (xstrdup(s)); - if ((strchr(quoted, s[0]) != NULL || s[0] == '~') && s[1] == '\0') { + if (s[0] != ' ' && + (strchr(quoted, s[0]) != NULL || s[0] == '~') && + s[1] == '\0') { xasprintf(&escaped, "\\%c", s[0]); return (escaped); } diff --git a/cmd-show-options.c b/cmd-show-options.c index 6e3eca0d..3c5b6d47 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -31,16 +31,16 @@ static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *); static void cmd_show_options_print(struct cmd *, struct cmdq_item *, - struct options_entry *, int); + struct options_entry *, int, int); static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *, - struct options *); + enum options_table_scope, struct options *); const struct cmd_entry cmd_show_options_entry = { .name = "show-options", .alias = "show", - .args = { "gHqst:vw", 0, 1 }, - .usage = "[-gHqsvw] [-t target-session|target-window] [option]", + .args = { "AgHqst:vw", 0, 1 }, + .usage = "[-AgHqsvw] [-t target-session|target-window] [option]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, @@ -86,7 +86,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) enum options_table_scope scope; char *argument, *name = NULL, *cause; const char *target; - int window, idx, ambiguous; + int window, idx, ambiguous, parent; struct options_entry *o; window = (self->entry == &cmd_show_window_options_entry); @@ -99,7 +99,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } - return (cmd_show_options_all(self, item, oo)); + return (cmd_show_options_all(self, item, scope, oo)); } argument = format_single(item, args->argv[0], c, s, wl, NULL); @@ -164,8 +164,13 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) goto fail; } o = options_get_only(oo, name); + if (args_has(args, 'A') && o == NULL) { + o = options_get(oo, name); + parent = 1; + } else + parent = 0; if (o != NULL) - cmd_show_options_print(self, item, o, idx); + cmd_show_options_print(self, item, o, idx, parent); free(name); free(argument); @@ -179,7 +184,7 @@ fail: static void cmd_show_options_print(struct cmd *self, struct cmdq_item *item, - struct options_entry *o, int idx) + struct options_entry *o, int idx, int parent) { struct options_array_item *a; const char *name = options_name(o); @@ -198,7 +203,8 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, } while (a != NULL) { idx = options_array_item_index(a); - cmd_show_options_print(self, item, o, idx); + cmd_show_options_print(self, item, o, idx, + parent); a = options_array_next(a); } return; @@ -210,10 +216,17 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, cmdq_print(item, "%s", value); else if (options_isstring(o)) { escaped = args_escape(value); - cmdq_print(item, "%s %s", name, escaped); + if (parent) + cmdq_print(item, "%s* %s", name, escaped); + else + cmdq_print(item, "%s %s", name, escaped); free(escaped); - } else - cmdq_print(item, "%s %s", name, value); + } else { + if (parent) + cmdq_print(item, "%s* %s", name, value); + else + cmdq_print(item, "%s %s", name, value); + } free(value); free(tmp); @@ -221,39 +234,54 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, static enum cmd_retval cmd_show_options_all(struct cmd *self, struct cmdq_item *item, - struct options *oo) + enum options_table_scope scope, struct options *oo) { + const struct options_table_entry *oe; struct options_entry *o; struct options_array_item *a; u_int idx; - const struct options_table_entry *oe; + int parent; + + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope != scope) + continue; - o = options_first(oo); - while (o != NULL) { - oe = options_table_entry(o); 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)))) { - o = options_next(o); + (~oe->flags & OPTIONS_TABLE_IS_HOOK)))) continue; - } + + o = options_get_only(oo, oe->name); + if (o == NULL) { + if (!args_has(self->args, 'A')) + continue; + o = options_get(oo, oe->name); + if (o == NULL) + continue; + parent = 1; + } else + parent = 0; + if (!options_isarray(o)) - cmd_show_options_print(self, item, o, -1); + cmd_show_options_print(self, item, o, -1, parent); else if ((a = options_array_first(o)) == NULL) { - if (!args_has(self->args, 'v')) - cmdq_print(item, "%s", options_name(o)); + if (!args_has(self->args, 'v')) { + if (parent) + cmdq_print(item, "%s*", options_name(o)); + else + cmdq_print(item, "%s", options_name(o)); + } } else { while (a != NULL) { idx = options_array_item_index(a); - cmd_show_options_print(self, item, o, idx); + cmd_show_options_print(self, item, o, idx, parent); a = options_array_next(a); } } - o = options_next(o); } return (CMD_RETURN_NORMAL); } diff --git a/tmux.1 b/tmux.1 index 041097b6..4b3568f3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3680,7 +3680,7 @@ function key sequences; these have a number included to indicate modifiers such as Shift, Alt or Ctrl. .El .It Xo Ic show-options -.Op Fl gHqsvw +.Op Fl AgHqsvw .Op Fl t Ar target-session | Ar target-window .Op Ar option .Xc @@ -3714,6 +3714,9 @@ is set, no error will be returned if is unset. .Fl H includes hooks (omitted by default). +.Fl A +includes options inherited from a parent set of options, such options are +marked with an asterisk. .It Xo Ic show-window-options .Op Fl gv .Op Fl t Ar target-window -- cgit From c1ede507d954b98a73c40665e7aee6fe5f0c5bce Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Jun 2019 07:41:29 +0000 Subject: Add a helper function to work out option table from name. --- cmd-set-option.c | 55 ++++++------------------------------------------------ cmd-show-options.c | 46 ++------------------------------------------- options.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++- tmux.h | 3 +++ 4 files changed, 62 insertions(+), 94 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 7be561f2..10b70304 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -92,16 +92,19 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) struct options *oo; struct options_entry *parent, *o; char *name, *argument, *value = NULL, *cause; - const char *target; int window, idx, already, error, ambiguous; struct style *sy; + window = (self->entry == &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')) { notify_hook(item, argument); + free(argument); return (CMD_RETURN_NORMAL); } @@ -123,25 +126,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) else value = xstrdup(args->argv[1]); - /* - * Figure out the scope: for user options it comes from the arguments, - * otherwise from the option name. - */ - if (*name == '@') { - window = (self->entry == &cmd_set_window_option_entry); - scope = options_scope_from_flags(args, window, fs, &oo, &cause); - } else { - if (options_get_only(global_options, name) != NULL) - scope = OPTIONS_TABLE_SERVER; - else if (options_get_only(global_s_options, name) != NULL) - scope = OPTIONS_TABLE_SESSION; - else if (options_get_only(global_w_options, name) != NULL) - scope = OPTIONS_TABLE_WINDOW; - else { - scope = OPTIONS_TABLE_NONE; - xasprintf(&cause, "unknown option: %s", argument); - } - } + /* Get the scope and table for the option .*/ + scope = options_scope_from_name(args, window, name, fs, &oo, &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) goto out; @@ -149,35 +135,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) free(cause); goto fail; } - - /* Which table should this option go into? */ - if (scope == OPTIONS_TABLE_SERVER) - oo = global_options; - else if (scope == OPTIONS_TABLE_SESSION) { - if (args_has(self->args, 'g')) - oo = global_s_options; - else if (s == NULL) { - target = args_get(args, 't'); - if (target != NULL) - cmdq_error(item, "no such session: %s", target); - else - cmdq_error(item, "no current session"); - goto fail; - } else - oo = s->options; - } else if (scope == OPTIONS_TABLE_WINDOW) { - if (args_has(self->args, 'g')) - oo = global_w_options; - else if (wl == NULL) { - target = args_get(args, 't'); - if (target != NULL) - cmdq_error(item, "no such window: %s", target); - else - cmdq_error(item, "no current window"); - goto fail; - } else - oo = wl->window->options; - } o = options_get_only(oo, name); parent = options_get(oo, name); diff --git a/cmd-show-options.c b/cmd-show-options.c index 3c5b6d47..109c6c52 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -85,11 +85,11 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) struct options *oo; enum options_table_scope scope; char *argument, *name = NULL, *cause; - const char *target; int window, idx, ambiguous, parent; struct options_entry *o; window = (self->entry == &cmd_show_window_options_entry); + if (args->argc == 0) { scope = options_scope_from_flags(args, window, fs, &oo, &cause); if (scope == OPTIONS_TABLE_NONE) { @@ -113,49 +113,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "invalid option: %s", argument); goto fail; } - if (*name == '@') - scope = options_scope_from_flags(args, window, fs, &oo, &cause); - else { - if (options_get_only(global_options, name) != NULL) - scope = OPTIONS_TABLE_SERVER; - else if (options_get_only(global_s_options, name) != NULL) - scope = OPTIONS_TABLE_SESSION; - else if (options_get_only(global_w_options, name) != NULL) - scope = OPTIONS_TABLE_WINDOW; - else { - scope = OPTIONS_TABLE_NONE; - xasprintf(&cause, "unknown option: %s", argument); - } - if (scope == OPTIONS_TABLE_SERVER) - oo = global_options; - else if (scope == OPTIONS_TABLE_SESSION) { - if (args_has(self->args, 'g')) - oo = global_s_options; - else if (s == NULL) { - target = args_get(args, 't'); - if (target != NULL) { - cmdq_error(item, "no such session: %s", - target); - } else - cmdq_error(item, "no current session"); - goto fail; - } else - oo = s->options; - } else if (scope == OPTIONS_TABLE_WINDOW) { - if (args_has(self->args, 'g')) - oo = global_w_options; - else if (wl == NULL) { - target = args_get(args, 't'); - if (target != NULL) { - cmdq_error(item, "no such window: %s", - target); - } else - cmdq_error(item, "no current window"); - goto fail; - } else - oo = wl->window->options; - } - } + scope = options_scope_from_name(args, window, name, fs, &oo, &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) goto fail; diff --git a/options.c b/options.c index 619c27ed..3bce6041 100644 --- a/options.c +++ b/options.c @@ -725,13 +725,63 @@ options_set_style(struct options *oo, const char *name, int append, return (o); } +enum options_table_scope +options_scope_from_name(struct args *args, int window, + const char *name, struct cmd_find_state *fs, struct options **oo, + char **cause) +{ + struct session *s = fs->s; + struct winlink *wl = fs->wl; + const char *target = args_get(args, 't'); + enum options_table_scope scope; + + if (*name == '@') + return (options_scope_from_flags(args, window, fs, oo, cause)); + + if (options_get_only(global_options, name) != NULL) + scope = OPTIONS_TABLE_SERVER; + else if (options_get_only(global_s_options, name) != NULL) + scope = OPTIONS_TABLE_SESSION; + else if (options_get_only(global_w_options, name) != NULL) + scope = OPTIONS_TABLE_WINDOW; + else { + xasprintf(cause, "unknown option: %s", name); + return (OPTIONS_TABLE_NONE); + } + + if (scope == OPTIONS_TABLE_SERVER) + *oo = global_options; + else if (scope == OPTIONS_TABLE_SESSION) { + if (args_has(args, 'g')) + *oo = global_s_options; + else if (s == NULL) { + if (target != NULL) + xasprintf(cause, "no such session: %s", target); + else + xasprintf(cause, "no current session"); + } else + *oo = s->options; + } else if (scope == OPTIONS_TABLE_WINDOW) { + if (args_has(args, 'g')) + *oo = global_w_options; + else if (wl == NULL) { + if (target != NULL) + xasprintf(cause, "no such window: %s", target); + else + xasprintf(cause, "no current window"); + } else + *oo = wl->window->options; + } + return (scope); +} + enum options_table_scope options_scope_from_flags(struct args *args, int window, struct cmd_find_state *fs, struct options **oo, char **cause) { struct session *s = fs->s; struct winlink *wl = fs->wl; - const char *target= args_get(args, 't'); + const char *target = args_get(args, 't'); if (args_has(args, 's')) { *oo = global_options; diff --git a/tmux.h b/tmux.h index cc5e55c0..0557e98d 100644 --- a/tmux.h +++ b/tmux.h @@ -1819,6 +1819,9 @@ 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 *); +enum options_table_scope options_scope_from_name(struct args *, int, + const char *, struct cmd_find_state *, struct options **, + char **); enum options_table_scope options_scope_from_flags(struct args *, int, struct cmd_find_state *, struct options **, char **); -- cgit From 5f92f92908b81b4ec66682adb84b9ffc8d83c2f7 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Jun 2019 11:59:59 +0000 Subject: Add a per-pane option set. Pane options inherit from window options (so there should be no change to existing behaviour) and are set and shown with set-option -p and show-options -p. Change remain-on-exit and window-style/window-active-style to be pane options (some others will be changed later). This makes select-pane -P and -g unnecessary so no longer document them (they still work) and no longer document set-window-option and show-window-options in favour of set-option -w and show-options -w. --- cmd-break-pane.c | 2 + cmd-join-pane.c | 2 + cmd-select-pane.c | 18 ++-- cmd-set-option.c | 13 +-- cmd-show-options.c | 26 +++--- cmd-swap-pane.c | 4 + format.c | 2 + input.c | 16 ++-- options-table.c | 6 +- options.c | 100 ++++++++++++++------- server-fn.c | 2 +- tmux.1 | 255 +++++++++++++++++++++++------------------------------ tmux.c | 6 +- tmux.h | 27 +++--- tty.c | 46 +++++----- window.c | 55 +++++++----- 16 files changed, 301 insertions(+), 279 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 3b929dee..b4c5b7cd 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -77,6 +77,8 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) layout_close_pane(wp); w = wp->window = window_create(w->sx, w->sy); + options_set_parent(wp->options, w->options); + wp->flags |= PANE_STYLECHANGED; TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index ab245302..4a0c7339 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -140,6 +140,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) TAILQ_REMOVE(&src_w->panes, src_wp, entry); 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); layout_assign_pane(lc, src_wp); diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 92ecb734..52c58dbc 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_select_pane_entry = { .name = "select-pane", .alias = "selectp", - .args = { "DdegLlMmP:RT:t:U", 0, 0 }, - .usage = "[-DdegLlMmRU] [-P style] [-T title] " CMD_TARGET_PANE_USAGE, + .args = { "DdegLlMmP:RT:t:U", 0, 0 }, /* -P and -g deprecated */ + .usage = "[-DdeLlMmRU] [-T title] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -90,9 +90,10 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) struct window *w = wl->window; struct session *s = item->target.s; struct window_pane *wp = item->target.wp, *lastwp, *markedwp; - struct style *sy = &wp->style; char *pane_title; const char *style; + struct style *sy; + struct options_entry *o; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { lastwp = w->last; @@ -144,15 +145,18 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(self->args, 'P') || args_has(self->args, 'g')) { if ((style = args_get(args, 'P')) != NULL) { - style_set(sy, &grid_default_cell); - if (style_parse(sy, &grid_default_cell, style) == -1) { + o = options_set_style(wp->options, "window-style", 0, + style); + if (o == NULL) { cmdq_error(item, "bad style: %s", style); return (CMD_RETURN_ERROR); } - wp->flags |= PANE_REDRAW; + wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); } - if (args_has(self->args, 'g')) + if (args_has(self->args, 'g')) { + sy = options_get_style(wp->options, "window-style"); cmdq_print(item, "%s", style_tostring(sy)); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-option.c b/cmd-set-option.c index 10b70304..23b45230 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -43,10 +43,10 @@ const struct cmd_entry cmd_set_option_entry = { .name = "set-option", .alias = "set", - .args = { "aFgoqst:uw", 1, 2 }, - .usage = "[-aFgosquw] [-t target-window] option [value]", + .args = { "aFgopqst:uw", 1, 2 }, + .usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]", - .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, + .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_set_option_exec @@ -88,11 +88,12 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) struct session *s = fs->s; struct winlink *wl = fs->wl; struct window *w; - enum options_table_scope scope; + struct window_pane *wp; struct options *oo; struct options_entry *parent, *o; char *name, *argument, *value = NULL, *cause; int window, idx, already, error, ambiguous; + int scope; struct style *sy; window = (self->entry == &cmd_set_window_option_entry); @@ -249,8 +250,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) alerts_reset_all(); if (strcmp(name, "window-style") == 0 || strcmp(name, "window-active-style") == 0) { - RB_FOREACH(w, windows, &windows) - w->flags |= WINDOW_STYLECHANGED; + 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) diff --git a/cmd-show-options.c b/cmd-show-options.c index 109c6c52..5b22b8bd 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -33,16 +33,16 @@ static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *); static void cmd_show_options_print(struct cmd *, struct cmdq_item *, struct options_entry *, int, int); static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *, - enum options_table_scope, struct options *); + int, struct options *); const struct cmd_entry cmd_show_options_entry = { .name = "show-options", .alias = "show", - .args = { "AgHqst:vw", 0, 1 }, - .usage = "[-AgHqsvw] [-t target-session|target-window] [option]", + .args = { "AgHpqst:vw", 0, 1 }, + .usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]", - .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, + .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_show_options_exec @@ -83,9 +83,8 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct options *oo; - enum options_table_scope scope; char *argument, *name = NULL, *cause; - int window, idx, ambiguous, parent; + int window, idx, ambiguous, parent, scope; struct options_entry *o; window = (self->entry == &cmd_show_window_options_entry); @@ -191,17 +190,18 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, } static enum cmd_retval -cmd_show_options_all(struct cmd *self, struct cmdq_item *item, - enum options_table_scope scope, struct options *oo) +cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, + struct options *oo) { const struct options_table_entry *oe; struct options_entry *o; struct options_array_item *a; + const char *name; u_int idx; int parent; for (oe = options_table; oe->name != NULL; oe++) { - if (oe->scope != scope) + if (~oe->scope & scope) continue; if ((self->entry != &cmd_show_hooks_entry && @@ -228,15 +228,17 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, cmd_show_options_print(self, item, o, -1, parent); else if ((a = options_array_first(o)) == NULL) { if (!args_has(self->args, 'v')) { + name = options_name(o); if (parent) - cmdq_print(item, "%s*", options_name(o)); + cmdq_print(item, "%s*", name); else - cmdq_print(item, "%s", options_name(o)); + cmdq_print(item, "%s", name); } } else { while (a != NULL) { idx = options_array_item_index(a); - cmd_show_options_print(self, item, o, idx, parent); + cmd_show_options_print(self, item, o, idx, + parent); a = options_array_next(a); } } diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 2ad05561..994ad0e8 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -90,7 +90,11 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) src_wp->layout_cell = dst_lc; src_wp->window = dst_w; + options_set_parent(src_wp->options, dst_w->options); + src_wp->flags |= PANE_STYLECHANGED; dst_wp->window = src_w; + options_set_parent(dst_wp->options, src_w->options); + dst_wp->flags |= PANE_STYLECHANGED; sx = src_wp->sx; sy = src_wp->sy; xoff = src_wp->xoff; yoff = src_wp->yoff; diff --git a/format.c b/format.c index 50b537fa..6b07e67e 100644 --- a/format.c +++ b/format.c @@ -1052,6 +1052,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers) 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) diff --git a/input.c b/input.c index e54b5ac0..3e85e14f 100644 --- a/input.c +++ b/input.c @@ -2347,12 +2347,14 @@ 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 (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) goto bad; - - wp->style.gc.fg = colour_join_rgb(r, g, b); - wp->flags |= PANE_REDRAW; + 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->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; @@ -2366,12 +2368,14 @@ 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 (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) goto bad; - - wp->style.gc.bg = colour_join_rgb(r, g, b); - wp->flags |= PANE_REDRAW; + 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->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; diff --git a/options-table.c b/options-table.c index 32032f8b..b287d660 100644 --- a/options-table.c +++ b/options-table.c @@ -700,7 +700,7 @@ const struct options_table_entry options_table[] = { { .name = "remain-on-exit", .type = OPTIONS_TABLE_FLAG, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_num = 0 }, @@ -712,7 +712,7 @@ const struct options_table_entry options_table[] = { { .name = "window-active-style", .type = OPTIONS_TABLE_STYLE, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "default" }, @@ -725,7 +725,7 @@ const struct options_table_entry options_table[] = { { .name = "window-style", .type = OPTIONS_TABLE_STYLE, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "default" }, diff --git a/options.c b/options.c index 3bce6041..bbb6af98 100644 --- a/options.c +++ b/options.c @@ -100,7 +100,7 @@ options_parent_table_entry(struct options *oo, const char *s) if (oo->parent == NULL) fatalx("no parent options for %s", s); - o = options_get_only(oo->parent, s); + o = options_get(oo->parent, s); if (o == NULL) fatalx("%s not in parent options", s); return (o->tableentry); @@ -178,6 +178,12 @@ options_free(struct options *oo) free(oo); } +void +options_set_parent(struct options *oo, struct options *parent) +{ + oo->parent = parent; +} + struct options_entry * options_first(struct options *oo) { @@ -545,7 +551,7 @@ options_parse_get(struct options *oo, const char *s, int *idx, int only) } char * -options_match(const char *s, int *idx, int* ambiguous) +options_match(const char *s, int *idx, int *ambiguous) { const struct options_table_entry *oe, *found; char *name; @@ -725,70 +731,96 @@ options_set_style(struct options *oo, const char *name, int append, return (o); } -enum options_table_scope +int options_scope_from_name(struct args *args, int window, const char *name, struct cmd_find_state *fs, struct options **oo, char **cause) { - struct session *s = fs->s; - struct winlink *wl = fs->wl; - const char *target = args_get(args, 't'); - enum options_table_scope scope; + struct session *s = fs->s; + struct winlink *wl = fs->wl; + struct window_pane *wp = fs->wp; + const char *target = args_get(args, 't'); + const struct options_table_entry *oe; + int scope; if (*name == '@') return (options_scope_from_flags(args, window, fs, oo, cause)); - if (options_get_only(global_options, name) != NULL) - scope = OPTIONS_TABLE_SERVER; - else if (options_get_only(global_s_options, name) != NULL) - scope = OPTIONS_TABLE_SESSION; - else if (options_get_only(global_w_options, name) != NULL) - scope = OPTIONS_TABLE_WINDOW; - else { + for (oe = options_table; oe->name != NULL; oe++) { + if (strcmp(oe->name, name) == 0) + break; + } + if (oe->name == NULL) { xasprintf(cause, "unknown option: %s", name); return (OPTIONS_TABLE_NONE); } + scope = oe->scope; - if (scope == OPTIONS_TABLE_SERVER) + switch (scope) { + case OPTIONS_TABLE_SERVER: *oo = global_options; - else if (scope == OPTIONS_TABLE_SESSION) { + break; + case OPTIONS_TABLE_SESSION: if (args_has(args, 'g')) *oo = global_s_options; - else if (s == NULL) { - if (target != NULL) - xasprintf(cause, "no such session: %s", target); - else - xasprintf(cause, "no current session"); - } else + else if (s == NULL && target != NULL) + xasprintf(cause, "no such session: %s", target); + else if (s == NULL) + xasprintf(cause, "no current session"); + else *oo = s->options; - } else if (scope == OPTIONS_TABLE_WINDOW) { + break; + case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE: + if (args_has(args, 'p')) { + if (wp == NULL && target != NULL) + xasprintf(cause, "no such pane: %s", target); + else if (wp == NULL) + xasprintf(cause, "no current pane"); + else + *oo = wp->options; + break; + } + scope = OPTIONS_TABLE_WINDOW; + /* FALLTHROUGH */ + case OPTIONS_TABLE_WINDOW: if (args_has(args, 'g')) *oo = global_w_options; - else if (wl == NULL) { - if (target != NULL) - xasprintf(cause, "no such window: %s", target); - else - xasprintf(cause, "no current window"); - } else + else if (wl == NULL && target != NULL) + xasprintf(cause, "no such window: %s", target); + else if (wl == NULL) + xasprintf(cause, "no current window"); + else *oo = wl->window->options; + break; } return (scope); } -enum options_table_scope +int options_scope_from_flags(struct args *args, int window, struct cmd_find_state *fs, struct options **oo, char **cause) { - struct session *s = fs->s; - struct winlink *wl = fs->wl; - const char *target = args_get(args, 't'); + struct session *s = fs->s; + struct winlink *wl = fs->wl; + struct window_pane *wp = fs->wp; + const char *target = args_get(args, 't'); if (args_has(args, 's')) { *oo = global_options; return (OPTIONS_TABLE_SERVER); } - if (window || args_has(args, 'w')) { + if (args_has(args, 'p')) { + if (wp == NULL) { + if (target != NULL) + xasprintf(cause, "no such pane: %s", target); + else + xasprintf(cause, "no current pane"); + return (OPTIONS_TABLE_NONE); + } + *oo = wp->options; + return (OPTIONS_TABLE_PANE); + } else if (window || args_has(args, 'w')) { if (args_has(args, 'g')) { *oo = global_w_options; return (OPTIONS_TABLE_WINDOW); diff --git a/server-fn.c b/server-fn.c index 372bf21d..25d3aeb1 100644 --- a/server-fn.c +++ b/server-fn.c @@ -307,7 +307,7 @@ server_destroy_pane(struct window_pane *wp, int notify) wp->fd = -1; } - if (options_get_number(w->options, "remain-on-exit")) { + if (options_get_number(wp->options, "remain-on-exit")) { if (~wp->flags & PANE_STATUSREADY) return; diff --git a/tmux.1 b/tmux.1 index 4b3568f3..ec7fdc31 100644 --- a/tmux.1 +++ b/tmux.1 @@ -876,7 +876,7 @@ refresh-client -t/dev/ttyp2 rename-session -tfirst newname -set-window-option -t:0 monitor-activity on +set-option -wt:0 monitor-activity on new-window ; split-window -d @@ -2323,8 +2323,7 @@ applies the last set layout if possible (undoes the most recent layout change). .Fl E spreads the current pane and any panes next to it out evenly. .It Xo Ic select-pane -.Op Fl DdegLlMmRU -.Op Fl P Ar style +.Op Fl DdeLlMmRU .Op Fl T Ar title .Op Fl t Ar target-pane .Xc @@ -2351,6 +2350,8 @@ command. enables or .Fl d disables input to the pane. +.Fl T +sets the pane title. .Pp .Fl m and @@ -2365,25 +2366,6 @@ to .Ic swap-pane and .Ic swap-window . -.Pp -Each pane has a style: by default the -.Ic window-style -and -.Ic window-active-style -options are used, -.Ic select-pane -.Fl P -sets the style for a single pane. -For example, to set the pane 1 background to red: -.Bd -literal -offset indent -select-pane -t:.1 -P 'bg=red' -.Ed -.Pp -.Fl g -shows the current pane style. -.Pp -.Fl T -sets the pane title. .It Xo Ic select-window .Op Fl lnpT .Op Fl t Ar target-window @@ -2702,16 +2684,17 @@ is present, all key bindings are removed. The appearance and behaviour of .Nm may be modified by changing the value of various options. -There are three types of option: +There are four types of option: .Em server options , .Em session options +.Em window options and -.Em window options . +.Em pane options . .Pp The .Nm server has a set of global options which do not apply to any particular -window or session. +window or session or pane. These are altered with the .Ic set-option .Fl s @@ -2733,16 +2716,29 @@ The available server and session options are listed under the .Ic set-option command. .Pp -Similarly, a set of window options is attached to each window, and there is -a set of global window options from which any unset options are inherited. -Window options are altered with the -.Ic set-window-option -command and can be listed with the -.Ic show-window-options -command. -All window options are documented with the -.Ic set-window-option -command. +Similarly, a set of window options is attached to each window and a set of pane +options to each pane. +Pane options inherit from window options. +This means any pane option may be set as a window option to apply the option to +all panes in the window without the option set, for example these commands will +set the background colour to red for all panes except pane 0: +.Bd -literal -offset indent +set -w window-style bg=red +set -pt:.0 window-style bg=blue +.Ed +.Pp +There is also a set of global window options from which any unset window or +pane options are inherited. +Window and pane options are altered with +.Ic set-option +.Fl w +and +.Fl p +commands and displayed with +.Ic show-option +.Fl w +and +.Fl p . .Pp .Nm also supports user options which are prefixed with a @@ -2760,26 +2756,27 @@ abc123 Commands which set options are as follows: .Bl -tag -width Ds .It Xo Ic set-option -.Op Fl aFgoqsuw -.Op Fl t Ar target-session | Ar target-window +.Op Fl aFgopqsuw +.Op Fl t Ar target-pane .Ar option Ar value .Xc .D1 (alias: Ic set ) -Set a window option with -.Fl w -(equivalent to the -.Ic set-window-option -command), +Set a pane option with +.Fl p , +a window option with +.Fl w , a server option with .Fl s , otherwise a session option. If the option is not a user option, .Fl w -and +or .Fl s -are unnecessary - +may be unnecessary - .Nm -will infer the type from the option name. +will infer the type from the option name, assuming +.Fl w +for pane options. If .Fl g is given, the global session or window option is set. @@ -2824,13 +2821,49 @@ blue foreground. Without .Fl a , the result would be the default background and a blue foreground. -.Pp -Available window options are listed under -.Ic set-window-option . -.Pp +.It Xo Ic show-options +.Op Fl AgHpqsvw +.Op Fl t Ar target-pane +.Op Ar option +.Xc +.D1 (alias: Ic show ) +Show the pane options (or a single option if +.Ar option +is provided) with +.Fl p , +the window options with +.Fl w , +the server options with +.Fl s , +otherwise the session options. +If the option is not a user option, +.Fl w +or +.Fl s +may be unnecessary - +.Nm +will infer the type from the option name, assuming +.Fl w +for pane options. +Global session or window options are listed if +.Fl g +is used. +.Fl v +shows only the option value, not the name. +If +.Fl q +is set, no error will be returned if +.Ar option +is unset. +.Fl H +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: .Bl -tag -width Ds @@ -3357,26 +3390,8 @@ copy mode. The default is .Ql \ -_@ . .El -.It Xo Ic set-window-option -.Op Fl aFgoqu -.Op Fl t Ar target-window -.Ar option Ar value -.Xc -.D1 (alias: Ic setw ) -Set a window option. -The -.Fl a , -.Fl F , -.Fl g , -.Fl o , -.Fl q -and -.Fl u -flags work similarly to the -.Ic set-option -command. .Pp -Supported window options are: +Available window options are: .Pp .Bl -tag -width Ds -compact .It Xo Ic aggressive-resize @@ -3436,7 +3451,7 @@ or later with or with a terminal escape sequence. It may be switched off globally with: .Bd -literal -offset indent -set-window-option -g automatic-rename off +set-option -wg automatic-rename off .Ed .Pp .It Ic automatic-rename-format Ar format @@ -3553,29 +3568,12 @@ see the section. Attributes are ignored. .Pp -.It Xo Ic remain-on-exit -.Op Ic on | off -.Xc -A window with this flag set is not destroyed when the program running in it -exits. -The window may be reactivated with the -.Ic respawn-window -command. -.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-active-style Ar style -Set the style for the window's active pane. -For how to specify -.Ar style , -see the -.Sx STYLES -section. -.Pp .It Ic window-status-activity-style Ar style Set status line style for windows with an activity alert. For how to specify @@ -3655,14 +3653,6 @@ command and the .Ic aggressive-resize option. .Pp -.It Ic window-style Ar style -Set the default window style. -For how to specify -.Ar style , -see the -.Sx STYLES -section. -.Pp .It Xo Ic wrap-search .Op Ic on | off .Xc @@ -3679,57 +3669,34 @@ will generate function key sequences; these have a number included to indicate modifiers such as Shift, Alt or Ctrl. .El -.It Xo Ic show-options -.Op Fl AgHqsvw -.Op Fl t Ar target-session | Ar target-window -.Op Ar option -.Xc -.D1 (alias: Ic show ) -Show the window options (or a single window option if given) with -.Fl w -(equivalent to -.Ic show-window-options ) , -the server options with -.Fl s , -otherwise the session options for -.Ar target session . -If -.Ar option -is given and is not a user option, -.Fl w -and -.Fl s -are unnecessary - -.Nm -will infer the type from the option name. -Global session or window options are listed if -.Fl g -is used. -.Fl v -shows only the option value, not the name. -If -.Fl q -is set, no error will be returned if -.Ar option -is unset. -.Fl H -includes hooks (omitted by default). -.Fl A -includes options inherited from a parent set of options, such options are -marked with an asterisk. -.It Xo Ic show-window-options -.Op Fl gv -.Op Fl t Ar target-window -.Op Ar option +.Pp +Available pane options are: +.Pp +.Bl -tag -width Ds -compact +.It Xo Ic remain-on-exit +.Op Ic on | off .Xc -.D1 (alias: Ic showw ) -List the window options or a single option for -.Ar target-window , -or the global window options if -.Fl g -is used. -.Fl v -shows only the option value, not the name. +A pane with this flag set is not destroyed when the program running in it +exits. +The pane may be reactivated with the +.Ic respawn-pane +command. +.Pp +.It Ic window-active-style Ar style +Set the pane style when it is the active pane. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-style Ar style +Set the pane style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. .El .Sh HOOKS .Nm diff --git a/tmux.c b/tmux.c index ac8a131a..c3fe3ee6 100644 --- a/tmux.c +++ b/tmux.c @@ -321,11 +321,11 @@ main(int argc, char **argv) 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) + if (oe->scope & OPTIONS_TABLE_SERVER) options_default(global_options, oe); - if (oe->scope == OPTIONS_TABLE_SESSION) + if (oe->scope & OPTIONS_TABLE_SESSION) options_default(global_s_options, oe); - if (oe->scope == OPTIONS_TABLE_WINDOW) + if (oe->scope & OPTIONS_TABLE_WINDOW) options_default(global_w_options, oe); } diff --git a/tmux.h b/tmux.h index 0557e98d..8adfcba8 100644 --- a/tmux.h +++ b/tmux.h @@ -810,6 +810,7 @@ struct window_pane { u_int active_point; struct window *window; + struct options *options; struct layout_cell *layout_cell; struct layout_cell *saved_layout_cell; @@ -836,6 +837,7 @@ struct window_pane { #define PANE_STATUSREADY 0x200 #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 +#define PANE_STYLECHANGED 0x1000 int argc; char **argv; @@ -854,7 +856,8 @@ struct window_pane { struct input_ctx *ictx; - struct style style; + struct style cached_style; + struct style cached_active_style; int *palette; int pipe_fd; @@ -914,7 +917,6 @@ struct window { #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 #define WINDOW_ZOOMED 0x8 -#define WINDOW_STYLECHANGED 0x10 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) int alerts_queued; @@ -922,9 +924,6 @@ struct window { struct options *options; - struct style style; - struct style active_style; - u_int references; TAILQ_HEAD(, winlink) winlinks; @@ -1605,12 +1604,11 @@ enum options_table_type { OPTIONS_TABLE_COMMAND }; -enum options_table_scope { - OPTIONS_TABLE_NONE, - OPTIONS_TABLE_SERVER, - OPTIONS_TABLE_SESSION, - OPTIONS_TABLE_WINDOW -}; +#define OPTIONS_TABLE_NONE 0 +#define OPTIONS_TABLE_SERVER 0x1 +#define OPTIONS_TABLE_SESSION 0x2 +#define OPTIONS_TABLE_WINDOW 0x4 +#define OPTIONS_TABLE_PANE 0x8 #define OPTIONS_TABLE_IS_ARRAY 0x1 #define OPTIONS_TABLE_IS_HOOK 0x2 @@ -1618,7 +1616,7 @@ enum options_table_scope { struct options_table_entry { const char *name; enum options_table_type type; - enum options_table_scope scope; + int scope; int flags; u_int minimum; @@ -1780,6 +1778,7 @@ void notify_pane(const char *, struct window_pane *); /* options.c */ struct options *options_create(struct options *); void options_free(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 *); struct options_entry *options_empty(struct options *, @@ -1819,10 +1818,10 @@ 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 *); -enum options_table_scope options_scope_from_name(struct args *, int, +int options_scope_from_name(struct args *, int, const char *, struct cmd_find_state *, struct options **, char **); -enum options_table_scope options_scope_from_flags(struct args *, int, +int options_scope_from_flags(struct args *, int, struct cmd_find_state *, struct options **, char **); /* options-table.c */ diff --git a/tty.c b/tty.c index 05df4f0f..8d3b2858 100644 --- a/tty.c +++ b/tty.c @@ -2132,7 +2132,7 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, /* Ignore cell if it is the same as the last one. */ if (wp != NULL && (int)wp->id == tty->last_wp && - ~(wp->window->flags & WINDOW_STYLECHANGED) && + ~(wp->flags & PANE_STYLECHANGED) && gc->attr == tty->last_cell.attr && gc->fg == tty->last_cell.fg && gc->bg == tty->last_cell.bg) @@ -2514,30 +2514,28 @@ fallback_256: static void tty_default_colours(struct grid_cell *gc, struct window_pane *wp) { - struct window *w = wp->window; - struct options *oo = w->options; - struct style *active, *pane, *window; - int c; - - if (w->flags & WINDOW_STYLECHANGED) { - w->flags &= ~WINDOW_STYLECHANGED; - active = options_get_style(oo, "window-active-style"); - style_copy(&w->active_style, active); - window = options_get_style(oo, "window-style"); - style_copy(&w->style, window); + 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 = &w->active_style; - window = &w->style; + active_style = &wp->cached_active_style; + style = &wp->cached_style; } - pane = &wp->style; if (gc->fg == 8) { - if (pane->gc.fg != 8) - gc->fg = pane->gc.fg; - else if (wp == w->active && active->gc.fg != 8) - gc->fg = active->gc.fg; + if (wp == wp->window->active && active_style->gc.fg != 8) + gc->fg = active_style->gc.fg; else - gc->fg = window->gc.fg; + gc->fg = style->gc.fg; if (gc->fg != 8) { c = window_pane_get_palette(wp, gc->fg); @@ -2547,12 +2545,10 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) } if (gc->bg == 8) { - if (pane->gc.bg != 8) - gc->bg = pane->gc.bg; - else if (wp == w->active && active->gc.bg != 8) - gc->bg = active->gc.bg; + if (wp == wp->window->active && active_style->gc.bg != 8) + gc->bg = active_style->gc.bg; else - gc->bg = window->gc.bg; + gc->bg = style->gc.bg; if (gc->bg != 8) { c = window_pane_get_palette(wp, gc->bg); diff --git a/window.c b/window.c index e7941c70..25784454 100644 --- a/window.c +++ b/window.c @@ -313,7 +313,7 @@ window_create(u_int sx, u_int sy) w = xcalloc(1, sizeof *w); w->name = NULL; - w->flags = WINDOW_STYLECHANGED; + w->flags = 0; TAILQ_INIT(&w->panes); w->active = NULL; @@ -450,31 +450,37 @@ 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 *sy; + struct style *sy1, *sy2; + int c1, c2; if (wp == w->active) return; - /* - * If window-style and window-active-style are the same, we don't need - * to redraw panes when switching active panes. - */ - sy = options_get_style(w->options, "window-active-style"); - if (style_equal(sy, options_get_style(w->options, "window-style"))) - return; - - /* - * If the now active or inactive pane do not have a custom style or if - * the palette is different, they need to be redrawn. - */ - if (window_pane_get_palette(w->active, w->active->style.gc.fg) != -1 || - window_pane_get_palette(w->active, w->active->style.gc.bg) != -1 || - style_is_default(&w->active->style)) - w->active->flags |= PANE_REDRAW; - if (window_pane_get_palette(wp, wp->style.gc.fg) != -1 || - window_pane_get_palette(wp, wp->style.gc.bg) != -1 || - style_is_default(&wp->style)) - wp->flags |= PANE_REDRAW; + for (;;) { + /* + * 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)) + wp->flags |= PANE_REDRAW; + else { + c1 = window_pane_get_palette(wp, sy1->gc.fg); + c2 = window_pane_get_palette(wp, sy2->gc.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); + if (c1 != c2) + wp->flags |= PANE_REDRAW; + } + } + if (wp == w->active) + break; + wp = w->active; + } } struct window_pane * @@ -778,6 +784,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp = xcalloc(1, sizeof *wp); wp->window = w; + wp->options = options_create(w->options); + wp->flags = PANE_STYLECHANGED; wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); @@ -808,8 +816,6 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->saved_cx = UINT_MAX; wp->saved_cy = UINT_MAX; - style_set(&wp->style, &grid_default_cell); - screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; @@ -852,6 +858,7 @@ window_pane_destroy(struct window_pane *wp) RB_REMOVE(window_pane_tree, &all_window_panes, wp); + options_free(wp->options); free((void *)wp->cwd); free(wp->shell); cmd_free_argv(wp->argc, wp->argv); -- cgit From a0e2c1b4cae9269d04ee2e80e1d1cb8adc78cd8a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 20 Jun 2019 13:08:13 +0100 Subject: Add to CHANGES. --- CHANGES | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES b/CHANGES index 9129fa3b..95c8b619 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,17 @@ CHANGES FROM 3.0 to X.X +* Add pane options, set with set-option -p and displayed with show-options -p. + Pane options inherit from window options (so every pane option is also + a window option). The pane style is now configured by setting window-style + and window-active-style in the pane options; select-pane -P and -g now change + the option but are no longer documented. + +* Do not document set-window-option and show-window-options. set-option -w and + show-options -w should be used instead. + +* Add a -A flag to show-options to show parent options as well (they are marked + with a *). + * Resize panes lazily - do not resize unless they are in an attached, active window. -- cgit From c1573727f0ea1d8a2530edc1a7848d9aa1d6f590 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Jun 2019 13:39:17 +0000 Subject: Still need to walk the options tree for user options. --- cmd-show-options.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd-show-options.c b/cmd-show-options.c index 5b22b8bd..0b9eb096 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -200,6 +200,12 @@ 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); + } for (oe = options_table; oe->name != NULL; oe++) { if (~oe->scope & scope) continue; -- cgit From fc1df91e034627f674ed905be6a1159da883545e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Jun 2019 13:40:22 +0000 Subject: allow-rename and alternate-screen can be pane options. --- input.c | 2 +- options-table.c | 4 ++-- tmux.1 | 43 ++++++++++++++++++++----------------------- window.c | 4 ++-- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/input.c b/input.c index 3e85e14f..7df2a7b0 100644 --- a/input.c +++ b/input.c @@ -2259,7 +2259,7 @@ input_exit_rename(struct input_ctx *ictx) { if (ictx->flags & INPUT_DISCARD) return; - if (!options_get_number(ictx->wp->window->options, "allow-rename")) + if (!options_get_number(ictx->wp->options, "allow-rename")) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); diff --git a/options-table.c b/options-table.c index b287d660..228cf3f0 100644 --- a/options-table.c +++ b/options-table.c @@ -563,13 +563,13 @@ const struct options_table_entry options_table[] = { { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_num = 0 }, { .name = "alternate-screen", .type = OPTIONS_TABLE_FLAG, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_num = 1 }, diff --git a/tmux.1 b/tmux.1 index ec7fdc31..1315e29a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3410,29 +3410,6 @@ session; this option is good for full-screen programs which support .Dv SIGWINCH and poor for interactive programs such as shells. .Pp -.It Xo Ic allow-rename -.Op Ic on | off -.Xc -Allow programs to change the window name using a terminal escape -sequence (\eek...\ee\e\e). -The default is off. -.Pp -.It Xo Ic alternate-screen -.Op Ic on | off -.Xc -This option configures whether programs running inside -.Nm -may use the terminal alternate screen feature, which allows the -.Em smcup -and -.Em rmcup -.Xr terminfo 5 -capabilities. -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. -The default is on. -.Pp .It Xo Ic automatic-rename .Op Ic on | off .Xc @@ -3673,6 +3650,26 @@ as Shift, Alt or Ctrl. Available pane options are: .Pp .Bl -tag -width Ds -compact +.It Xo Ic allow-rename +.Op Ic on | off +.Xc +Allow programs in the pane to change the window name using a terminal escape +sequence (\eek...\ee\e\e). +.Pp +.It Xo Ic alternate-screen +.Op Ic on | off +.Xc +This option configures whether programs running inside the pane may use the +terminal alternate screen feature, which allows the +.Em smcup +and +.Em rmcup +.Xr terminfo 5 +capabilities. +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 Xo Ic remain-on-exit .Op Ic on | off .Xc diff --git a/window.c b/window.c index 25784454..77ca2ce2 100644 --- a/window.c +++ b/window.c @@ -944,7 +944,7 @@ window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, if (wp->saved_grid != NULL) return; - if (!options_get_number(wp->window->options, "alternate-screen")) + if (!options_get_number(wp->options, "alternate-screen")) return; sx = screen_size_x(s); sy = screen_size_y(s); @@ -972,7 +972,7 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, struct screen *s = &wp->base; u_int sx, sy; - if (!options_get_number(wp->window->options, "alternate-screen")) + if (!options_get_number(wp->options, "alternate-screen")) return; /* -- cgit From f4e835754c7e13ed6e810104290608fe27c83cca Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Jun 2019 15:40:14 +0000 Subject: Fix how regex substitution works with empty matches. --- regsub.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/regsub.c b/regsub.c index 53c83a8f..89355bef 100644 --- a/regsub.c +++ b/regsub.c @@ -63,7 +63,8 @@ regsub(const char *pattern, const char *with, const char *text, int flags) { regex_t r; regmatch_t m[10]; - size_t start, end, len = 0; + ssize_t start, end, last, len = 0; + int empty = 0; char *buf = NULL; if (*text == '\0') @@ -72,9 +73,10 @@ regsub(const char *pattern, const char *with, const char *text, int flags) return (NULL); start = 0; + last = 0; end = strlen(text); - while (start != end) { + while (start <= end) { m[0].rm_so = start; m[0].rm_eo = end; @@ -82,14 +84,29 @@ regsub(const char *pattern, const char *with, const char *text, int flags) regsub_copy(&buf, &len, text, start, end); break; } - if (m[0].rm_so == m[0].rm_eo) { - regsub_copy(&buf, &len, text, start, end); - break; - } - regsub_copy(&buf, &len, text, start, m[0].rm_so); - regsub_expand(&buf, &len, with, text, m, nitems(m)); - start = m[0].rm_eo; + /* + * Append any text not part of this match (from the end of the + * last match). + */ + regsub_copy(&buf, &len, text, last, m[0].rm_so); + + /* + * If the last match was empty and this one isn't (it is either + * later or has matched text), expand this match. If it is + * empty, move on one character and try again from there. + */ + if (empty || m[0].rm_so != last || m[0].rm_so != m[0].rm_eo) { + regsub_expand(&buf, &len, with, text, m, nitems(m)); + + last = m[0].rm_eo; + start = m[0].rm_eo; + empty = 0; + } else { + last = m[0].rm_eo; + start = m[0].rm_eo + 1; + empty = 1; + } } buf[len] = '\0'; -- cgit From cb5e681ef6f238a69ee6d366dd9760a49e28887b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Jun 2019 18:13:04 +0000 Subject: FIx return of options_scope_from_name on error. --- options.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/options.c b/options.c index bbb6af98..21ac82f9 100644 --- a/options.c +++ b/options.c @@ -741,7 +741,7 @@ options_scope_from_name(struct args *args, int window, struct window_pane *wp = fs->wp; const char *target = args_get(args, 't'); const struct options_table_entry *oe; - int scope; + int scope = OPTIONS_TABLE_NONE; if (*name == '@') return (options_scope_from_flags(args, window, fs, oo, cause)); @@ -754,21 +754,23 @@ options_scope_from_name(struct args *args, int window, xasprintf(cause, "unknown option: %s", name); return (OPTIONS_TABLE_NONE); } - scope = oe->scope; - - switch (scope) { + switch (oe->scope) { case OPTIONS_TABLE_SERVER: *oo = global_options; + scope = OPTIONS_TABLE_SERVER; break; case OPTIONS_TABLE_SESSION: - if (args_has(args, 'g')) + if (args_has(args, 'g')) { *oo = global_s_options; - else if (s == NULL && target != NULL) + scope = OPTIONS_TABLE_SESSION; + } else if (s == NULL && target != NULL) xasprintf(cause, "no such session: %s", target); else if (s == NULL) xasprintf(cause, "no current session"); - else + else { *oo = s->options; + scope = OPTIONS_TABLE_SESSION; + } break; case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE: if (args_has(args, 'p')) { @@ -776,21 +778,26 @@ options_scope_from_name(struct args *args, int window, xasprintf(cause, "no such pane: %s", target); else if (wp == NULL) xasprintf(cause, "no current pane"); - else + else { *oo = wp->options; + scope = OPTIONS_TABLE_PANE; + } break; } scope = OPTIONS_TABLE_WINDOW; /* FALLTHROUGH */ case OPTIONS_TABLE_WINDOW: - if (args_has(args, 'g')) + if (args_has(args, 'g')) { *oo = global_w_options; - else if (wl == NULL && target != NULL) + scope = OPTIONS_TABLE_WINDOW; + } else if (wl == NULL && target != NULL) xasprintf(cause, "no such window: %s", target); else if (wl == NULL) xasprintf(cause, "no current window"); - else + else { *oo = wl->window->options; + scope = OPTIONS_TABLE_WINDOW; + } break; } return (scope); -- cgit From 97a317a656e27523ed7437d8512be36080c739ed Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Jun 2019 19:29:38 +0000 Subject: Need to always check focus even if not current window. --- server-client.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server-client.c b/server-client.c index 14c20617..e4862d84 100644 --- a/server-client.c +++ b/server-client.c @@ -1265,10 +1265,11 @@ server_client_loop(void) break; } TAILQ_FOREACH(wp, &w->panes, entry) { - if (wl != NULL && wp->fd != -1) { + if (wp->fd != -1) { if (focus) server_client_check_focus(wp); - server_client_check_resize(wp); + if (wl != NULL) + server_client_check_resize(wp); } wp->flags &= ~PANE_REDRAW; } -- cgit From 1d8e545bc1cf9de10b718dc81b422edf81dabe07 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Jun 2019 20:31:04 +0000 Subject: Add -r to find-window for regex instead of fnmatch. --- cmd-find-window.c | 81 +++++++++++++++++++++++++++++++++++++------------------ tmux.1 | 8 +++--- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/cmd-find-window.c b/cmd-find-window.c index c2d230a5..c29878b5 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 = { "CNt:TZ", 1, 1 }, - .usage = "[-CNTZ] " CMD_TARGET_PANE_USAGE " match-string", + .args = { "CNrt:TZ", 1, 1 }, + .usage = "[-CNrTZ] " CMD_TARGET_PANE_USAGE " match-string", .target = { 't', CMD_FIND_PANE, 0 }, @@ -57,30 +57,59 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) if (!C && !N && !T) C = N = T = 1; - 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); + 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); + } new_args = args_parse("", 1, &argv); if (args_has(args, 'Z')) diff --git a/tmux.1 b/tmux.1 index 1315e29a..a7b4af05 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1828,14 +1828,16 @@ With .Fl b , other commands are not blocked from running until the indicator is closed. .It Xo Ic find-window -.Op Fl CNTZ +.Op Fl rCNTZ .Op Fl t Ar target-pane .Ar match-string .Xc .D1 (alias: Ic findw ) -Search for the +Search for a .Xr fnmatch 3 -pattern +pattern or, with +.Fl r , +regular expression .Ar match-string in window names, titles, and visible content (but not history). The flags control matching behavior: -- cgit From e3c2772d2f7d876c5284062d7cb1695f5e395e9a Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 21 Jun 2019 07:11:01 +0000 Subject: Man page fixes from lacygoill at lacygoill dot me. --- tmux.1 | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tmux.1 b/tmux.1 index a7b4af05..f4ba7471 100644 --- a/tmux.1 +++ b/tmux.1 @@ -456,7 +456,7 @@ Will execute .Ic if-shell , the shell command .Xr true 1 , -.Ic new-window +.Ic split-window and .Ic kill-session in that order. @@ -470,7 +470,7 @@ commands and their arguments. This section describes the syntax of commands parsed by .Nm , for example in a configuration file or at the command prompt. -Note the when commands are entered into the shell, they are parsed by the shell +Note that when commands are entered into the shell, they are parsed by the shell - see for example .Xr ksh 1 or @@ -2333,9 +2333,7 @@ spreads the current pane and any panes next to it out evenly. Make pane .Ar target-pane the active pane in window -.Ar target-window , -or set its style (with -.Fl P ) . +.Ar target-window. If one of .Fl D , .Fl L , @@ -4065,7 +4063,7 @@ will expand the format twice, for example .Ql #{E:status-left} is the result of expanding the content of the .Ic status-left -option rather than the content itself. +option rather than the option itself. .Ql T: is like .Ql E: @@ -4101,7 +4099,7 @@ would change into .Ql bxBxbx . .Pp -In addition, the first line of a shell command's output may be inserted using +In addition, the last line of a shell command's output may be inserted using .Ql #() . For example, .Ql #(uptime) @@ -4120,6 +4118,14 @@ global environment set (see the .Sx GLOBAL AND SESSION ENVIRONMENT section). .Pp +An +.Ql l +specifies that a string should be interpreted literally and not expanded. +For example +.Ql #{l:#{?pane_in_mode,yes,no}} +will be replaced by +.Ql #{?pane_in_mode,yes,no} . +.Pp The following variables are available, where appropriate: .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" .It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" -- cgit From f1100f97f7a06fcbebcf18b2c46e7f8f89fdd65b Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 23 Jun 2019 10:00:29 +0000 Subject: Do not always set scope for panes because the window check might fail, GitHub issue 1810. --- options.c | 1 - 1 file changed, 1 deletion(-) diff --git a/options.c b/options.c index 21ac82f9..1be9f8cd 100644 --- a/options.c +++ b/options.c @@ -784,7 +784,6 @@ options_scope_from_name(struct args *args, int window, } break; } - scope = OPTIONS_TABLE_WINDOW; /* FALLTHROUGH */ case OPTIONS_TABLE_WINDOW: if (args_has(args, 'g')) { -- cgit From bdab5950955539ea4ffab0816faf182607db4c2b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 24 Jun 2019 08:20:02 +0000 Subject: Trim trailing spaces when matching. --- window.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/window.c b/window.c index 77ca2ce2..f900a1b2 100644 --- a/window.c +++ b/window.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -1222,6 +1223,7 @@ window_pane_search(struct window_pane *wp, const char *term, int regex, char *new = NULL, *line; u_int i; int flags = 0, found; + size_t n; if (!regex) { if (ignore) @@ -1236,6 +1238,12 @@ window_pane_search(struct window_pane *wp, const char *term, int regex, for (i = 0; i < screen_size_y(s); i++) { line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); + for (n = strlen(line); n > 0; n--) { + if (!isspace((u_char)line[n - 1])) + break; + line[n - 1] = '\0'; + } + log_debug("%s: %s", __func__, line); if (!regex) found = (fnmatch(new, line, 0) == 0); else -- cgit From 20b938bcb18b8ae8b0535a1bcf8e7e1670a830bc Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 24 Jun 2019 10:04:29 +0000 Subject: Expand arguments to C and s format modifiers (matches m which already expands). --- format.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/format.c b/format.c index 6b07e67e..cb67405f 100644 --- a/format.c +++ b/format.c @@ -1644,13 +1644,15 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; } else if (search != NULL) { /* Search in pane. */ + new = format_expand(ft, copy); if (wp == NULL) { - format_log(ft, "search '%s' but no pane", copy); + format_log(ft, "search '%s' but no pane", new); value = xstrdup("0"); } else { - format_log(ft, "search '%s' pane %%%u", copy, wp->id); - value = format_search(fm, wp, copy); + format_log(ft, "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) { @@ -1780,11 +1782,14 @@ done: /* Perform substitution if any. */ if (sub != NULL) { - new = format_sub(sub, value, sub->argv[0], sub->argv[1]); - format_log(ft, "substituted '%s' to '%s': %s", sub->argv[0], - sub->argv[1], new); + left = format_expand(ft, sub->argv[0]); + right = format_expand(ft, sub->argv[1]); + new = format_sub(sub, value, left, right); + format_log(ft, "substitute '%s' to '%s': %s", left, right, new); free(value); value = new; + free(right); + free(left); } /* Truncate the value if needed. */ -- cgit From d83f356218fc9144735667e39c9553bcf905d10b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 26 Jun 2019 13:03:47 +0000 Subject: Add #define for the pane status line option position numbers. --- layout.c | 36 ++++++++++++++++++------------------ options-table.c | 2 +- screen-redraw.c | 22 +++++++++------------- tmux.h | 5 +++++ window.c | 48 ++++++++++++++++++++++++++++++++++++------------ 5 files changed, 69 insertions(+), 44 deletions(-) diff --git a/layout.c b/layout.c index 86d307ef..6e78b439 100644 --- a/layout.c +++ b/layout.c @@ -232,20 +232,20 @@ layout_fix_offsets(struct layout_cell *lc) * case for the most upper panes only. */ static int -layout_need_status(struct layout_cell *lc, int at_top) +layout_need_status(struct layout_cell *lc, int status) { - struct layout_cell *first_lc; + struct layout_cell *next; if (lc->parent != NULL) { if (lc->parent->type == LAYOUT_LEFTRIGHT) - return (layout_need_status(lc->parent, at_top)); + return (layout_need_status(lc->parent, status)); - if (at_top) - first_lc = TAILQ_FIRST(&lc->parent->cells); + if (status == PANE_STATUS_TOP) + next = TAILQ_FIRST(&lc->parent->cells); else - first_lc = TAILQ_LAST(&lc->parent->cells,layout_cells); - if (lc == first_lc) - return (layout_need_status(lc->parent, at_top)); + next = TAILQ_LAST(&lc->parent->cells,layout_cells); + if (lc == next) + return (layout_need_status(lc->parent, status)); return (0); } return (1); @@ -264,8 +264,8 @@ layout_fix_panes(struct window *w) if ((lc = wp->layout_cell) == NULL) continue; - if (status != 0) - shift = layout_need_status(lc, status == 1); + if (status != PANE_STATUS_OFF) + shift = layout_need_status(lc, status); else shift = 0; @@ -317,8 +317,8 @@ layout_resize_check(struct window *w, struct layout_cell *lc, available = lc->sx; else { available = lc->sy; - if (status != 0) - minimum += layout_need_status(lc, status == 1); + if (status != PANE_STATUS_OFF) + minimum += layout_need_status(lc, status); } if (available > minimum) available -= minimum; @@ -862,8 +862,8 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, break; case LAYOUT_TOPBOTTOM: minimum = PANE_MINIMUM * 2 + 1; - if (status != 0) - minimum += layout_need_status(lc, status == 1); + if (status != PANE_STATUS_OFF) + minimum += layout_need_status(lc, status); if (sy < minimum) return (NULL); break; @@ -1030,8 +1030,8 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) size = parent->sx; else if (parent->type == LAYOUT_TOPBOTTOM) { size = parent->sy; - if (status != 0) - size -= layout_need_status(parent, status == 1); + if (status != PANE_STATUS_OFF) + size -= layout_need_status(parent, status); } else return (0); if (size < number - 1) @@ -1050,8 +1050,8 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change); } else if (parent->type == LAYOUT_TOPBOTTOM) { this = each; - if (status != 0) - this += layout_need_status(lc, status == 1); + if (status != PANE_STATUS_OFF) + this += layout_need_status(lc, status); change = this - (int)lc->sy; layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change); } diff --git a/options-table.c b/options-table.c index 228cf3f0..7bb767c7 100644 --- a/options-table.c +++ b/options-table.c @@ -689,7 +689,7 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_pane_status_list, - .default_num = 0 + .default_num = PANE_STATUS_OFF }, { .name = "pane-border-style", diff --git a/screen-redraw.c b/screen-redraw.c index 65e890b6..2943e73b 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -45,10 +45,6 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *, #define CELL_BORDERS " xqlkmjwvtun~" -#define CELL_STATUS_OFF 0 -#define CELL_STATUS_TOP 1 -#define CELL_STATUS_BOTTOM 2 - /* 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) @@ -112,12 +108,12 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, if (px > w->sx || py > w->sy) return (CELL_OUTSIDE); - if (pane_status != CELL_STATUS_OFF) { + if (pane_status != PANE_STATUS_OFF) { TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; - if (pane_status == CELL_STATUS_TOP) + if (pane_status == PANE_STATUS_TOP) line = wp->yoff - 1; else line = wp->yoff + wp->sy; @@ -153,7 +149,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, borders |= 8; if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py)) borders |= 4; - if (pane_status == CELL_STATUS_TOP) { + if (pane_status == PANE_STATUS_TOP) { if (py != 0 && screen_redraw_cell_border(c, px, py - 1)) borders |= 2; } else { @@ -208,9 +204,9 @@ screen_redraw_check_is(u_int px, u_int py, int type, int pane_status, border = screen_redraw_cell_border1(wantwp, px, py); if (border == 0 || border == -1) return (0); - if (pane_status == CELL_STATUS_TOP && border == 4) + if (pane_status == PANE_STATUS_TOP && border == 4) return (0); - if (pane_status == CELL_STATUS_BOTTOM && border == 3) + if (pane_status == PANE_STATUS_BOTTOM && border == 3) return (0); /* If there are more than two panes, that's enough. */ @@ -222,7 +218,7 @@ screen_redraw_check_is(u_int px, u_int py, int type, int pane_status, return (1); /* With status lines mark the entire line. */ - if (pane_status != CELL_STATUS_OFF) + if (pane_status != PANE_STATUS_OFF) return (1); /* Check if the pane covers the whole width. */ @@ -324,7 +320,7 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) s = &wp->status_screen; size = wp->status_size; - if (ctx->pane_status == CELL_STATUS_TOP) + if (ctx->pane_status == PANE_STATUS_TOP) yoff = wp->yoff - 1; else yoff = wp->yoff + wp->sy; @@ -386,7 +382,7 @@ screen_redraw_update(struct client *c, int flags) if (c->overlay_draw != NULL) flags |= CLIENT_REDRAWOVERLAY; - if (options_get_number(wo, "pane-border-status") != CELL_STATUS_OFF) { + if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) { redraw = 0; TAILQ_FOREACH(wp, &w->panes, entry) { if (screen_redraw_make_pane_status(c, w, wp)) @@ -441,7 +437,7 @@ screen_redraw_screen(struct client *c) screen_redraw_set_context(c, &ctx); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { - if (ctx.pane_status != CELL_STATUS_OFF) + if (ctx.pane_status != PANE_STATUS_OFF) screen_redraw_draw_pane_status(&ctx); screen_redraw_draw_borders(&ctx); } diff --git a/tmux.h b/tmux.h index 8adfcba8..ba257ea8 100644 --- a/tmux.h +++ b/tmux.h @@ -955,6 +955,11 @@ TAILQ_HEAD(winlink_stack, winlink); #define WINDOW_SIZE_SMALLEST 1 #define WINDOW_SIZE_MANUAL 2 +/* Pane border status option. */ +#define PANE_STATUS_OFF 0 +#define PANE_STATUS_TOP 1 +#define PANE_STATUS_BOTTOM 2 + /* Layout direction. */ enum layout_type { LAYOUT_LEFTRIGHT, diff --git a/window.c b/window.c index f900a1b2..c8b9b710 100644 --- a/window.c +++ b/window.c @@ -1288,25 +1288,35 @@ window_pane_choose_best(struct window_pane **list, u_int size) struct window_pane * window_pane_find_up(struct window_pane *wp) { + struct window *w; struct window_pane *next, *best, **list; u_int edge, left, right, end, size; int status, found; if (wp == NULL) return (NULL); - status = options_get_number(wp->window->options, "pane-border-status"); + w = wp->window; + status = options_get_number(w->options, "pane-border-status"); list = NULL; size = 0; edge = wp->yoff; - if (edge == (status == 1 ? 1 : 0)) - edge = wp->window->sy + 1 - (status == 2 ? 1 : 0); + if (status == PANE_STATUS_TOP) { + if (edge == 1) + edge = w->sy + 1; + } else if (status == PANE_STATUS_BOTTOM) { + if (edge == 0) + edge = w->sy; + } else { + if (edge == 0) + edge = w->sy + 1; + } left = wp->xoff; right = wp->xoff + wp->sx; - TAILQ_FOREACH(next, &wp->window->panes, entry) { + TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->yoff + next->sy + 1 != edge) @@ -1335,25 +1345,35 @@ window_pane_find_up(struct window_pane *wp) struct window_pane * window_pane_find_down(struct window_pane *wp) { + struct window *w; struct window_pane *next, *best, **list; u_int edge, left, right, end, size; int status, found; if (wp == NULL) return (NULL); - status = options_get_number(wp->window->options, "pane-border-status"); + w = wp->window; + status = options_get_number(w->options, "pane-border-status"); list = NULL; size = 0; edge = wp->yoff + wp->sy + 1; - if (edge >= wp->window->sy - (status == 2 ? 1 : 0)) - edge = (status == 1 ? 1 : 0); + if (status == PANE_STATUS_TOP) { + if (edge >= w->sy) + edge = 1; + } else if (status == PANE_STATUS_BOTTOM) { + if (edge >= w->sy - 1) + edge = 0; + } else { + if (edge >= wp->sy) + edge = 0; + } left = wp->xoff; right = wp->xoff + wp->sx; - TAILQ_FOREACH(next, &wp->window->panes, entry) { + TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->yoff != edge) @@ -1382,24 +1402,26 @@ window_pane_find_down(struct window_pane *wp) struct window_pane * window_pane_find_left(struct window_pane *wp) { + struct window *w; struct window_pane *next, *best, **list; u_int edge, top, bottom, end, size; int found; if (wp == NULL) return (NULL); + w = wp->window; list = NULL; size = 0; edge = wp->xoff; if (edge == 0) - edge = wp->window->sx + 1; + edge = w->sx + 1; top = wp->yoff; bottom = wp->yoff + wp->sy; - TAILQ_FOREACH(next, &wp->window->panes, entry) { + TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->xoff + next->sx + 1 != edge) @@ -1428,24 +1450,26 @@ window_pane_find_left(struct window_pane *wp) struct window_pane * window_pane_find_right(struct window_pane *wp) { + struct window *w; struct window_pane *next, *best, **list; u_int edge, top, bottom, end, size; int found; if (wp == NULL) return (NULL); + w = wp->window; list = NULL; size = 0; edge = wp->xoff + wp->sx + 1; - if (edge >= wp->window->sx) + if (edge >= w->sx) edge = 0; top = wp->yoff; bottom = wp->yoff + wp->sy; - TAILQ_FOREACH(next, &wp->window->panes, entry) { + TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->xoff != edge) -- cgit From 80d76612b8e8a572f96a58bcbd217f81d58d9b0f Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 26 Jun 2019 13:05:06 +0000 Subject: Fix some comments (top/bottom not left/right). --- tty.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tty.c b/tty.c index 8d3b2858..1c0c88d4 100644 --- a/tty.c +++ b/tty.c @@ -1060,17 +1060,17 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, *y = ctx->yoff + py - ctx->oy; *ry = ny; } else if (yoff < ctx->oy && yoff + ny > ctx->oy + ctx->sy) { - /* Both left and right not visible. */ + /* Both top and bottom not visible. */ *j = ctx->oy; *y = 0; *ry = ctx->sy; } else if (yoff < ctx->oy) { - /* Left not visible. */ + /* Top not visible. */ *j = ctx->oy - (ctx->yoff + py); *y = 0; *ry = ny - *j; } else { - /* Right not visible. */ + /* Bottom not visible. */ *j = 0; *y = (ctx->yoff + py) - ctx->oy; *ry = ctx->sy - *y; -- cgit From c599ad63f8857bd74e85150e60339fd2efbb9650 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 26 Jun 2019 13:05:24 +0000 Subject: Log window and pane resizes. --- window.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/window.c b/window.c index c8b9b710..8108600d 100644 --- a/window.c +++ b/window.c @@ -412,6 +412,7 @@ window_set_name(struct window *w, const char *new_name) void window_resize(struct window *w, u_int sx, u_int sy) { + log_debug("%s: @%u resize %ux%u", __func__, w->id, sx, sy); w->sx = sx; w->sy = sy; } @@ -923,6 +924,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wp->sx = sx; 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); wme = TAILQ_FIRST(&wp->modes); -- cgit From 87ea14328ce60fdfb8bd0aef15b7e3b3bdccb1ed Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 26 Jun 2019 18:28:31 +0000 Subject: Pass keys that aren't 0-9 on to normal key processing when display-panes is active (restores previous behaviour). --- cmd-display-panes.c | 2 +- server-client.c | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd-display-panes.c b/cmd-display-panes.c index aa665f08..df97819c 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -204,7 +204,7 @@ cmd_display_panes_key(struct client *c, struct key_event *event) struct cmd_parse_result *pr; if (event->key < '0' || event->key > '9') - return (1); + return (-1); wp = window_pane_at_index(w, event->key - '0'); if (wp == NULL) diff --git a/server-client.c b/server-client.c index e4862d84..a982fe8e 100644 --- a/server-client.c +++ b/server-client.c @@ -1220,9 +1220,13 @@ server_client_handle_key(struct client *c, struct key_event *event) * blocked so they need to be processed immediately rather than queued. */ if ((~c->flags & CLIENT_READONLY) && c->overlay_key != NULL) { - if (c->overlay_key(c, event) != 0) + switch (c->overlay_key(c, event)) { + case 0: + return (0); + case 1: server_client_clear_overlay(c); - return (0); + return (0); + } } /* -- cgit From 3a6d90adadfcd4aa6b513df7f8ae5c4dcc05a6dc Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 26 Jun 2019 18:44:22 +0000 Subject: Fix a typo in window_pane_find_down (w not wp) and a missing PANE_STATUS_TOP. --- layout.c | 4 ++-- window.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/layout.c b/layout.c index 6e78b439..e84da936 100644 --- a/layout.c +++ b/layout.c @@ -272,7 +272,7 @@ layout_fix_panes(struct window *w) wp->xoff = lc->xoff; wp->yoff = lc->yoff; - if (shift && status == 1) + if (shift && status == PANE_STATUS_TOP) wp->yoff += 1; window_pane_resize(wp, lc->sx, lc->sy - shift); @@ -1021,7 +1021,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) number = 0; TAILQ_FOREACH (lc, &parent->cells, entry) - number++; + number++; if (number <= 1) return (0); status = options_get_number(w->options, "pane-border-status"); diff --git a/window.c b/window.c index 8108600d..65097e1c 100644 --- a/window.c +++ b/window.c @@ -1368,7 +1368,7 @@ window_pane_find_down(struct window_pane *wp) if (edge >= w->sy - 1) edge = 0; } else { - if (edge >= wp->sy) + if (edge >= w->sy) edge = 0; } -- cgit From dae2868d1227b95fd076fb4a5efa6256c7245943 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 27 Jun 2019 15:17:41 +0000 Subject: Add support for underscore colours with Setulc capability, mostly from Kai Moschcau. --- colour.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- grid.c | 6 +++-- input.c | 11 ++++++-- screen-write.c | 2 +- style.c | 2 +- tmux.1 | 30 ++++++++++++++++++--- tmux.h | 5 +++- tty-term.c | 1 + tty.c | 64 ++++++++++++++++++++++++++++++++++++++++---- 9 files changed, 184 insertions(+), 21 deletions(-) diff --git a/colour.c b/colour.c index eaee6058..c7972878 100644 --- a/colour.c +++ b/colour.c @@ -230,11 +230,85 @@ colour_fromstring(const char *s) return (-1); } -/* Convert 256 colour palette to 16. */ -u_char -colour_256to16(u_char c) +/* Convert 256 colour to RGB colour. */ +int +colour_256toRGB(int c) +{ + static const int table[256] = { + 0x000000, 0x800000, 0x008000, 0x808000, + 0x000080, 0x800080, 0x008080, 0xc0c0c0, + 0x808080, 0xff0000, 0x00ff00, 0xffff00, + 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, + 0x000000, 0x00005f, 0x000087, 0x0000af, + 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, + 0x005f87, 0x005faf, 0x005fd7, 0x005fff, + 0x008700, 0x00875f, 0x008787, 0x0087af, + 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, + 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, + 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, + 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, + 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, + 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, + 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, + 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, + 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, + 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, + 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, + 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, + 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, + 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, + 0x870000, 0x87005f, 0x870087, 0x8700af, + 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, + 0x875f87, 0x875faf, 0x875fd7, 0x875fff, + 0x878700, 0x87875f, 0x878787, 0x8787af, + 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, + 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, + 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, + 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, + 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, + 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, + 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, + 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, + 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, + 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, + 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, + 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, + 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, + 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, + 0xd70000, 0xd7005f, 0xd70087, 0xd700af, + 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, + 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, + 0xd78700, 0xd7875f, 0xd78787, 0xd787af, + 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, + 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, + 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, + 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, + 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, + 0xff0000, 0xff005f, 0xff0087, 0xff00af, + 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, + 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, + 0xff8700, 0xff875f, 0xff8787, 0xff87af, + 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, + 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, + 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, + 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, + 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, + 0x080808, 0x121212, 0x1c1c1c, 0x262626, + 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, + 0x585858, 0x626262, 0x6c6c6c, 0x767676, + 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, + 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, + 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee + }; + + return (table[c & 0xff] | COLOUR_FLAG_RGB); +} + +/* Convert 256 colour to 16 colour. */ +int +colour_256to16(int c) { - static const u_char table[256] = { + static const char table[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, @@ -253,5 +327,5 @@ colour_256to16(u_char c) 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15 }; - return (table[c]); + return (table[c & 0xff]); } diff --git a/grid.c b/grid.c index d185f364..cddb5871 100644 --- a/grid.c +++ b/grid.c @@ -37,12 +37,12 @@ /* Default grid cell data. */ const struct grid_cell grid_default_cell = { - 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } + 0, 0, 8, 8, 0, { { ' ' }, 0, 1, 1 } }; /* Cleared grid cell data. */ const struct grid_cell grid_cleared_cell = { - GRID_FLAG_CLEARED, 0, 8, 8, { { ' ' }, 0, 1, 1 } + GRID_FLAG_CLEARED, 0, 8, 8, 0, { { ' ' }, 0, 1, 1 } }; static const struct grid_cell_entry grid_cleared_entry = { GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } } @@ -82,6 +82,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce, return (1); if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB)) return (1); + if (gc->us != 0) /* only supports 256 or RGB */ + return (1); return (0); } diff --git a/input.c b/input.c index 7df2a7b0..ff8d7a17 100644 --- a/input.c +++ b/input.c @@ -1829,6 +1829,8 @@ input_csi_dispatch_sgr_256_do(struct input_ctx *ictx, int fgbg, int c) gc->fg = c | COLOUR_FLAG_256; else if (fgbg == 48) gc->bg = c | COLOUR_FLAG_256; + else if (fgbg == 58) + gc->us = c | COLOUR_FLAG_256; } return (1); } @@ -1862,6 +1864,8 @@ input_csi_dispatch_sgr_rgb_do(struct input_ctx *ictx, int fgbg, int r, int g, gc->fg = colour_join_rgb(r, g, b); else if (fgbg == 48) gc->bg = colour_join_rgb(r, g, b); + else if (fgbg == 58) + gc->us = colour_join_rgb(r, g, b); return (1); } @@ -1938,7 +1942,7 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i) } return; } - if (n < 2 || (p[0] != 38 && p[0] != 48)) + if (n < 2 || (p[0] != 38 && p[0] != 48 && p[0] != 58)) return; switch (p[1]) { case 2: @@ -1983,7 +1987,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) if (n == -1) continue; - if (n == 38 || n == 48) { + if (n == 38 || n == 48 || n == 58) { i++; switch (input_get(ictx, i, 0, -1)) { case 2: @@ -2078,6 +2082,9 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 55: gc->attr &= ~GRID_ATTR_OVERLINE; break; + case 59: + gc->us = 0; + break; case 90: case 91: case 92: diff --git a/screen-write.c b/screen-write.c index 174c7a82..631328a3 100644 --- a/screen-write.c +++ b/screen-write.c @@ -36,7 +36,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 = { - GRID_FLAG_PADDING, 0, 8, 8, { { 0 }, 0, 0, 0 } + GRID_FLAG_PADDING, 0, 8, 8, 0, { { 0 }, 0, 0, 0 } }; struct screen_write_collect_item { diff --git a/style.c b/style.c index ce78175a..4c6174c7 100644 --- a/style.c +++ b/style.c @@ -30,7 +30,7 @@ /* Default style. */ static struct style style_default = { - { 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } }, + { 0, 0, 8, 8, 0, { { ' ' }, 0, 1, 1 } }, STYLE_ALIGN_DEFAULT, STYLE_LIST_OFF, diff --git a/tmux.1 b/tmux.1 index f4ba7471..8b093220 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5044,11 +5044,33 @@ $ 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 underline. -The single parameter is one of: 0 for no underline, 1 for normal -underline, 2 for double underline, 3 for curly underline, 4 for dotted -underline and 5 for dashed underline. +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.h b/tmux.h index ba257ea8..7126b30f 100644 --- a/tmux.h +++ b/tmux.h @@ -427,6 +427,7 @@ enum tty_code_code { TTYC_SETAF, TTYC_SETRGBB, TTYC_SETRGBF, + TTYC_SETULC, TTYC_SGR0, TTYC_SITM, TTYC_SMACS, @@ -599,6 +600,7 @@ struct grid_cell { u_short attr; int fg; int bg; + int us; struct utf8_data data; }; struct grid_cell_entry { @@ -2194,7 +2196,8 @@ int colour_join_rgb(u_char, u_char, u_char); void colour_split_rgb(int, u_char *, u_char *, u_char *); const char *colour_tostring(int); int colour_fromstring(const char *s); -u_char colour_256to16(u_char); +int colour_256toRGB(int); +int colour_256to16(int); /* attributes.c */ const char *attributes_tostring(int); diff --git a/tty-term.c b/tty-term.c index 83bfcdc1..39e2b013 100644 --- a/tty-term.c +++ b/tty-term.c @@ -246,6 +246,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SETAF] = { TTYCODE_STRING, "setaf" }, [TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" }, [TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" }, + [TTYC_SETULC] = { TTYCODE_STRING, "Setulc" }, [TTYC_SE] = { TTYCODE_STRING, "Se" }, [TTYC_SGR0] = { TTYCODE_STRING, "sgr0" }, [TTYC_SITM] = { TTYCODE_STRING, "sitm" }, diff --git a/tty.c b/tty.c index 1c0c88d4..c3e2f0f6 100644 --- a/tty.c +++ b/tty.c @@ -49,8 +49,11 @@ 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_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 *); static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int); @@ -1287,6 +1290,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, gcp->attr != last.attr || gcp->fg != last.fg || gcp->bg != last.bg || + gcp->us != last.us || ux + width + gcp->data.width > nx || (sizeof buf) - len < gcp->data.size)) { tty_attributes(tty, &last, wp); @@ -2135,7 +2139,8 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, ~(wp->flags & PANE_STYLECHANGED) && gc->attr == tty->last_cell.attr && gc->fg == tty->last_cell.fg && - gc->bg == tty->last_cell.bg) + 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); @@ -2163,14 +2168,18 @@ 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); - /* If any bits are being cleared, reset everything. */ - if (tc->attr & ~gc2.attr) + /* + * If any bits are being cleared or the underline colour is now default, + * reset everything. + */ + if ((tc->attr & ~gc2.attr) || (tc->us != gc2.us && gc2.us == 0)) tty_reset(tty); /* * Set the colours. This may call tty_reset() (so it comes next) and - * may add to (NOT remove) the desired attributes by changing new_attr. + * may add to (NOT remove) the desired attributes. */ tty_colours(tty, &gc2); @@ -2223,7 +2232,7 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) int have_ax; /* No changes? Nothing is necessary. */ - if (gc->fg == tc->fg && gc->bg == tc->bg) + if (gc->fg == tc->fg && gc->bg == tc->bg && gc->us == tc->us) return; /* @@ -2271,6 +2280,10 @@ 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. */ + if (gc->us != tc->us) + tty_colours_us(tty, gc); } static void @@ -2385,6 +2398,22 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) gc->bg -= 90; } +static void +tty_check_us(__unused struct tty *tty, struct window_pane *wp, 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) + gc->us = c; + } + + /* Underscore colour is set as RGB so convert a 256 colour to RGB. */ + if (gc->us & COLOUR_FLAG_256) + gc->us = colour_256toRGB (gc->us); +} + static void tty_colours_fg(struct tty *tty, const struct grid_cell *gc) { @@ -2449,6 +2478,31 @@ save_bg: tc->bg = gc->bg; } +static void +tty_colours_us(struct tty *tty, const struct grid_cell *gc) +{ + struct grid_cell *tc = &tty->cell; + u_int c; + u_char r, g, b; + + /* 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" + * 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); + + /* Save the new values in the terminal current cell. */ + tc->us = gc->us; +} + static int tty_try_colour(struct tty *tty, int colour, const char *type) { -- cgit From b434692db20e6ef279ca13b6d25b0d519ac2c134 Mon Sep 17 00:00:00 2001 From: jmc Date: Thu, 27 Jun 2019 17:29:16 +0000 Subject: minor eol issues; --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 8b093220..86f2865b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2333,7 +2333,7 @@ spreads the current pane and any panes next to it out evenly. Make pane .Ar target-pane the active pane in window -.Ar target-window. +.Ar target-window . If one of .Fl D , .Fl L , -- cgit From 6ce38b73956836c26c8914cdf5002da7900a5d2d Mon Sep 17 00:00:00 2001 From: deraadt Date: Fri, 28 Jun 2019 05:44:09 +0000 Subject: asprintf returns -1, not an arbitrary value < 0. Also upon error the (very sloppy specification) leaves an undefined value in *ret, so it is wrong to inspect it, the error condition is enough. discussed a little with nicm, and then much more with millert until we were exasperated --- xmalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmalloc.c b/xmalloc.c index 22ea3540..f249e397 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -111,7 +111,7 @@ xvasprintf(char **ret, const char *fmt, va_list ap) i = vasprintf(ret, fmt, ap); - if (i < 0 || *ret == NULL) + if (i == -1) fatalx("xasprintf: %s", strerror(errno)); return i; -- cgit From 4ff7bc3eb32e9d66312d16757fb8c083df2d87d6 Mon Sep 17 00:00:00 2001 From: deraadt Date: Fri, 28 Jun 2019 13:35:05 +0000 Subject: When system calls indicate an error they return -1, not some arbitrary value < 0. errno is only updated in this case. Change all (most?) callers of syscalls to follow this better, and let's see if this strictness helps us in the future. --- client.c | 2 +- job.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index b376b038..d071b1db 100644 --- a/client.c +++ b/client.c @@ -434,7 +434,7 @@ client_stdin_callback(__unused int fd, __unused short events, struct msg_stdin_data data; data.size = read(STDIN_FILENO, data.data, sizeof data.data); - if (data.size < 0 && (errno == EINTR || errno == EAGAIN)) + if (data.size == -1 && (errno == EINTR || errno == EAGAIN)) return; proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); diff --git a/job.c b/job.c index 73f62359..0c316fd8 100644 --- a/job.c +++ b/job.c @@ -118,7 +118,7 @@ job_run(const char *cmd, struct session *s, const char *cwd, close(out[0]); nullfd = open(_PATH_DEVNULL, O_RDWR, 0); - if (nullfd < 0) + if (nullfd == -1) fatal("open failed"); if (dup2(nullfd, STDERR_FILENO) == -1) fatal("dup2 failed"); -- cgit From cf30e0f9357b7e42491645a87eeae06ffb8e8066 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 30 Jun 2019 19:21:53 +0000 Subject: Do not double free window if pane fails to start. --- spawn.c | 4 +--- tmux.h | 1 - window.c | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/spawn.c b/spawn.c index bae220e1..08d0426c 100644 --- a/spawn.c +++ b/spawn.c @@ -172,10 +172,8 @@ spawn_window(struct spawn_context *sc, char **cause) /* Spawn the pane. */ wp = spawn_pane(sc, cause); if (wp == NULL) { - if (~sc->flags & SPAWN_RESPAWN) { - window_destroy(w); + if (~sc->flags & SPAWN_RESPAWN) winlink_remove(&s->windows, sc->wl); - } return (NULL); } diff --git a/tmux.h b/tmux.h index 7126b30f..d6dacf63 100644 --- a/tmux.h +++ b/tmux.h @@ -2362,7 +2362,6 @@ struct window *window_find_by_id_str(const char *); struct window *window_find_by_id(u_int); void window_update_activity(struct window *); struct window *window_create(u_int, u_int); -void window_destroy(struct window *); void window_pane_set_event(struct window_pane *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); diff --git a/window.c b/window.c index 65097e1c..06145221 100644 --- a/window.c +++ b/window.c @@ -338,7 +338,7 @@ window_create(u_int sx, u_int sy) return (w); } -void +static void window_destroy(struct window *w) { log_debug("window @%u destroyed (%d references)", w->id, w->references); -- cgit From 2da050413c17e587be8bede710112096ce6b5f5a Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Jul 2019 06:56:00 +0000 Subject: Add a "fill" style attribute to clear the entire format drawing area in a colour, GitHub issue 1815. --- format-draw.c | 15 ++++++++++++++- style.c | 12 ++++++++++++ tmux.1 | 3 +++ tmux.h | 1 + 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/format-draw.c b/format-draw.c index b589ca5e..bb5c65fb 100644 --- a/format-draw.c +++ b/format-draw.c @@ -511,8 +511,9 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL]; u_int map[] = { LEFT, LEFT, CENTRE, RIGHT }; int focus_start = -1, focus_end = -1; - int list_state = -1; + int list_state = -1, fill = -1; enum style_align list_align = STYLE_ALIGN_DEFAULT; + struct grid_cell gc; struct style sy; struct utf8_data *ud = &sy.gc.data; const char *cp, *end; @@ -590,6 +591,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, style_tostring(&sy)); free(tmp); + /* If this style has a fill colour, store it for later. */ + if (sy.fill != 8) + fill = sy.fill; + /* Check the list state. */ switch (sy.list) { case STYLE_LIST_ON: @@ -711,6 +716,14 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, fr->argument, names[fr->index], fr->start, fr->end); } + /* Clear the available area. */ + if (fill != -1) { + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.bg = fill; + for (i = 0; i < available; i++) + screen_write_putc(octx, &gc, ' '); + } + /* * Draw the screens. How they are arranged depends on where the list * appearsq. diff --git a/style.c b/style.c index 4c6174c7..f195301f 100644 --- a/style.c +++ b/style.c @@ -32,6 +32,7 @@ static struct style style_default = { { 0, 0, 8, 8, 0, { { ' ' }, 0, 1, 1 } }, + 8, STYLE_ALIGN_DEFAULT, STYLE_LIST_OFF, @@ -127,6 +128,10 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->align = STYLE_ALIGN_RIGHT; else goto error; + } else if (end > 5 && strncasecmp(tmp, "fill=", 5) == 0) { + if ((value = colour_fromstring(tmp + 5)) == -1) + goto error; + sy->fill = value; } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) { if ((value = colour_fromstring(tmp + 3)) == -1) goto error; @@ -213,6 +218,11 @@ style_tostring(struct style *sy) tmp); comma = ","; } + if (sy->fill != 8) { + off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma, + colour_tostring(sy->fill)); + comma = ","; + } if (gc->fg != 8) { off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma, colour_tostring(gc->fg)); @@ -290,6 +300,8 @@ style_equal(struct style *sy1, struct style *sy2) 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); diff --git a/tmux.1 b/tmux.1 index 86f2865b..44ef4a96 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4337,6 +4337,9 @@ to unset. .Ic align=right .Xc Align text to the left, centre or right of the available space if appropriate. +.It Xo Ic fill=colour +.Xc +Fill the available space with a background colour if appropriate. .It Xo Ic list=on , .Ic list=focus , .Ic list=left-marker , diff --git a/tmux.h b/tmux.h index d6dacf63..e43140b2 100644 --- a/tmux.h +++ b/tmux.h @@ -682,6 +682,7 @@ TAILQ_HEAD(style_ranges, style_range); struct style { struct grid_cell gc; + int fill; enum style_align align; enum style_list list; -- cgit From 6a489fa7f6360bcd9c0d6b87546d3eb6b0a3654a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Jul 2019 20:09:19 +0000 Subject: Command prompt key presses need to avoid the command queue, GitHub issue 1817. Also a tmux.1 fix from jmc. --- server-client.c | 36 +++++++++++++++++------------------- tmux.1 | 3 +-- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/server-client.c b/server-client.c index a982fe8e..7aeead41 100644 --- a/server-client.c +++ b/server-client.c @@ -1026,16 +1026,6 @@ server_client_key_callback(struct cmdq_item *item, void *data) fatal("gettimeofday failed"); session_update_activity(s, &c->activity_time); - /* Handle status line. */ - if (~c->flags & CLIENT_READONLY) - status_message_clear(c); - if (c->prompt_string != NULL) { - if (c->flags & CLIENT_READONLY) - goto out; - if (status_prompt_key(c, key) == 0) - goto out; - } - /* Check for mouse keys. */ m->valid = 0; if (key == KEYC_MOUSE) { @@ -1216,16 +1206,24 @@ server_client_handle_key(struct client *c, struct key_event *event) return (0); /* - * Key presses in overlay mode are a special case. The queue might be - * blocked so they need to be processed immediately rather than queued. + * Key presses in overlay mode and the command prompt are a special + * case. The queue might be blocked so they need to be processed + * immediately rather than queued. */ - if ((~c->flags & CLIENT_READONLY) && c->overlay_key != NULL) { - switch (c->overlay_key(c, event)) { - case 0: - return (0); - case 1: - server_client_clear_overlay(c); - return (0); + 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: + return (0); + case 1: + server_client_clear_overlay(c); + return (0); + } } } diff --git a/tmux.1 b/tmux.1 index 44ef4a96..02ef1f2c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4337,8 +4337,7 @@ to unset. .Ic align=right .Xc Align text to the left, centre or right of the available space if appropriate. -.It Xo Ic fill=colour -.Xc +.It Ic fill=colour Fill the available space with a background colour if appropriate. .It Xo Ic list=on , .Ic list=focus , -- cgit From 55c694a4679ee225b1962db63671422f3e641fc6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jul 2019 07:52:27 +0000 Subject: Do not use uninitialized buffer name. --- window-copy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index a7cb5dfa..28d728bb 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2700,7 +2700,7 @@ window_copy_append_selection(struct window_mode_entry *wme) struct window_pane *wp = wme->wp; char *buf; struct paste_buffer *pb; - const char *bufdata, *bufname; + const char *bufdata, *bufname = NULL; size_t len, bufsize; struct screen_write_ctx ctx; -- cgit From 3635b3cd6c3343b2ec3f7173facef8a284e7e613 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 6 Jul 2019 20:37:29 +0000 Subject: Correctly clear underscore colour in grid_get_cell1, also fix struct grid_cell to avoid padding. Fixes increased memory use reported by Suraj N Kurapati. --- format-draw.c | 2 +- grid.c | 5 +++-- screen-write.c | 2 +- style.c | 2 +- tmux.h | 6 +++--- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/format-draw.c b/format-draw.c index bb5c65fb..e0ca89f0 100644 --- a/format-draw.c +++ b/format-draw.c @@ -565,7 +565,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, cp++; } - /* Draw the cell to th current screen. */ + /* Draw the cell to the current screen. */ screen_write_cell(&ctx[current], &sy.gc); width[current] += ud->width; continue; diff --git a/grid.c b/grid.c index cddb5871..cbff99e2 100644 --- a/grid.c +++ b/grid.c @@ -37,12 +37,12 @@ /* Default grid cell data. */ const struct grid_cell grid_default_cell = { - 0, 0, 8, 8, 0, { { ' ' }, 0, 1, 1 } + { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 }; /* Cleared grid cell data. */ const struct grid_cell grid_cleared_cell = { - GRID_FLAG_CLEARED, 0, 8, 8, 0, { { ' ' }, 0, 1, 1 } + { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0 }; static const struct grid_cell_entry grid_cleared_entry = { GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } } @@ -475,6 +475,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) gc->bg = gce->data.bg; if (gce->flags & GRID_FLAG_BG256) gc->bg |= COLOUR_FLAG_256; + gc->us = 0; utf8_set(&gc->data, gce->data.data); } diff --git a/screen-write.c b/screen-write.c index 631328a3..943e3044 100644 --- a/screen-write.c +++ b/screen-write.c @@ -36,7 +36,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 = { - GRID_FLAG_PADDING, 0, 8, 8, 0, { { 0 }, 0, 0, 0 } + { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 0, 8, 8 }; struct screen_write_collect_item { diff --git a/style.c b/style.c index f195301f..9f986314 100644 --- a/style.c +++ b/style.c @@ -30,7 +30,7 @@ /* Default style. */ static struct style style_default = { - { 0, 0, 8, 8, 0, { { ' ' }, 0, 1, 1 } }, + { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 }, 8, STYLE_ALIGN_DEFAULT, diff --git a/tmux.h b/tmux.h index e43140b2..0b0d998e 100644 --- a/tmux.h +++ b/tmux.h @@ -596,13 +596,13 @@ enum utf8_state { /* Grid cell data. */ struct grid_cell { - u_char flags; + struct utf8_data data; /* 21 bytes */ u_short attr; + u_char flags; int fg; int bg; int us; - struct utf8_data data; -}; +} __packed; struct grid_cell_entry { u_char flags; union { -- cgit From ddf53d6e4e76463e6d777b2de7304572333935e9 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 6 Jul 2019 20:56:34 +0000 Subject: Correctly adjust mouse position if the status line is at the top and more than one line. GitHub issue 1822. --- cmd-resize-pane.c | 8 ++++---- cmd.c | 4 ++-- server-client.c | 7 ++++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index c978edfb..8d35d96f 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -144,13 +144,13 @@ cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m) w = wl->window; y = m->y + m->oy; x = m->x + m->ox; - if (m->statusat == 0 && y > 0) - y--; + if (m->statusat == 0 && y >= m->statuslines) + y -= m->statuslines; else if (m->statusat > 0 && y >= (u_int)m->statusat) y = m->statusat - 1; ly = m->ly + m->oy; lx = m->lx + m->ox; - if (m->statusat == 0 && ly > 0) - ly--; + if (m->statusat == 0 && ly >= m->statuslines) + ly -= m->statuslines; else if (m->statusat > 0 && ly >= (u_int)m->statusat) ly = m->statusat - 1; diff --git a/cmd.c b/cmd.c index a071dec5..c838c53f 100644 --- a/cmd.c +++ b/cmd.c @@ -597,8 +597,8 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, } log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : ""); - if (m->statusat == 0 && y > 0) - y--; + if (m->statusat == 0 && y >= m->statuslines) + y -= m->statuslines; if (x < wp->xoff || x >= wp->xoff + wp->sx) return (-1); diff --git a/server-client.c b/server-client.c index 7aeead41..3fd16ec5 100644 --- a/server-client.c +++ b/server-client.c @@ -524,9 +524,10 @@ have_event: /* Is this on the status line? */ m->statusat = status_at_line(c); + m->statuslines = status_line_size(c); if (m->statusat != -1 && y >= (u_int)m->statusat && - y < m->statusat + status_line_size(c)) { + y < m->statusat + m->statuslines) { sr = status_get_range(c, x, y - m->statusat); if (sr == NULL) { where = STATUS_DEFAULT; @@ -555,8 +556,8 @@ have_event: /* Not on status line. Adjust position and check for border or pane. */ if (where == NOWHERE) { px = x; - if (m->statusat == 0 && y > 0) - py = y - 1; + if (m->statusat == 0 && y >= m->statuslines) + py = y - m->statuslines; else if (m->statusat > 0 && y >= (u_int)m->statusat) py = m->statusat - 1; else -- cgit From cbe781203f80b0e45cafa44bc3d1635c8d3cf354 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 8 Jul 2019 11:38:14 +0000 Subject: Use the clear history function for the 3J sequence rather than doing it manually. --- screen-write.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/screen-write.c b/screen-write.c index 943e3044..98cdf158 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1169,11 +1169,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) void screen_write_clearhistory(struct screen_write_ctx *ctx) { - struct screen *s = ctx->s; - struct grid *gd = s->grid; - - grid_move_lines(gd, 0, gd->hsize, gd->sy, 8); - gd->hscrolled = gd->hsize = 0; + grid_clear_history(ctx->s->grid); } /* Clear a collected line. */ -- cgit From a4be028b76b8cc9c2066e30db8dc3866b52418ef Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 8 Jul 2019 20:29:11 +0000 Subject: Clear search marks before resize, GitHub issue 1823. --- window-copy.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/window-copy.c b/window-copy.c index 28d728bb..d868631c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -577,6 +577,7 @@ 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 screen_write_ctx ctx; + int search; screen_resize(s, sx, sy, 1); if (data->backing != &wp->base) @@ -589,13 +590,15 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) 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); screen_write_start(&ctx, NULL, s); window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1); screen_write_stop(&ctx); - if (data->searchmark != NULL) + if (search) window_copy_search_marks(wme, NULL); data->searchx = data->cx; data->searchy = data->cy; -- cgit From b74b8be680bb7ca0a6952adafd29d97e85da5c67 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Jul 2019 12:44:47 +0000 Subject: Add j and k for navigation in menus, GitHub issue 1828. --- menu.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/menu.c b/menu.c index cc21c796..da8d89c4 100644 --- a/menu.c +++ b/menu.c @@ -206,8 +206,18 @@ menu_key_cb(struct client *c, struct key_event *event) c->flags |= CLIENT_REDRAWOVERLAY; return (0); } + for (i = 0; i < (u_int)count; i++) { + name = menu->items[i].name; + if (name == NULL || *name == '-') + continue; + if (event->key == menu->items[i].key) { + md->choice = i; + goto chosen; + } + } switch (event->key) { case KEYC_UP: + case 'k': if (old == -1) old = 0; do { @@ -220,6 +230,7 @@ menu_key_cb(struct client *c, struct key_event *event) c->flags |= CLIENT_REDRAWOVERLAY; return (0); case KEYC_DOWN: + case 'j': if (old == -1) old = 0; do { @@ -239,15 +250,6 @@ menu_key_cb(struct client *c, struct key_event *event) case 'q': return (1); } - for (i = 0; i < (u_int)count; i++) { - name = menu->items[i].name; - if (name == NULL || *name == '-') - continue; - if (event->key == menu->items[i].key) { - md->choice = i; - goto chosen; - } - } return (0); chosen: -- cgit From ad11d49d640c911b42c5292b5f5b7dbdffb4908b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Jul 2019 13:19:36 +0000 Subject: Do not leak empty lines, GitHub issue 1824. --- control.c | 1 + 1 file changed, 1 insertion(+) diff --git a/control.c b/control.c index 16fa71bb..c4cf5338 100644 --- a/control.c +++ b/control.c @@ -80,6 +80,7 @@ control_callback(struct client *c, int closed, __unused void *data) if (line == NULL) break; if (*line == '\0') { /* empty line exit */ + free(line); c->flags |= CLIENT_EXIT; break; } -- cgit From fc2016dbb665f01e795a89632a1bb74294bfc4e1 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Jul 2019 14:03:12 +0000 Subject: Add a -H flag to send-keys to send literal keys given as hex numbers (needed for control clients to send mouse sequences). Also add some format flags for UTF-8 and SGR mouse mode. Requested by Bradley Smith in GitHub issues 1832 and 1833. --- arguments.c | 16 +++++++++--- cmd-send-keys.c | 77 ++++++++++++++++++++++++++++++++++----------------------- format.c | 6 +++++ input-keys.c | 7 ++++++ key-string.c | 6 +++++ tmux.1 | 19 ++++++++++---- tmux.h | 3 ++- 7 files changed, 94 insertions(+), 40 deletions(-) diff --git a/arguments.c b/arguments.c index 751d0607..acdaf8aa 100644 --- a/arguments.c +++ b/arguments.c @@ -38,6 +38,7 @@ TAILQ_HEAD(args_values, args_value); struct args_entry { u_char flag; struct args_values values; + u_int count; RB_ENTRY(args_entry) entry; }; @@ -174,6 +175,7 @@ args_print(struct args *args) size_t len; char *buf; int i; + u_int j; struct args_entry *entry; struct args_value *value; @@ -187,7 +189,8 @@ args_print(struct args *args) if (*buf == '\0') args_print_add(&buf, &len, "-"); - args_print_add(&buf, &len, "%c", entry->flag); + for (j = 0; j < entry->count; j++) + args_print_add(&buf, &len, "%c", entry->flag); } /* Then the flags with arguments. */ @@ -244,7 +247,12 @@ args_escape(const char *s) int args_has(struct args *args, u_char ch) { - return (args_find(args, ch) != NULL); + struct args_entry *entry; + + entry = args_find(args, ch); + if (entry == NULL) + return (0); + return (entry->count); } /* Set argument value in the arguments tree. */ @@ -258,9 +266,11 @@ args_set(struct args *args, u_char ch, const char *s) if (entry == NULL) { entry = xcalloc(1, sizeof *entry); entry->flag = ch; + entry->count = 1; TAILQ_INIT(&entry->values); RB_INSERT(args_tree, &args->tree, entry); - } + } else + entry->count++; if (s != NULL) { value = xcalloc(1, sizeof *value); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index fd1c1ab9..9ed50d3d 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_send_keys_entry = { .name = "send-keys", .alias = "send", - .args = { "lXRMN:t:", 0, -1 }, - .usage = "[-lXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...", + .args = { "HlXRMN:t:", 0, -1 }, + .usage = "[-HlXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...", .target = { 't', CMD_FIND_PANE, 0 }, @@ -56,9 +56,9 @@ const struct cmd_entry cmd_send_prefix_entry = { }; static struct cmdq_item * -cmd_send_keys_inject(struct client *c, struct cmd_find_state *fs, - struct cmdq_item *item, key_code key) +cmd_send_keys_inject_key(struct client *c, struct cmdq_item *item, key_code key) { + struct cmd_find_state *fs = &item->target; struct window_mode_entry *wme; struct key_table *table; struct key_binding *bd; @@ -81,6 +81,44 @@ cmd_send_keys_inject(struct client *c, struct cmd_find_state *fs, return (item); } +static struct cmdq_item * +cmd_send_keys_inject_string(struct client *c, struct cmdq_item *item, + struct args *args, int i) +{ + const char *s = args->argv[i]; + struct utf8_data *ud, *uc; + wchar_t wc; + key_code key; + char *endptr; + long n; + int literal; + + if (args_has(args, 'H')) { + n = strtol(s, &endptr, 16); + if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') + return (item); + return (cmd_send_keys_inject_key(c, item, KEYC_LITERAL|n)); + } + + literal = args_has(args, 'l'); + if (!literal) { + key = key_string_lookup_string(s); + if (key != KEYC_NONE && key != KEYC_UNKNOWN) + return (cmd_send_keys_inject_key(c, item, key)); + literal = 1; + } + if (literal) { + ud = utf8_fromcstr(s); + for (uc = ud; uc->size != 0; uc++) { + if (utf8_combine(uc, &wc) != UTF8_DONE) + continue; + item = cmd_send_keys_inject_key(c, item, wc); + } + free(ud); + } + return (item); +} + static enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) { @@ -90,11 +128,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct mouse_event *m = &item->shared->mouse; - struct cmd_find_state *fs = &item->target; struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); - struct utf8_data *ud, *uc; - wchar_t wc; - int i, literal; + int i; key_code key; u_int np = 1; char *cause = NULL; @@ -141,7 +176,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(c, fs, item, key); + cmd_send_keys_inject_key(c, item, key); return (CMD_RETURN_NORMAL); } @@ -151,28 +186,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) } for (; np != 0; np--) { - for (i = 0; i < args->argc; i++) { - literal = args_has(args, 'l'); - if (!literal) { - key = key_string_lookup_string(args->argv[i]); - if (key != KEYC_NONE && key != KEYC_UNKNOWN) { - item = cmd_send_keys_inject(c, fs, item, - key); - } else - literal = 1; - } - if (literal) { - ud = utf8_fromcstr(args->argv[i]); - for (uc = ud; uc->size != 0; uc++) { - if (utf8_combine(uc, &wc) != UTF8_DONE) - continue; - item = cmd_send_keys_inject(c, fs, item, - wc); - } - free(ud); - } - } - + for (i = 0; i < args->argc; i++) + item = cmd_send_keys_inject_string(c, item, args, i); } return (CMD_RETURN_NORMAL); diff --git a/format.c b/format.c index cb67405f..df569bc2 100644 --- a/format.c +++ b/format.c @@ -2300,6 +2300,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) !!(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)); @@ -2309,6 +2311,10 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) !!(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); } diff --git a/input-keys.c b/input-keys.c index 39401a38..6e521ae1 100644 --- a/input-keys.c +++ b/input-keys.c @@ -173,6 +173,13 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) return; } + /* Literal keys go as themselves (can't be more than eight bits). */ + if (key & KEYC_LITERAL) { + ud.data[0] = (u_char)key; + bufferevent_write(wp->event, &ud.data[0], 1); + return; + } + /* * 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. diff --git a/key-string.c b/key-string.c index 8442727d..a1ef4f51 100644 --- a/key-string.c +++ b/key-string.c @@ -284,6 +284,12 @@ key_string_lookup_key(key_code key) return (out); } + /* Literal keys are themselves. */ + if (key & KEYC_LITERAL) { + snprintf(out, sizeof out, "%c", (int)(key & 0xff)); + 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 diff --git a/tmux.1 b/tmux.1 index 02ef1f2c..810cb211 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2622,7 +2622,7 @@ With only .Ar key-table . .It Xo Ic send-keys -.Op Fl lMRX +.Op Fl HlMRX .Op Fl N Ar repeat-count .Op Fl t Ar target-pane .Ar key Ar ... @@ -2637,10 +2637,16 @@ or .Ql NPage ) 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. +.Pp The .Fl l -flag disables key name lookup and sends the keys literally. -All arguments are sent sequentially from first to last. +flag disables key name lookup and processes the keys as literal UTF-8 +characters. +The +.Fl H +flag expects each key to be a hexadecimal number for an ASCII character. +.Pp The .Fl R flag causes the terminal state to be reset. @@ -4180,11 +4186,14 @@ The following variables are available, where appropriate: .It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag" .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" .It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" +.It Li "mouse_line" Ta "" Ta "Line under mouse, if any" +.It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag" .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" +.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" +.It Li "mouse_word" Ta "" Ta "Word under mouse, if any" .It Li "mouse_x" Ta "" Ta "Mouse X position, if any" .It Li "mouse_y" Ta "" Ta "Mouse Y position, if any" -.It Li "mouse_word" Ta "" Ta "Word under mouse, if any" -.It Li "mouse_line" Ta "" Ta "Line under mouse, if any" +.It Li "origin_flag" Ta "" Ta "Pane origin flag" .It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window" .It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window" diff --git a/tmux.h b/tmux.h index 0b0d998e..fdb0a7c7 100644 --- a/tmux.h +++ b/tmux.h @@ -111,9 +111,10 @@ struct winlink; #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) +#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM|KEYC_LITERAL) #define KEYC_MASK_KEY (~KEYC_MASK_MOD) /* Is this a mouse key? */ -- cgit From f4d858e7a0888cfd1d09c421f71729e833322851 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 10 Jul 2019 11:20:10 +0000 Subject: Add -F to refresh-client to specify flags for control clients - one flag at the moment, no-output which turns off forwarding pane output. From Thomas Adam. GitHub issue 1834. --- cmd-refresh-client.c | 62 +++++++++++++++++++++++++++++++++------------------- control-notify.c | 3 +++ tmux.1 | 11 ++++++++-- tmux.h | 1 + 4 files changed, 53 insertions(+), 24 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index e5ae099f..49921a74 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -19,6 +19,7 @@ #include #include +#include #include "tmux.h" @@ -33,8 +34,9 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "cC:DlLRSt:U", 0, 1 }, - .usage = "[-cDlLRSU] [-C size] " CMD_TARGET_CLIENT_USAGE " [adjustment]", + .args = { "cC:DF:lLRSt:U", 0, 1 }, + .usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE + " [adjustment]", .flags = CMD_AFTERHOOK, .exec = cmd_refresh_client_exec @@ -48,6 +50,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) struct tty *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) @@ -107,28 +110,43 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'l')) { if (c->session != NULL) tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?"); - } else if (args_has(args, 'C')) { - if ((size = args_get(args, 'C')) == NULL) { - cmdq_error(item, "missing size"); - return (CMD_RETURN_ERROR); - } - if (sscanf(size, "%u,%u", &x, &y) != 2 && - sscanf(size, "%ux%u", &x, &y)) { - 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); + return (CMD_RETURN_NORMAL); + } + + if (args_has(args, 'C') || args_has(args, 'F')) { + if (args_has(args, 'C')) { + if (!(c->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(&c->tty, x, y); + c->flags |= CLIENT_SIZECHANGED; + recalculate_sizes(); } - if (!(c->flags & CLIENT_CONTROL)) { - cmdq_error(item, "not a control client"); - return (CMD_RETURN_ERROR); + if (args_has(args, 'F')) { + if (!(c->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) + c->flags |= CLIENT_CONTROL_NOOUTPUT; + } + free(copy); } - tty_set_size(&c->tty, x, y); - c->flags |= CLIENT_SIZECHANGED; - recalculate_sizes(); return (CMD_RETURN_NORMAL); } diff --git a/control-notify.c b/control-notify.c index 340dab73..a1e2b7bf 100644 --- a/control-notify.c +++ b/control-notify.c @@ -36,6 +36,9 @@ control_notify_input(struct client *c, struct window_pane *wp, 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. diff --git a/tmux.1 b/tmux.1 index 810cb211..85d69bf1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1162,7 +1162,8 @@ is used, the option will not be applied. .It Xo Ic refresh-client .Op Fl cDlLRSU -.Op Fl C Ar width,height +.Op Fl C Ar XxY +.Op Fl F Ar flags .Op Fl t Ar target-client .Op Ar adjustment .Xc @@ -1205,7 +1206,13 @@ 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 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. +.Pp .Fl l requests the clipboard from the client using the .Xr xterm 1 diff --git a/tmux.h b/tmux.h index fdb0a7c7..b09b52a8 100644 --- a/tmux.h +++ b/tmux.h @@ -1516,6 +1516,7 @@ struct client { #define CLIENT_STATUSOFF 0x800000 #define CLIENT_REDRAWSTATUSALWAYS 0x1000000 #define CLIENT_REDRAWOVERLAY 0x2000000 +#define CLIENT_CONTROL_NOOUTPUT 0x4000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ -- cgit From 91b6145499c3aa5591fd2d7ed20bd69b15c80d34 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 10 Jul 2019 14:33:24 +0000 Subject: The command item changes so can't keep getting the target out of it, need to use the one from the first item. Fixes crash reported by M Kelly. --- cmd-send-keys.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 9ed50d3d..fe202335 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -56,9 +56,9 @@ const struct cmd_entry cmd_send_prefix_entry = { }; static struct cmdq_item * -cmd_send_keys_inject_key(struct client *c, struct cmdq_item *item, key_code key) +cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, + struct cmdq_item *item, key_code key) { - struct cmd_find_state *fs = &item->target; struct window_mode_entry *wme; struct key_table *table; struct key_binding *bd; @@ -82,8 +82,8 @@ cmd_send_keys_inject_key(struct client *c, struct cmdq_item *item, key_code key) } static struct cmdq_item * -cmd_send_keys_inject_string(struct client *c, struct cmdq_item *item, - struct args *args, int i) +cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, + struct cmdq_item *item, struct args *args, int i) { const char *s = args->argv[i]; struct utf8_data *ud, *uc; @@ -97,14 +97,14 @@ cmd_send_keys_inject_string(struct client *c, struct cmdq_item *item, n = strtol(s, &endptr, 16); if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') return (item); - return (cmd_send_keys_inject_key(c, item, KEYC_LITERAL|n)); + return (cmd_send_keys_inject_key(c, fs, item, KEYC_LITERAL|n)); } literal = args_has(args, 'l'); if (!literal) { key = key_string_lookup_string(s); if (key != KEYC_NONE && key != KEYC_UNKNOWN) - return (cmd_send_keys_inject_key(c, item, key)); + return (cmd_send_keys_inject_key(c, fs, item, key)); literal = 1; } if (literal) { @@ -112,8 +112,8 @@ cmd_send_keys_inject_string(struct client *c, struct cmdq_item *item, for (uc = ud; uc->size != 0; uc++) { if (utf8_combine(uc, &wc) != UTF8_DONE) continue; - item = cmd_send_keys_inject_key(c, item, wc); - } + item = cmd_send_keys_inject_key(c, fs, item, wc); + } free(ud); } return (item); @@ -124,6 +124,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; 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; @@ -176,7 +177,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, item, key); + cmd_send_keys_inject_key(c, fs, item, key); return (CMD_RETURN_NORMAL); } @@ -187,7 +188,7 @@ 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, item, args, i); + item = cmd_send_keys_inject_string(c, fs, item, args, i); } return (CMD_RETURN_NORMAL); -- cgit From 6ceeceab7adfb7b6a779df52f811f010aae0721c Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Jul 2019 18:25:07 +0000 Subject: Make layout_fix_offsets take a window like layout_fix_panes. --- layout-custom.c | 2 +- layout-set.c | 8 ++++---- layout.c | 30 +++++++++++++++++++++--------- tmux.h | 2 +- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/layout-custom.c b/layout-custom.c index bedcaf10..d759c206 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -165,7 +165,7 @@ layout_parse(struct window *w, const char *layout) layout_assign(&wp, lc); /* Update pane offsets and sizes. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); recalculate_sizes(); diff --git a/layout-set.c b/layout-set.c index 12b4780f..82247149 100644 --- a/layout-set.c +++ b/layout-set.c @@ -158,7 +158,7 @@ layout_set_even(struct window *w, enum layout_type type) layout_spread_cell(w, lc); /* Fix cell offsets. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -257,7 +257,7 @@ layout_set_main_h(struct window *w) } /* Fix cell offsets. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -344,7 +344,7 @@ layout_set_main_v(struct window *w) } /* Fix cell offsets. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -453,7 +453,7 @@ layout_set_tiled(struct window *w) } /* Fix cell offsets. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); diff --git a/layout.c b/layout.c index e84da936..f6bba34f 100644 --- a/layout.c +++ b/layout.c @@ -200,8 +200,8 @@ layout_make_node(struct layout_cell *lc, enum layout_type type) } /* Fix cell offsets based on their sizes. */ -void -layout_fix_offsets(struct layout_cell *lc) +static void +layout_fix_offsets1(struct layout_cell *lc) { struct layout_cell *lcchild; u_int xoff, yoff; @@ -212,7 +212,7 @@ layout_fix_offsets(struct layout_cell *lc) lcchild->xoff = xoff; lcchild->yoff = lc->yoff; if (lcchild->type != LAYOUT_WINDOWPANE) - layout_fix_offsets(lcchild); + layout_fix_offsets1(lcchild); xoff += lcchild->sx + 1; } } else { @@ -221,12 +221,24 @@ layout_fix_offsets(struct layout_cell *lc) lcchild->xoff = lc->xoff; lcchild->yoff = yoff; if (lcchild->type != LAYOUT_WINDOWPANE) - layout_fix_offsets(lcchild); + layout_fix_offsets1(lcchild); yoff += lcchild->sy + 1; } } } +/* Update cell offsets based on their sizes. */ +void +layout_fix_offsets(struct window *w) +{ + struct layout_cell *lc = w->layout_root; + + lc->xoff = 0; + lc->yoff = 0; + + layout_fix_offsets1(lc); +} + /* * Returns 1 if we need to reserve space for the pane status line. This is the * case for the most upper panes only. @@ -507,7 +519,7 @@ layout_resize(struct window *w, u_int sx, u_int sy) layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange); /* Fix cell offsets. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); } @@ -567,7 +579,7 @@ layout_resize_layout(struct window *w, struct layout_cell *lc, } /* Fix cell offsets. */ - layout_fix_offsets(w->layout_root); + layout_fix_offsets(w); layout_fix_panes(w); notify_window("window-layout-changed", w); } @@ -988,7 +1000,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, if (full_size) { if (!resize_first) layout_resize_child_cells(wp->window, lc); - layout_fix_offsets(wp->window->layout_root); + layout_fix_offsets(wp->window); } else layout_make_leaf(lc, wp); @@ -1006,7 +1018,7 @@ layout_close_pane(struct window_pane *wp) /* Fix pane offsets and sizes. */ if (w->layout_root != NULL) { - layout_fix_offsets(w->layout_root); + layout_fix_offsets(w); layout_fix_panes(w); } notify_window("window-layout-changed", w); @@ -1073,7 +1085,7 @@ layout_spread_out(struct window_pane *wp) do { if (layout_spread_cell(w, parent)) { - layout_fix_offsets(parent); + layout_fix_offsets(w); layout_fix_panes(w); break; } diff --git a/tmux.h b/tmux.h index b09b52a8..076a5fe6 100644 --- a/tmux.h +++ b/tmux.h @@ -2438,7 +2438,7 @@ void layout_set_size(struct layout_cell *, u_int, 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 layout_cell *); +void layout_fix_offsets(struct window *); void layout_fix_panes(struct window *); void layout_resize_adjust(struct window *, struct layout_cell *, enum layout_type, int); -- cgit From eac055bfaf1b21b71d5fc79bc29a2810aa20085f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Jul 2019 18:43:32 +0000 Subject: Simplify code to work out if an extra line is needed in the cell. --- layout.c | 105 ++++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 41 deletions(-) diff --git a/layout.c b/layout.c index f6bba34f..37214d02 100644 --- a/layout.c +++ b/layout.c @@ -39,7 +39,6 @@ static int layout_resize_pane_grow(struct window *, struct layout_cell *, enum layout_type, int, int); static int layout_resize_pane_shrink(struct window *, struct layout_cell *, enum layout_type, int); -static int layout_need_status(struct layout_cell *, int); static u_int layout_new_pane_size(struct window *, u_int, struct layout_cell *, enum layout_type, u_int, u_int, u_int); @@ -199,7 +198,7 @@ layout_make_node(struct layout_cell *lc, enum layout_type type) lc->wp = NULL; } -/* Fix cell offsets based on their sizes. */ +/* Fix cell offsets for a child cell. */ static void layout_fix_offsets1(struct layout_cell *lc) { @@ -239,55 +238,74 @@ layout_fix_offsets(struct window *w) layout_fix_offsets1(lc); } -/* - * Returns 1 if we need to reserve space for the pane status line. This is the - * case for the most upper panes only. - */ +/* Is this a top cell? */ static int -layout_need_status(struct layout_cell *lc, int status) +layout_cell_is_top(struct window *w, struct layout_cell *lc) { struct layout_cell *next; - if (lc->parent != NULL) { - if (lc->parent->type == LAYOUT_LEFTRIGHT) - return (layout_need_status(lc->parent, status)); + while (lc != w->layout_root) { + next = lc->parent; + if (next->type == LAYOUT_TOPBOTTOM && + lc != TAILQ_FIRST(&next->cells)) + return (0); + lc = next; + } + return (1); +} - if (status == PANE_STATUS_TOP) - next = TAILQ_FIRST(&lc->parent->cells); - else - next = TAILQ_LAST(&lc->parent->cells,layout_cells); - if (lc == next) - return (layout_need_status(lc->parent, status)); - return (0); +/* Is this a bottom cell? */ +static int +layout_cell_is_bottom(struct window *w, struct layout_cell *lc) +{ + struct layout_cell *next; + + while (lc != w->layout_root) { + next = lc->parent; + if (next->type == LAYOUT_TOPBOTTOM && + lc != TAILQ_LAST(&next->cells, layout_cells)) + return (0); + lc = next; } return (1); } +/* + * Returns 1 if we need to add an extra line for the pane status line. This is + * the case for the most upper or lower panes only. + */ +static int +layout_add_border(struct window *w, struct layout_cell *lc, int status) +{ + if (status == PANE_STATUS_TOP) + return (layout_cell_is_top(w, lc)); + if (status == PANE_STATUS_BOTTOM) + return (layout_cell_is_bottom(w, lc)); + return (0); +} + /* Update pane offsets and sizes based on their cells. */ void layout_fix_panes(struct window *w) { struct window_pane *wp; struct layout_cell *lc; - int shift, status; + int status; status = options_get_number(w->options, "pane-border-status"); TAILQ_FOREACH(wp, &w->panes, entry) { if ((lc = wp->layout_cell) == NULL) continue; - if (status != PANE_STATUS_OFF) - shift = layout_need_status(lc, status); - else - shift = 0; - wp->xoff = lc->xoff; wp->yoff = lc->yoff; - if (shift && status == PANE_STATUS_TOP) - wp->yoff += 1; - - window_pane_resize(wp, lc->sx, lc->sy - shift); + if (layout_add_border(w, lc, status)) { + if (status == PANE_STATUS_TOP) + wp->yoff++; + window_pane_resize(wp, lc->sx, lc->sy - 1); + } else + window_pane_resize(wp, lc->sx, lc->sy); } } @@ -324,13 +342,15 @@ layout_resize_check(struct window *w, struct layout_cell *lc, status = options_get_number(w->options, "pane-border-status"); if (lc->type == LAYOUT_WINDOWPANE) { /* Space available in this cell only. */ - minimum = PANE_MINIMUM; - if (type == LAYOUT_LEFTRIGHT) + if (type == LAYOUT_LEFTRIGHT) { available = lc->sx; - else { + minimum = PANE_MINIMUM; + } else { available = lc->sy; - if (status != PANE_STATUS_OFF) - minimum += layout_need_status(lc, status); + if (layout_add_border(w, lc, status)) + minimum = PANE_MINIMUM + 1; + else + minimum = PANE_MINIMUM; } if (available > minimum) available -= minimum; @@ -873,9 +893,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, return (NULL); break; case LAYOUT_TOPBOTTOM: - minimum = PANE_MINIMUM * 2 + 1; - if (status != PANE_STATUS_OFF) - minimum += layout_need_status(lc, status); + if (layout_add_border(wp->window, lc, status)) + minimum = PANE_MINIMUM * 2 + 2; + else + minimum = PANE_MINIMUM * 2 + 1; if (sy < minimum) return (NULL); break; @@ -1041,9 +1062,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) if (parent->type == LAYOUT_LEFTRIGHT) size = parent->sx; else if (parent->type == LAYOUT_TOPBOTTOM) { - size = parent->sy; - if (status != PANE_STATUS_OFF) - size -= layout_need_status(parent, status); + if (layout_add_border(w, parent, status)) + size = parent->sy - 1; + else + size = parent->sy; } else return (0); if (size < number - 1) @@ -1061,9 +1083,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) change = each - (int)lc->sx; layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change); } else if (parent->type == LAYOUT_TOPBOTTOM) { - this = each; - if (status != PANE_STATUS_OFF) - this += layout_need_status(lc, status); + if (layout_add_border(w, lc, status)) + this = each + 1; + else + this = each; change = this - (int)lc->sy; layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change); } -- cgit From b89f2f28bb5f21bfe836870d907ec6992acb5f28 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jul 2019 10:30:56 +0000 Subject: Fix grid clear code to correctly clear with the default background colour rather than ending up with the used count higher than the total size, GitHub issue 1829. --- grid-view.c | 1 - grid.c | 28 ++++++++++++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/grid-view.c b/grid-view.c index a4bd5ba2..fa3bfbf6 100644 --- a/grid-view.c +++ b/grid-view.c @@ -214,7 +214,6 @@ grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx, u_int bg) sx = grid_view_x(gd, gd->sx); grid_move_cells(gd, px, px + nx, py, sx - px - nx, bg); - grid_clear(gd, sx - nx, py, px + nx - (sx - nx), 1, bg); } /* Convert cells into a string. */ diff --git a/grid.c b/grid.c index cbff99e2..f33eef72 100644 --- a/grid.c +++ b/grid.c @@ -547,7 +547,7 @@ void grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) { struct grid_line *gl; - u_int xx, yy; + u_int xx, yy, ox, sx; if (nx == 0 || ny == 0) return; @@ -564,16 +564,20 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) for (yy = py; yy < py + ny; yy++) { gl = &gd->linedata[yy]; - if (px + nx >= gd->sx && px < gl->cellused) - gl->cellused = px; - if (px > gl->cellsize && COLOUR_DEFAULT(bg)) - continue; - if (px + nx >= gl->cellsize && COLOUR_DEFAULT(bg)) { - gl->cellsize = px; - continue; + + sx = gd->sx; + if (sx > gl->cellsize) + sx = gl->cellsize; + ox = nx; + if (COLOUR_DEFAULT(bg)) { + if (px > sx) + continue; + if (px + nx > sx) + ox = sx - px; } - grid_expand_line(gd, yy, px + nx, 8); /* default bg first */ - for (xx = px; xx < px + nx; xx++) + + grid_expand_line(gd, yy, px + ox, 8); /* default bg first */ + for (xx = px; xx < px + ox; xx++) grid_clear_cell(gd, xx, yy, bg); } } @@ -1216,6 +1220,10 @@ grid_reflow(struct grid *gd, u_int sx) struct grid_cell gc; u_int yy, width, i, at, first; + /* Do not reflow to the same size. */ + if (sx == gd->sx) + return; + /* * Create a destination grid. This is just used as a container for the * line data and may not be fully valid. -- cgit From 99852f8401477e90e0887f1f2964fe6bdca86a57 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jul 2019 14:11:52 +0000 Subject: Fix check for wrapping when redrawing entire lines, GitHub issue 1836. --- tty.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tty.c b/tty.c index c3e2f0f6..ab1da9fd 100644 --- a/tty.c +++ b/tty.c @@ -1217,7 +1217,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, const struct grid_cell *gcp; struct grid_line *gl; u_int i, j, ux, sx, width; - int flags, cleared = 0; + int flags, cleared = 0, wrapped = 0; char buf[512]; size_t len; u_int cellsize; @@ -1274,8 +1274,10 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, tty_putcode(tty, TTYC_EL1); cleared = 1; } - } else + } else { log_debug("%s: wrapped line %u", __func__, aty); + wrapped = 1; + } memcpy(&last, &grid_default_cell, sizeof last); len = 0; @@ -1299,13 +1301,15 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, tty_clear_line(tty, wp, aty, atx + ux, width, last.bg); } else { - tty_cursor(tty, atx + ux, aty); + if (!wrapped || atx != 0 || ux != 0) + tty_cursor(tty, atx + ux, aty); tty_putn(tty, buf, len, width); } ux += width; len = 0; width = 0; + wrapped = 0; } if (gcp->flags & GRID_FLAG_SELECTED) @@ -1339,7 +1343,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, log_debug("%s: %zu cleared (end)", __func__, len); tty_clear_line(tty, wp, aty, atx + ux, width, last.bg); } else { - tty_cursor(tty, atx + ux, aty); + if (!wrapped || atx != 0 || ux != 0) + tty_cursor(tty, atx + ux, aty); tty_putn(tty, buf, len, width); } ux += width; -- cgit From 80b82c8d1484ce02fea49d53daf20c1bf4260c83 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 17 Jul 2019 08:54:46 +0100 Subject: Mention dependencies earlier. --- .github/README.md | 8 +++----- README | 7 +++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/README.md b/.github/README.md index ce12f6aa..50d08c0c 100644 --- a/.github/README.md +++ b/.github/README.md @@ -30,7 +30,9 @@ configure with `--enable-utempter` to enable this. ### From version control -To get and build the latest from version control: +To get and build the latest from version control - note that this requires at +least a working C compiler, `make`, `autoconf`, `automake`, `pkg-config` as +well as `libevent` and `ncurses` libraries and headers: ~~~bash git clone https://github.com/tmux/tmux.git @@ -39,10 +41,6 @@ sh autogen.sh ./configure && make ~~~ -(Note that this requires at least a working C compiler, `make`, `autoconf`, -`automake`, `pkg-config` as well as `libevent` and `ncurses` libraries and -headers.) - ## Contributing Bug reports, feature suggestions and especially code contributions are most diff --git a/README b/README index 8a9070b8..da8f508f 100644 --- a/README +++ b/README @@ -26,16 +26,15 @@ To build and install tmux from a release tarball, use: tmux can use the utempter library to update utmp(5), if it is installed - run configure with --enable-utempter to enable this. -To get and build the latest from version control: +To get and build the latest from version control - note that this requires at +least a working C compiler, make, autoconf, automake, pkg-config as well as +libevent and ncurses libraries and headers: $ git clone https://github.com/tmux/tmux.git $ cd tmux $ sh autogen.sh $ ./configure && make -(Note that this requires at least a working C compiler, make, autoconf, -automake, pkg-config as well as libevent and ncurses libraries and headers.) - * Contributing Bug reports, feature suggestions and especially code contributions are most -- cgit From 7c78ebce45484716e6e02292d7d107e2b15cdbcb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 17 Jul 2019 08:57:22 +0100 Subject: cc, make, libevent, ncurses are dependencies for all builds so no need to list them for VCS build. --- .github/README.md | 5 ++--- README | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/README.md b/.github/README.md index 50d08c0c..2b299cc5 100644 --- a/.github/README.md +++ b/.github/README.md @@ -30,9 +30,8 @@ configure with `--enable-utempter` to enable this. ### From version control -To get and build the latest from version control - note that this requires at -least a working C compiler, `make`, `autoconf`, `automake`, `pkg-config` as -well as `libevent` and `ncurses` libraries and headers: +To get and build the latest from version control - note that this requires +`autoconf`, `automake` and `pkg-config`: ~~~bash git clone https://github.com/tmux/tmux.git diff --git a/README b/README index da8f508f..4f577060 100644 --- a/README +++ b/README @@ -26,9 +26,8 @@ To build and install tmux from a release tarball, use: tmux can use the utempter library to update utmp(5), if it is installed - run configure with --enable-utempter to enable this. -To get and build the latest from version control - note that this requires at -least a working C compiler, make, autoconf, automake, pkg-config as well as -libevent and ncurses libraries and headers: +To get and build the latest from version control - note that this requires +autoconf, automake and pkg-config: $ git clone https://github.com/tmux/tmux.git $ cd tmux -- cgit From 9e7774bb966f6f90270df8a0d16efb2b2a5ba860 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Jul 2019 17:46:51 +0000 Subject: Clear overlay on normal key press. --- server-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-client.c b/server-client.c index 3fd16ec5..d5d88717 100644 --- a/server-client.c +++ b/server-client.c @@ -1226,6 +1226,7 @@ server_client_handle_key(struct client *c, struct key_event *event) return (0); } } + server_client_clear_overlay(c); } /* -- cgit From f6a9f6b4ad139f46426ff308669b49a8dc25592e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Jul 2019 17:49:23 +0000 Subject: Use the right client for if -b. --- cmd-if-shell.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index a992602c..2befbc0c 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -121,7 +121,10 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->cmd_else = NULL; memcpy(&cdata->mouse, m, sizeof cdata->mouse); - cdata->client = item->client; + if (!args_has(args, 'b')) + cdata->client = item->client; + else + cdata->client = c; if (cdata->client != NULL) cdata->client->references++; -- cgit From df7b5292abd220fc7ac8e6f0d1945aa08143fef1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 19 Jul 2019 07:18:03 +0000 Subject: More man page fixes from lacygoill at lacygoill dot me, GitHub issue 1805. --- tmux.1 | 58 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/tmux.1 b/tmux.1 index 85d69bf1..f78c9106 100644 --- a/tmux.1 +++ b/tmux.1 @@ -609,7 +609,7 @@ Most commands accept the optional .Fl s ) argument with one of .Ar target-client , -.Ar target-session +.Ar target-session , .Ar target-window , or .Ar target-pane . @@ -776,7 +776,7 @@ may consist entirely of the token .Ql {mouse} (alternative form .Ql = ) -to specify the most recent mouse event +to specify the session, window or pane where the most recent mouse event occurred (see the .Sx MOUSE SUPPORT section) @@ -1361,6 +1361,9 @@ It is also entered when a command that produces output, such as .Ic list-keys , is executed from a key binding. .Pp +In copy mode an indicator is displayed in the top-right corner of the pane with +the current position and the number of lines in the history. +.Pp Commands are sent to copy mode using the .Fl X flag to the @@ -1414,6 +1417,7 @@ The following commands are supported in copy mode: .It Li "jump-to-backward " Ta "T" Ta "" .It Li "jump-to-forward " Ta "t" Ta "" .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-}" .It Li "next-space" Ta "W" Ta "" .It Li "next-space-end" Ta "E" Ta "" @@ -1423,6 +1427,7 @@ 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 "previous-matching-bracket" Ta "" Ta "M-C-b" .It Li "previous-paragraph" Ta "{" Ta "M-{" .It Li "previous-space" Ta "B" Ta "" .It Li "previous-word" Ta "b" Ta "M-b" @@ -3017,6 +3022,18 @@ for all terminal types matching The terminal entry value is passed through .Xr strunvis 3 before interpretation. +.It Ic user-keys[] Ar key +Set list of user-defined key escape sequences. +Each item is associated with a key named +.Ql User0 , +.Ql User1 , +and so on. +.Pp +For example: +.Bd -literal -offset indent +set -s user-keys[0] "\ee[5;30012~" +bind User0 resize-pane -L 3 +.Ed .El .Pp Available session options are: @@ -3223,7 +3240,7 @@ the terminal appears to be .Xr xterm 1 . This option is off by default. .It Ic set-titles-string Ar string -String used to set the window title if +String used to set the client terminal title if .Ic set-titles is on. Formats are expanded, see the @@ -3357,18 +3374,6 @@ removed from the session environment (as if was given to the .Ic set-environment command). -.It Ic user-keys[] Ar key -Set list of user-defined key escape sequences. -Each item is associated with a key named -.Ql User0 , -.Ql User1 , -and so on. -.Pp -For example: -.Bd -literal -offset indent -set -s user-keys[0] "\ee[5;30012~" -bind User0 resize-pane -L 3 -.Ed .It Xo Ic visual-activity .Op Ic on | off | both .Xc @@ -3915,7 +3920,7 @@ flag with a .Ar format argument. This is a string which controls the output format of the command. -Replacement variables are enclosed in +Format variables are enclosed in .Ql #{ and .Ql } , @@ -3974,7 +3979,7 @@ For example: #{?pane_in_mode,#[fg=white#,bg=red],#[fg=red#,bg=white]}#W . .Ed .Pp -Comparisons may be expressed by prefixing two comma-separated +String comparisons may be expressed by prefixing two comma-separated alternatives by .Ql == , .Ql != , @@ -4142,7 +4147,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 "alternate_on" Ta "" Ta "If pane is in alternate screen" +.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" .It Li "buffer_created" Ta "" Ta "Time buffer created" @@ -4213,19 +4218,19 @@ The following variables are available, where appropriate: .It Li "pane_format" Ta "" Ta "1 if format is for a pane (not assuming the current)" .It Li "pane_height" Ta "" Ta "Height of pane" .It Li "pane_id" Ta "#D" Ta "Unique pane ID" -.It Li "pane_in_mode" Ta "" Ta "If pane is in a mode" +.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 "If input to pane is disabled" +.It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled" .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" -.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any." +.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any" .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" .It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" .It Li "pane_start_command" Ta "" Ta "Command pane started with" -.It Li "pane_synchronized" Ta "" Ta "If pane is synchronized" +.It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" .It Li "pane_title" Ta "#T" Ta "Title of pane" .It Li "pane_top" Ta "" Ta "Top of pane" @@ -4590,7 +4595,7 @@ session option. Commands related to the status line are as follows: .Bl -tag -width Ds .It Xo Ic command-prompt -.Op Fl 1i +.Op Fl 1Ni .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client @@ -4640,6 +4645,8 @@ but any quotation marks are escaped. .Fl 1 makes the prompt only accept one key press, in this case the resulting input is a single character. +.Fl N +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. @@ -4651,7 +4658,7 @@ 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 "Delete current word" Ta "" Ta "C-w" +.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" .It Li "Execute command" Ta "Enter" Ta "Enter" @@ -4769,8 +4776,7 @@ section; information is taken from .Ar target-pane if .Fl t -is given, otherwise the active pane for the session attached to -.Ar target-client . +is given, otherwise the active pane. .Pp .Fl v prints verbose logging as the format is parsed and -- cgit From bf6d1aeaa44775923a72a4da49def96268dfa304 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 19 Jul 2019 07:20:51 +0000 Subject: Add a few vi(1) key aliases, from D Ben Knoble. --- mode-tree.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mode-tree.c b/mode-tree.c index 20ac3226..03a91ef8 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -933,6 +933,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, case '\016': /* C-n */ mode_tree_down(mtd, 1); break; + case 'g': case KEYC_PPAGE: case '\002': /* C-b */ for (i = 0; i < mtd->height; i++) { @@ -941,6 +942,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_up(mtd, 1); } break; + case 'G': case KEYC_NPAGE: case '\006': /* C-f */ for (i = 0; i < mtd->height; i++) { @@ -1019,6 +1021,8 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_build(mtd); } break; + case '?': + case '/': case '\023': /* C-s */ mtd->references++; status_prompt_set(c, "(search) ", "", -- cgit From 90dba3ec66b712a9a12f7a61fee89d9647c726f6 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 24 Jul 2019 21:16:17 +0000 Subject: Mark pane status line jobs with FORMAT_STATUS also so it redraws when they finish, GitHub issue 1852. --- screen-redraw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen-redraw.c b/screen-redraw.c index 2943e73b..e7f4f077 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -266,7 +266,7 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, fmt = options_get_string(w->options, "pane-border-format"); - ft = format_create(c, NULL, FORMAT_PANE|wp->id, 0); + ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); format_defaults(ft, c, NULL, NULL, wp); expanded = format_expand_time(ft, fmt); -- cgit From 8f40f791d9d45fa70ca5d388eba0f4c2a4cf397b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 26 Jul 2019 20:08:40 +0000 Subject: Change "lost server" message to "server exited unexpectedly", from Neal McBurnett in GitHub issue 1857. --- client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.c b/client.c index d071b1db..a593d9ac 100644 --- a/client.c +++ b/client.c @@ -202,7 +202,7 @@ client_exit_message(void) case CLIENT_EXIT_TERMINATED: return ("terminated"); case CLIENT_EXIT_LOST_SERVER: - return ("lost server"); + return ("server exited unexpectedly"); case CLIENT_EXIT_EXITED: return ("exited"); case CLIENT_EXIT_SERVER_EXITED: -- cgit From e19622b8db143545f02b92da674de7f71f8a907a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 26 Jul 2019 21:22:53 +0100 Subject: Add to CHANGES. --- CHANGES | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 95c8b619..952a95a6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,23 @@ CHANGES FROM 3.0 to X.X +* Expand arguments to C and s format modifiers to match the m modifier. + +* Add support for underscore colours (Setulc capability must be added with + terminal-overrides as described in tmux(1)). + +* Add a "fill" style attribute for the fill colour of the drawing area (where + appropriate). + +* New -H flag to send-keys to send literal keys. + +* Format variables for pane mouse modes (mouse_utf8_flag and mouse_sgr_flag) + and for origin mode (origin_flag). + +* Add -F to refresh-client for flags for control mode clients, only one flag + (no-output) supported at the moment. + +* Add a few vi(1) keys for menus. + * Add pane options, set with set-option -p and displayed with show-options -p. Pane options inherit from window options (so every pane option is also a window option). The pane style is now configured by setting window-style @@ -16,7 +34,8 @@ CHANGES FROM 3.0 to X.X window. * Add regular expression support for the format search, match and substitute - modifiers and make them able to ignore case. + modifiers and make them able to ignore case. find-window now accepts -r to + use regular expressions. * Do not use $TMUX to find the session because for windows in multiple sessions it is wrong as often as it is right, and for windows in one session it is -- cgit