diff --git a/.github/workflows/regress.yml b/.github/workflows/regress.yml new file mode 100644 index 000000000..883ee720e --- /dev/null +++ b/.github/workflows/regress.yml @@ -0,0 +1,75 @@ +name: 'Run Tests' + +on: + workflow_dispatch: + schedule: + - cron: '33 3 * * *' + +permissions: + contents: read + +concurrency: + group: tmux-tests + cancel-in-progress: true + +jobs: + regress: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 45 + + strategy: + fail-fast: false + matrix: + include: + - name: ubuntu-24.04-x64 + runner: ubuntu-24.04 + make: make + # - name: ubuntu-24.04-arm64 + # runner: ubuntu-24.04-arm + # make: make + # - name: macos-26-arm64 + # runner: macos-26 + # make: gmake + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y \ + autoconf \ + automake \ + bison \ + build-essential \ + libevent-dev \ + libncurses-dev \ + libutf8proc-dev \ + pkg-config + + - name: dependencies + if: runner.os == 'macOS' + run: | + brew install \ + autoconf \ + automake \ + bison \ + libevent \ + make \ + ncurses \ + utf8proc \ + pkg-config + + - name: build + run: | + sh autogen.sh + ./configure --enable-utf8proc + ${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)" + + - name: test + run: | + cd regress + ${{ matrix.make }} diff --git a/CHANGES b/CHANGES index cb92e2ff0..7b1a2a54d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,16 @@ +CHANGES FROM 3.7a TO 3.7b + +* Fix so that the end of a synchronized update again triggers a redraw. + +CHANGES FROM 3.7 TO 3.7a + +* Fix crash in break-pane when no name is provided. + +* Scrollbar options are now cached rather than being looked up for every redraw + (issue 5298). + +* Only forbid #( in names, allow #[, empty names, : and . + CHANGES FROM 3.6b TO 3.7 * Add floating panes. These are panes which sit above the layout ("tiled @@ -5,6 +18,7 @@ CHANGES FROM 3.6b TO 3.7 the same escape sequence support). Floating panes are created with the new-pane command, bound to * by default. + This is an early release of this feature and they are relatively limited. Currently floating panes can only be moved and resized using the mouse. The default second status line (if status-format is set to 2) has changed to show a list of panes. Many obvious features are not yet available for floating diff --git a/cmd-list-keys.c b/cmd-list-keys.c index abb51b2c8..7aa38c61e 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -232,7 +232,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) n = 1; ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); - format_defaults(ft, NULL, NULL, NULL, NULL); + format_defaults(ft, tc, NULL, NULL, NULL); format_add(ft, "notes_only", "%d", notes_only); format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n)); format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n)); diff --git a/colour.c b/colour.c index 0db080fc4..b37882a88 100644 --- a/colour.c +++ b/colour.c @@ -290,6 +290,58 @@ colour_tostring(int c) return ("invalid"); } +/* Convert colour to an SGR escape sequence. */ +const char * +colour_toescape(struct client *c, int colour, int bg) +{ + static char s[32]; + u_char r, g, b; + int n, flags = (TERM_256COLOURS|TERM_RGBCOLOURS); + u_int o = (bg ? 40 : 30); + + if (c != NULL && (c->tty.flags & TTY_OPENED) && c->tty.term != NULL) + flags = c->tty.term->flags; + + if (colour & COLOUR_FLAG_THEME) { + n = colour & 0xff; + if (c != NULL && (u_int)n < COLOUR_THEME_COUNT) + colour = c->theme_colours[n]; + else + colour = colour_theme_terminal_colour(n); + } + + if (colour == 8 || colour == 9) { + xsnprintf(s, sizeof s, "\033[%dm", o + 9); + return (s); + } + + if ((~flags & TERM_RGBCOLOURS) & (colour & COLOUR_FLAG_RGB)) { + colour_split_rgb(colour, &r, &g, &b); + colour = colour_find_rgb(r, g, b); + } + if ((~flags & TERM_256COLOURS) & (colour & COLOUR_FLAG_256)) + colour = colour_256to16(colour); + + if (colour & COLOUR_FLAG_RGB) { + colour_split_rgb(colour, &r, &g, &b); + xsnprintf(s, sizeof s, "\033[%d;2;%u;%u;%um", o + 8, r, g, b); + return (s); + } + if (colour & COLOUR_FLAG_256) { + xsnprintf(s, sizeof s, "\033[%d;5;%um", o + 8, colour & 0xff); + return (s); + } + if (colour >= 0 && colour <= 7) { + xsnprintf(s, sizeof s, "\033[%dm", colour + o); + return (s); + } + if (colour >= 90 && colour <= 97) { + xsnprintf(s, sizeof s, "\033[%dm", colour + o - 30); + return (s); + } + return (NULL); +} + /* Convert background colour to theme. */ enum client_theme colour_totheme(int c) diff --git a/format.c b/format.c index f3c8a0356..04215399a 100644 --- a/format.c +++ b/format.c @@ -121,6 +121,8 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_CLIENT_TERMCAP 0x1000000 #define FORMAT_CLIENT_TERMFEAT 0x2000000 #define FORMAT_CLIENT_ENVIRON 0x4000000 +#define FORMAT_COLOUR_ESC_FG 0x8000000 +#define FORMAT_COLOUR_ESC_BG 0x10000000 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 100 @@ -4489,7 +4491,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, break; /* Check single character modifiers with no arguments. */ - if (strchr("labcdnwETSWPL!<>", cp[0]) != NULL && + if (strchr("labdnwETSWPL!<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -4511,7 +4513,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, } /* Now try single character with arguments. */ - if (strchr("ImCLNPSst=pReqW", cp[0]) == NULL) + if (strchr("ImCLNPSst=pReqWc", cp[0]) == NULL) break; c = cp[0]; @@ -5289,6 +5291,12 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, break; case 'c': modifiers |= FORMAT_COLOUR; + if (fm->argc < 1) + break; + if (strchr(fm->argv[0], 'f') != NULL) + modifiers |= FORMAT_COLOUR_ESC_FG; + if (strchr(fm->argv[0], 'b') != NULL) + modifiers |= FORMAT_COLOUR_ESC_BG; break; case 'd': modifiers |= FORMAT_DIRNAME; @@ -5487,11 +5495,28 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, /* Is this a colour? */ if (modifiers & FORMAT_COLOUR) { new = format_expand1(es, copy); - c = colour_fromstring(new); - if (c == -1 || (c = colour_force_rgb(c)) == -1) - value = xstrdup(""); - else - xasprintf(&value, "%06x", c & 0xffffff); + if (modifiers & (FORMAT_COLOUR_ESC_FG|FORMAT_COLOUR_ESC_BG)) { + if (strcasecmp(new, "none") == 0) + value = xstrdup("\033[0m"); + else if ((c = colour_fromstring(new)) == -1) + value = xstrdup(""); + else { + if (modifiers & FORMAT_COLOUR_ESC_BG) + cp = colour_toescape(ft->c, c, 1); + else + cp = colour_toescape(ft->c, c, 0); + if (cp == NULL) + value = xstrdup(""); + else + value = xstrdup(cp); + } + } else { + c = colour_fromstring(new); + if (c == -1 || (c = colour_force_rgb(c)) == -1) + value = xstrdup(""); + else + xasprintf(&value, "%06x", c & 0xffffff); + } free(new); goto done; } diff --git a/grid.c b/grid.c index 28680cbdd..63b3b9d3a 100644 --- a/grid.c +++ b/grid.c @@ -74,11 +74,45 @@ grid_check_lines(struct grid *gd) } } } + +void +grid_check_is_clear(struct grid *gd) +{ + struct grid_line *gl; + u_int yy, ny; + + assert(gd != NULL); + + if (gd->sy == 0) { + assert(gd->linedata == NULL); + return; + } + + assert(gd->linedata != NULL); + + ny = gd->hsize + gd->sy; + for (yy = 0; yy < ny; yy++) { + gl = &gd->linedata[yy]; + + assert(gl->celldata == NULL); + assert(gl->cellused == 0); + assert(gl->cellsize == 0); + assert(gl->extddata == NULL); + assert(gl->extdsize == 0); + assert(gl->flags == 0); + assert(gl->time == 0); + } +} #else static void grid_check_lines(__unused struct grid *gd) { } + +void +grid_check_is_clear(__unused struct grid *gd) +{ +} #endif /* Store cell in entry. */ @@ -341,28 +375,18 @@ grid_create(u_int sx, u_int sy, u_int hlimit) { struct grid *gd; - gd = xmalloc(sizeof *gd); + gd = xcalloc(1, sizeof *gd); gd->sx = sx; gd->sy = sy; if (hlimit != 0) gd->flags = GRID_HISTORY; - else - gd->flags = 0; - - gd->hscrolled = 0; - gd->hsize = 0; gd->hlimit = hlimit; - gd->scroll_added = 0; - gd->scroll_collected = 0; - gd->scroll_generation = 0; - if (gd->sy != 0) gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); - else - gd->linedata = NULL; + grid_check_is_clear(gd); return (gd); } diff --git a/layout-custom.c b/layout-custom.c index 0fc3f5d16..c5a348ac0 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -265,7 +265,7 @@ layout_parse(struct window *w, const char *layout, char **cause) window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1); /* Destroy the old layout and swap to the new. */ - layout_free_cell(w->layout_root); + layout_free_cell(w->layout_root, 0); w->layout_root = tiled_lc; /* Assign the panes into the cells. */ @@ -291,7 +291,7 @@ layout_parse(struct window *w, const char *layout, char **cause) return (0); fail: - layout_free_cell(tiled_lc); + layout_free_cell(tiled_lc, 0); return (-1); } @@ -423,6 +423,6 @@ layout_construct(struct layout_cell *lcparent, const char **layout, return (0); fail: - layout_free_cell(*lc); + layout_free_cell(*lc, 0); return (-1); } diff --git a/layout-set.c b/layout-set.c index 157133904..cf78585c2 100644 --- a/layout-set.c +++ b/layout-set.c @@ -124,12 +124,12 @@ layout_set_previous(struct window *w) } static struct window_pane * -layout_first_tiled(struct window *w) +layout_set_first_tiled(struct window *w) { struct window_pane *wp; TAILQ_FOREACH(wp, &w->panes, entry) { - if (!window_pane_is_floating(wp)) + if (wp->layout_cell && layout_cell_is_tiled(wp->layout_cell)) return (wp); } return (NULL); @@ -139,19 +139,15 @@ static void layout_set_even(struct window *w, enum layout_type type) { struct window_pane *wp; - struct layout_cell *lc, *lcnew; + struct layout_cell *lcroot, *lcchild; u_int n, sx, sy; layout_print_cell(w->layout_root, __func__, 1); - /* Get number of panes. */ n = window_count_panes(w, 0); if (n <= 1) return; - /* Free the old root and construct a new. */ - layout_free(w); - lc = w->layout_root = layout_create_cell(NULL); if (type == LAYOUT_LEFTRIGHT) { sx = (n * (PANE_MINIMUM + 1)) - 1; if (sx < w->sx) @@ -163,30 +159,30 @@ layout_set_even(struct window *w, enum layout_type type) sy = w->sy; sx = w->sx; } - layout_set_size(lc, sx, sy, 0, 0); - layout_make_node(lc, type); - /* Build new leaf cells. */ + layout_free(w, 1); + lcroot = w->layout_root = layout_create_cell(NULL); + layout_set_size(lcroot, sx, sy, 0, 0); + layout_make_node(lcroot, type); + TAILQ_FOREACH(wp, &w->panes, entry) { - if (window_pane_is_floating(wp)) - continue; - lcnew = layout_create_cell(lc); - layout_make_leaf(lcnew, wp); - lcnew->sx = w->sx; - lcnew->sy = w->sy; - TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); + lcchild = wp->layout_cell; + TAILQ_INSERT_TAIL(&lcroot->cells, lcchild, entry); + lcchild->parent = lcroot; + if (layout_cell_is_tiled(lcchild)) { + lcchild->sx = w->sx; + lcchild->sy = w->sy; + } } - /* Spread out cells. */ - layout_spread_cell(w, lc); + layout_spread_cell(w, lcroot); - /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w, NULL); layout_print_cell(w->layout_root, __func__, 1); - window_resize(w, lc->sx, lc->sy, -1, -1); + window_resize(w, lcroot->sx, lcroot->sy, -1, -1); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -206,15 +202,14 @@ layout_set_even_v(struct window *w) static void layout_set_main_h(struct window *w) { - struct window_pane *wp; - struct layout_cell *lc, *lcmain, *lcother, *lcchild; + struct window_pane *wp, *wpmain; + struct layout_cell *lcroot, *lcmain, *lcother, *lcchild; u_int n, mainh, otherh, sx, sy; char *cause; const char *s; layout_print_cell(w->layout_root, __func__, 1); - /* Get number of panes. */ n = window_count_panes(w, 0); if (n <= 1) return; @@ -255,52 +250,48 @@ layout_set_main_h(struct window *w) if (sx < w->sx) sx = w->sx; - /* Free old tree and create a new root. */ - layout_free(w); - lc = w->layout_root = layout_create_cell(NULL); - layout_set_size(lc, sx, mainh + otherh + 1, 0, 0); - layout_make_node(lc, LAYOUT_TOPBOTTOM); + layout_free(w, 1); + lcroot = w->layout_root = layout_create_cell(NULL); + layout_set_size(lcroot, sx, mainh + otherh + 1, 0, 0); + layout_make_node(lcroot, LAYOUT_TOPBOTTOM); - /* Create the main pane. */ - lcmain = layout_create_cell(lc); + wpmain = layout_set_first_tiled(w); + lcmain = wpmain->layout_cell; + lcmain->parent = lcroot; layout_set_size(lcmain, sx, mainh, 0, 0); - layout_make_leaf(lcmain, layout_first_tiled(w)); - TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); + TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry); - /* Create the other pane. */ - lcother = layout_create_cell(lc); - layout_set_size(lcother, sx, otherh, 0, 0); if (n == 1) { - wp = TAILQ_NEXT(layout_first_tiled(w), entry); - while (wp != NULL && window_pane_is_floating(wp)) + wp = TAILQ_NEXT(wpmain, entry); + while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell)) wp = TAILQ_NEXT(wp, entry); - layout_make_leaf(lcother, wp); - TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); + TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry); + wp->layout_cell->parent = lcroot; } else { + lcother = layout_create_cell(lcroot); + layout_set_size(lcother, sx, otherh, 0, 0); layout_make_node(lcother, LAYOUT_LEFTRIGHT); - TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); + TAILQ_INSERT_TAIL(&lcroot->cells, lcother, entry); - /* Add the remaining panes as children. */ TAILQ_FOREACH(wp, &w->panes, entry) { - if (window_pane_is_floating(wp)) + if (wp == wpmain) continue; - if (wp == layout_first_tiled(w)) - continue; - lcchild = layout_create_cell(lcother); - layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0); - layout_make_leaf(lcchild, wp); + lcchild = wp->layout_cell; TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry); + lcchild->parent = lcother; + if (layout_cell_is_tiled(lcchild)) + layout_set_size(lcchild, PANE_MINIMUM, otherh, + 0, 0); } layout_spread_cell(w, lcother); } - /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w, NULL); layout_print_cell(w->layout_root, __func__, 1); - window_resize(w, lc->sx, lc->sy, -1, -1); + window_resize(w, lcroot->sx, lcroot->sy, -1, -1); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -308,15 +299,14 @@ layout_set_main_h(struct window *w) static void layout_set_main_h_mirrored(struct window *w) { - struct window_pane *wp; - struct layout_cell *lc, *lcmain, *lcother, *lcchild; + struct window_pane *wp, *wpmain; + struct layout_cell *lcroot, *lcmain, *lcother, *lcchild; u_int n, mainh, otherh, sx, sy; char *cause; const char *s; layout_print_cell(w->layout_root, __func__, 1); - /* Get number of panes. */ n = window_count_panes(w, 0); if (n <= 1) return; @@ -357,52 +347,48 @@ layout_set_main_h_mirrored(struct window *w) if (sx < w->sx) sx = w->sx; - /* Free old tree and create a new root. */ - layout_free(w); - lc = w->layout_root = layout_create_cell(NULL); - layout_set_size(lc, sx, mainh + otherh + 1, 0, 0); - layout_make_node(lc, LAYOUT_TOPBOTTOM); + layout_free(w, 1); + lcroot = w->layout_root = layout_create_cell(NULL); + layout_set_size(lcroot, sx, mainh + otherh + 1, 0, 0); + layout_make_node(lcroot, LAYOUT_TOPBOTTOM); + + wpmain = layout_set_first_tiled(w); + lcmain = wpmain->layout_cell; + lcmain->parent = lcroot; + layout_set_size(lcmain, sx, mainh, 0, 0); + TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry); - /* Create the other pane. */ - lcother = layout_create_cell(lc); - layout_set_size(lcother, sx, otherh, 0, 0); if (n == 1) { - wp = TAILQ_NEXT(layout_first_tiled(w), entry); - while (wp != NULL && window_pane_is_floating(wp)) + wp = TAILQ_NEXT(wpmain, entry); + while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell)) wp = TAILQ_NEXT(wp, entry); - layout_make_leaf(lcother, wp); - TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); + TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry); + wp->layout_cell->parent = lcroot; } else { + lcother = layout_create_cell(lcroot); + layout_set_size(lcother, sx, otherh, 0, 0); layout_make_node(lcother, LAYOUT_LEFTRIGHT); - TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); + TAILQ_INSERT_HEAD(&lcroot->cells, lcother, entry); - /* Add the remaining panes as children. */ TAILQ_FOREACH(wp, &w->panes, entry) { - if (window_pane_is_floating(wp)) + if (wp == wpmain) continue; - if (wp == layout_first_tiled(w)) - continue; - lcchild = layout_create_cell(lcother); - layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0); - layout_make_leaf(lcchild, wp); + lcchild = wp->layout_cell; TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry); + lcchild->parent = lcother; + if (layout_cell_is_tiled(lcchild)) + layout_set_size(lcchild, PANE_MINIMUM, otherh, + 0, 0); } layout_spread_cell(w, lcother); } - /* Create the main pane. */ - lcmain = layout_create_cell(lc); - layout_set_size(lcmain, sx, mainh, 0, 0); - layout_make_leaf(lcmain, layout_first_tiled(w)); - TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); - - /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w, NULL); layout_print_cell(w->layout_root, __func__, 1); - window_resize(w, lc->sx, lc->sy, -1, -1); + window_resize(w, lcroot->sx, lcroot->sy, -1, -1); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -410,21 +396,20 @@ layout_set_main_h_mirrored(struct window *w) static void layout_set_main_v(struct window *w) { - struct window_pane *wp; - struct layout_cell *lc, *lcmain, *lcother, *lcchild; + struct window_pane *wp, *wpmain; + struct layout_cell *lcroot, *lcmain, *lcother, *lcchild; u_int n, mainw, otherw, sx, sy; char *cause; const char *s; layout_print_cell(w->layout_root, __func__, 1); - /* Get number of panes. */ n = window_count_panes(w, 0); if (n <= 1) return; n--; /* take off main pane */ - /* Find available width - take off one line for the border. */ + /* Find available width - take off one column for the border. */ sx = w->sx - 1; /* Get the main pane width. */ @@ -459,52 +444,48 @@ layout_set_main_v(struct window *w) if (sy < w->sy) sy = w->sy; - /* Free old tree and create a new root. */ - layout_free(w); - lc = w->layout_root = layout_create_cell(NULL); - layout_set_size(lc, mainw + otherw + 1, sy, 0, 0); - layout_make_node(lc, LAYOUT_LEFTRIGHT); + layout_free(w, 1); + lcroot = w->layout_root = layout_create_cell(NULL); + layout_set_size(lcroot, mainw + otherw + 1, sy, 0, 0); + layout_make_node(lcroot, LAYOUT_LEFTRIGHT); - /* Create the main pane. */ - lcmain = layout_create_cell(lc); + wpmain = layout_set_first_tiled(w); + lcmain = wpmain->layout_cell; + lcmain->parent = lcroot; layout_set_size(lcmain, mainw, sy, 0, 0); - layout_make_leaf(lcmain, layout_first_tiled(w)); - TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); + TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry); - /* Create the other pane. */ - lcother = layout_create_cell(lc); - layout_set_size(lcother, otherw, sy, 0, 0); if (n == 1) { - wp = TAILQ_NEXT(layout_first_tiled(w), entry); - while (wp != NULL && window_pane_is_floating(wp)) + wp = TAILQ_NEXT(wpmain, entry); + while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell)) wp = TAILQ_NEXT(wp, entry); - layout_make_leaf(lcother, wp); - TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); + TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry); + wp->layout_cell->parent = lcroot; } else { + lcother = layout_create_cell(lcroot); layout_make_node(lcother, LAYOUT_TOPBOTTOM); - TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); + layout_set_size(lcother, otherw, sy, 0, 0); + TAILQ_INSERT_TAIL(&lcroot->cells, lcother, entry); - /* Add the remaining panes as children. */ TAILQ_FOREACH(wp, &w->panes, entry) { - if (window_pane_is_floating(wp)) + if (wp == wpmain) continue; - if (wp == layout_first_tiled(w)) - continue; - lcchild = layout_create_cell(lcother); - layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0); - layout_make_leaf(lcchild, wp); + lcchild = wp->layout_cell; TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry); + lcchild->parent = lcother; + if (layout_cell_is_tiled(lcchild)) + layout_set_size(lcchild, otherw, PANE_MINIMUM, + 0, 0); } layout_spread_cell(w, lcother); } - /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w, NULL); layout_print_cell(w->layout_root, __func__, 1); - window_resize(w, lc->sx, lc->sy, -1, -1); + window_resize(w, lcroot->sx, lcroot->sy, -1, -1); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -512,8 +493,8 @@ layout_set_main_v(struct window *w) static void layout_set_main_v_mirrored(struct window *w) { - struct window_pane *wp; - struct layout_cell *lc, *lcmain, *lcother, *lcchild; + struct window_pane *wp, *wpmain; + struct layout_cell *lcroot, *lcmain, *lcother, *lcchild; u_int n, mainw, otherw, sx, sy; char *cause; const char *s; @@ -526,7 +507,7 @@ layout_set_main_v_mirrored(struct window *w) return; n--; /* take off main pane */ - /* Find available width - take off one line for the border. */ + /* Find available width - take off one column for the border. */ sx = w->sx - 1; /* Get the main pane width. */ @@ -561,62 +542,58 @@ layout_set_main_v_mirrored(struct window *w) if (sy < w->sy) sy = w->sy; - /* Free old tree and create a new root. */ - layout_free(w); - lc = w->layout_root = layout_create_cell(NULL); - layout_set_size(lc, mainw + otherw + 1, sy, 0, 0); - layout_make_node(lc, LAYOUT_LEFTRIGHT); + layout_free(w, 1); + lcroot = w->layout_root = layout_create_cell(NULL); + layout_set_size(lcroot, mainw + otherw + 1, sy, 0, 0); + layout_make_node(lcroot, LAYOUT_LEFTRIGHT); + + wpmain = layout_set_first_tiled(w); + lcmain = wpmain->layout_cell; + lcmain->parent = lcroot; + layout_set_size(lcmain, mainw, sy, 0, 0); + TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry); - /* Create the other pane. */ - lcother = layout_create_cell(lc); - layout_set_size(lcother, otherw, sy, 0, 0); if (n == 1) { - wp = TAILQ_NEXT(layout_first_tiled(w), entry); - while (wp != NULL && window_pane_is_floating(wp)) + wp = TAILQ_NEXT(wpmain, entry); + while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell)) wp = TAILQ_NEXT(wp, entry); - layout_make_leaf(lcother, wp); - TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); + TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry); + wp->layout_cell->parent = lcroot; } else { + lcother = layout_create_cell(lcroot); layout_make_node(lcother, LAYOUT_TOPBOTTOM); - TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); + layout_set_size(lcother, otherw, sy, 0, 0); + TAILQ_INSERT_HEAD(&lcroot->cells, lcother, entry); - /* Add the remaining panes as children. */ TAILQ_FOREACH(wp, &w->panes, entry) { - if (window_pane_is_floating(wp)) + if (wp == wpmain) continue; - if (wp == layout_first_tiled(w)) - continue; - lcchild = layout_create_cell(lcother); - layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0); - layout_make_leaf(lcchild, wp); + lcchild = wp->layout_cell; TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry); + lcchild->parent = lcother; + if (layout_cell_is_tiled(lcchild)) + layout_set_size(lcchild, otherw, PANE_MINIMUM, + 0, 0); } layout_spread_cell(w, lcother); } - /* Create the main pane. */ - lcmain = layout_create_cell(lc); - layout_set_size(lcmain, mainw, sy, 0, 0); - layout_make_leaf(lcmain, layout_first_tiled(w)); - TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); - - /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w, NULL); layout_print_cell(w->layout_root, __func__, 1); - window_resize(w, lc->sx, lc->sy, -1, -1); + window_resize(w, lcroot->sx, lcroot->sy, -1, -1); notify_window("window-layout-changed", w); server_redraw_window(w); } -void +static void layout_set_tiled(struct window *w) { struct options *oo = w->options; struct window_pane *wp; - struct layout_cell *lc, *lcrow, *lcchild; + struct layout_cell *lcroot, *lcrow, *lcchild; u_int n, width, height, used, sx, sy; u_int i, j, columns, rows, max_columns; @@ -647,56 +624,59 @@ layout_set_tiled(struct window *w) if (height < PANE_MINIMUM) height = PANE_MINIMUM; - /* Free old tree and create a new root. */ - layout_free(w); - lc = w->layout_root = layout_create_cell(NULL); sx = ((width + 1) * columns) - 1; if (sx < w->sx) sx = w->sx; sy = ((height + 1) * rows) - 1; if (sy < w->sy) sy = w->sy; - layout_set_size(lc, sx, sy, 0, 0); - layout_make_node(lc, LAYOUT_TOPBOTTOM); - /* Create a grid of the cells, skipping any floating panes. */ + layout_free(w, 1); + lcroot = w->layout_root = layout_create_cell(NULL); + layout_set_size(lcroot, sx, sy, 0, 0); + layout_make_node(lcroot, LAYOUT_TOPBOTTOM); + + /* Create a grid of the tiled cells. */ wp = TAILQ_FIRST(&w->panes); - while (wp != NULL && window_pane_is_floating(wp)) - wp = TAILQ_NEXT(wp, entry); for (j = 0; j < rows; j++) { + while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell)) + wp = TAILQ_NEXT(wp, entry); /* If this is the last cell, all done. */ if (wp == NULL) break; - /* Create the new row. */ - lcrow = layout_create_cell(lc); - layout_set_size(lcrow, w->sx, height, 0, 0); - TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); + lcchild = wp->layout_cell; /* If only one column, just use the row directly. */ if (n - (j * columns) == 1 || columns == 1) { - layout_make_leaf(lcrow, wp); + lcchild->parent = lcroot; + TAILQ_INSERT_TAIL(&lcroot->cells, lcchild, entry); + layout_set_size(lcchild, w->sx, height, 0, 0); wp = TAILQ_NEXT(wp, entry); - while (wp != NULL && window_pane_is_floating(wp)) - wp = TAILQ_NEXT(wp, entry); continue; } - /* Add in the columns. */ + /* Create the new row. */ + lcrow = layout_create_cell(lcroot); layout_make_node(lcrow, LAYOUT_LEFTRIGHT); + layout_set_size(lcrow, w->sx, height, 0, 0); + TAILQ_INSERT_TAIL(&lcroot->cells, lcrow, entry); + + /* Add in the columns. */ for (i = 0; i < columns; i++) { /* Create and add a pane cell. */ - lcchild = layout_create_cell(lcrow); - layout_set_size(lcchild, width, height, 0, 0); - layout_make_leaf(lcchild, wp); + lcchild->parent = lcrow; TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); + layout_set_size(lcchild, width, height, 0, 0); /* Move to the next non-floating cell. */ wp = TAILQ_NEXT(wp, entry); - while (wp != NULL && window_pane_is_floating(wp)) + while (wp != NULL && + !layout_cell_is_tiled(wp->layout_cell)) wp = TAILQ_NEXT(wp, entry); if (wp == NULL) break; + lcchild = wp->layout_cell; } /* @@ -713,21 +693,19 @@ layout_set_tiled(struct window *w) w->sx - used); } - /* Adjust the last row height to fit if necessary. */ used = (rows * height) + rows - 1; if (w->sy > used) { - lcrow = TAILQ_LAST(&lc->cells, layout_cells); + lcrow = TAILQ_LAST(&lcroot->cells, layout_cells); layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM, w->sy - used); } - /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w, NULL); layout_print_cell(w->layout_root, __func__, 1); - window_resize(w, lc->sx, lc->sy, -1, -1); + window_resize(w, lcroot->sx, lcroot->sy, -1, -1); notify_window("window-layout-changed", w); server_redraw_window(w); } diff --git a/layout.c b/layout.c index c1bb3c269..234aa26c1 100644 --- a/layout.c +++ b/layout.c @@ -89,20 +89,24 @@ layout_create_cell(struct layout_cell *lcparent) /* Free a layout cell. */ void -layout_free_cell(struct layout_cell *lc) +layout_free_cell(struct layout_cell *lc, int only_nodes) { - struct layout_cell *lcchild; + struct layout_cell *lcchild, *lcnext; - if (lc == NULL) + if (lc == NULL || (only_nodes && lc->type == LAYOUT_WINDOWPANE)) return; switch (lc->type) { case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: - while (!TAILQ_EMPTY(&lc->cells)) { - lcchild = TAILQ_FIRST(&lc->cells); - TAILQ_REMOVE(&lc->cells, lcchild, entry); - layout_free_cell(lcchild); + lcchild = TAILQ_FIRST(&lc->cells); + while (lcchild != NULL) { + lcnext = TAILQ_NEXT(lcchild, entry); + if (!only_nodes || lcchild->type != LAYOUT_WINDOWPANE) { + TAILQ_REMOVE(&lc->cells, lcchild, entry); + layout_free_cell(lcchild, only_nodes); + } + lcchild = lcnext; } break; case LAYOUT_WINDOWPANE: @@ -715,13 +719,13 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc, if (lcparent == NULL) { if (lc->wp != NULL) *lcroot = NULL; - layout_free_cell(lc); + layout_free_cell(lc, 0); return; } if (!layout_cell_is_tiled(lc)) { TAILQ_REMOVE(&lcparent->cells, lc, entry); - layout_free_cell(lc); + layout_free_cell(lc, 0); goto out; } @@ -737,7 +741,7 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc, /* Remove this from the parent's list. */ TAILQ_REMOVE(&lcparent->cells, lc, entry); - layout_free_cell(lc); + layout_free_cell(lc, 0); out: /* @@ -759,7 +763,7 @@ out: } else TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry); - layout_free_cell(lcparent); + layout_free_cell(lcparent, 0); } } @@ -777,9 +781,9 @@ layout_init(struct window *w, struct window_pane *wp) /* Free layout for pane. */ void -layout_free(struct window *w) +layout_free(struct window *w, int only_nodes) { - layout_free_cell(w->layout_root); + layout_free_cell(w->layout_root, only_nodes); } /* Resize the entire layout after window resize. */ @@ -1524,7 +1528,8 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) number = 0; TAILQ_FOREACH (lc, &parent->cells, entry) - number++; + if (layout_cell_is_tiled(lc)) + number++; if (number <= 1) return (0); status = window_get_pane_status(w); @@ -1552,6 +1557,8 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) changed = 0; TAILQ_FOREACH (lc, &parent->cells, entry) { + if (!layout_cell_is_tiled(lc)) + continue; change = 0; if (parent->type == LAYOUT_LEFTRIGHT) { change = each - (int)lc->sx; diff --git a/mode-tree.c b/mode-tree.c index fae0f3d8a..3a126129c 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -206,8 +206,6 @@ static const char* mode_tree_help_start[] = { "#[fg=themelightgrey]" " C-s #[#{E:tree-mode-border-style},acs]x#[default] Search forward", "#[fg=themelightgrey]" - " C-r #[#{E:tree-mode-border-style},acs]x#[default] Search backward", - "#[fg=themelightgrey]" " n #[#{E:tree-mode-border-style},acs]x#[default] Repeat search forward", "#[fg=themelightgrey]" " N #[#{E:tree-mode-border-style},acs]x#[default] Repeat search backward", diff --git a/regress/UTF-8-test.txt b/regress/UTF-8-test.txt index a5b5d50e6..ab230089d 100644 Binary files a/regress/UTF-8-test.txt and b/regress/UTF-8-test.txt differ diff --git a/regress/check-names.sh b/regress/check-names.sh index 21e185ac1..886299f2d 100644 --- a/regress/check-names.sh +++ b/regress/check-names.sh @@ -35,68 +35,59 @@ $TMUX set-option -qg allow-set-title on || exit 1 $TMUX set-option -qg allow-rename on || exit 1 $TMUX set-option -qg automatic-rename off || exit 1 -# Commands reject ':' and '.' for sessions and windows, but allow '#'. -$TMUX rename-session 'session#ok' || fail "session name with # rejected" -must_equal "$($TMUX display-message -p '#{session_name}')" 'session#ok' -must_fail $TMUX rename-session 'session:bad' -must_fail $TMUX rename-session 'session.bad' +# Commands allow empty names, ':', '.', '#' and '#('. +$TMUX rename-session '' || fail "empty session name rejected" +must_equal "$($TMUX display-message -p '#{session_name}')" '' +$TMUX rename-session 'session:.##(ok)' || \ + fail "session name with : . or #( rejected" +must_equal "$($TMUX display-message -p '#{session_name}')" 'session:.#(ok)' -$TMUX rename-window 'window#ok' || fail "window name with # rejected" -must_equal "$($TMUX display-message -p '#{window_name}')" 'window#ok' -must_fail $TMUX rename-window 'window:bad' -must_fail $TMUX rename-window 'window.bad' +$TMUX rename-window '' || fail "empty window name rejected" +must_equal "$($TMUX display-message -p '#{window_name}')" '' +$TMUX rename-window 'window:.##(ok)' || \ + fail "window name with : . or #( rejected" +must_equal "$($TMUX display-message -p '#{window_name}')" 'window:.#(ok)' -$TMUX set-option -q @name 'format#ok' || exit 1 +$TMUX set-option -q @name 'format:.#(ok)' || exit 1 $TMUX rename-session '#{@name}' || fail "format in session name not expanded" -must_equal "$($TMUX display-message -p '#{session_name}')" 'format#ok' +must_equal "$($TMUX display-message -p '#{session_name}')" 'format:.#(ok)' $TMUX rename-window '#{@name}' || fail "format in window name not expanded" -must_equal "$($TMUX display-message -p '#{window_name}')" 'format#ok' -must_fail $TMUX rename-session '#{session_name}:bad' -must_fail $TMUX rename-window '#{window_name}.bad' +must_equal "$($TMUX display-message -p '#{window_name}')" 'format:.#(ok)' +$TMUX set-option -q @name 'format:.#(ok)' || exit 1 pid=$($TMUX display-message -p '#{pid}') created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \ - -s 'new-session#ok' -n 'new-window#ok') || \ - fail "new-session name with # rejected" + -s 'new-session:.##(ok)' -n 'new-window:.##(ok)') || \ + fail "new-session name with : . or #( rejected" created_session=${created%:*} created_window=${created#*:} must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \ - 'new-session#ok' + 'new-session:.#(ok)' must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \ - 'new-window#ok' + 'new-window:.#(ok)' $TMUX kill-session -t "$created_session" -must_fail $TMUX new-session -d -s 'new-session:bad' -must_fail $TMUX new-session -d -s 'new-session.bad' -must_fail $TMUX new-session -d -n 'new-window:bad' -must_fail $TMUX new-session -d -n 'new-window.bad' - created_window=$($TMUX new-window -dP -F '#{window_id}' \ - -n 'created-window#ok') || \ - fail "new-window name with # rejected" + -n 'created-window:.##(ok)') || \ + fail "new-window name with : . or #( rejected" must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \ - 'created-window#ok' -must_fail $TMUX new-window -d -n 'created-window:bad' -must_fail $TMUX new-window -d -n 'created-window.bad' + 'created-window:.#(ok)' created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \ - -s 'new-session-#{pid}' -n 'new-window-#{pid}') || \ + -s 'new-session-#{pid}:.##(ok)' -n 'new-window-#{pid}:.##(ok)') || \ fail "format in new-session name not expanded" created_session=${created%:*} created_window=${created#*:} must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \ - "new-session-$pid" + "new-session-$pid:.#(ok)" must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \ - "new-window-$pid" + "new-window-$pid:.#(ok)" $TMUX kill-session -t "$created_session" created_window=$($TMUX new-window -dP -F '#{window_id}' -n '#{@name}') || \ fail "format in new-window name not expanded" must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \ - 'format#ok' -must_fail $TMUX new-session -d -s 'new-session-#{pid}:bad' -must_fail $TMUX new-session -d -n 'new-window-#{pid}.bad' -must_fail $TMUX new-window -d -n '#{window_name}:bad' + 'format:.#(ok)' # Invalid UTF-8 is never allowed for command names. invalid=$(printf '\302') @@ -124,9 +115,9 @@ must_equal "$($TMUX list-buffers -F '#{buffer_name}')" 'buffer#:.ok' # Window names from escape sequences allow '#' except in '#('. $TMUX send-keys "printf '\\033kescape#:.ok\\033\\\\'" Enter || exit 1 sleep 1 -must_equal "$($TMUX display-message -p '#{window_name}')" 'escape#__ok' +must_equal "$($TMUX display-message -p '#{window_name}')" 'escape#:.ok' -# Titles from escape sequences reject only '#'. +# Titles from escape sequences allow '#' except in '#('. $TMUX send-keys "printf '\\033]2;escape#:.ok\\007'" Enter || exit 1 sleep 1 must_equal "$($TMUX display-message -p '#{pane_title}')" 'escape#:.ok' diff --git a/regress/prompt-keys.sh b/regress/prompt-keys.sh index 7fa7a673b..8c17581b1 100644 --- a/regress/prompt-keys.sh +++ b/regress/prompt-keys.sh @@ -131,11 +131,14 @@ $IN send-keys -l "Z" || exit 1 settle search_is "hello Z" "C-w did not kill a word" -# C-a then C-k kills the whole line. +# C-a then C-k kills the whole line. The mode prompt no longer fills the rest +# of the row, so insert a marker to distinguish prompt input from tree content +# that may remain visible after the prompt. $IN send-keys C-a || exit 1 $IN send-keys C-k || exit 1 +$IN send-keys -l "X" || exit 1 settle -search_row | grep -q '(search) [^ ]' && fail "C-a C-k did not clear the line" +search_is "X" "C-a C-k did not clear the line" # --- 3. Editing kept the prompt open the whole time. --- in_tree_mode || fail "editing keys closed the mode" diff --git a/regress/screen-redraw-results/floating-over-scrollbar.result b/regress/screen-redraw-results/floating-over-scrollbar.result index 5e85eff82..d618c0a60 100644 --- a/regress/screen-redraw-results/floating-over-scrollbar.result +++ b/regress/screen-redraw-results/floating-over-scrollbar.result @@ -1,12 +1,12 @@ base      - ┌──────────────┐  - │OVERSB  │  - │  │  - │  │  - │  │  - └──────────────┘  + ┌──────────────┐  + │OVERSB  │  + │  │  + │  │  + │  │  + └──────────────┘        diff --git a/regress/screen-redraw-results/marked-pane-lr.result b/regress/screen-redraw-results/marked-pane-lr.result index 4f3d1af11..53c9aff44 100644 --- a/regress/screen-redraw-results/marked-pane-lr.result +++ b/regress/screen-redraw-results/marked-pane-lr.result @@ -1,12 +1,12 @@ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ + │ + │ + │ + │ + │ + │ + │ + │ + │ + │ + │ + │ diff --git a/regress/screen-redraw-results/marked-pane-status.result b/regress/screen-redraw-results/marked-pane-status.result index ff99a0bc2..502f4cc70 100644 --- a/regress/screen-redraw-results/marked-pane-status.result +++ b/regress/screen-redraw-results/marked-pane-status.result @@ -1,12 +1,12 @@ -── 0:left ──────────┬── 1:right ──────── - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ +── 0:left ──────────┬── 1:right ──────── + │ + │ + │ + │ + │ + │ + │ + │ + │ + │ + │ diff --git a/regress/screen-redraw-results/marked-pane-tb.result b/regress/screen-redraw-results/marked-pane-tb.result index 23fe88924..c16b2a177 100644 --- a/regress/screen-redraw-results/marked-pane-tb.result +++ b/regress/screen-redraw-results/marked-pane-tb.result @@ -4,7 +4,7 @@ -──────────────────────────────────────── +──────────────────────────────────────── diff --git a/regress/screen-redraw-results/marked-pane-three.result b/regress/screen-redraw-results/marked-pane-three.result index f7b776713..e199f5415 100644 --- a/regress/screen-redraw-results/marked-pane-three.result +++ b/regress/screen-redraw-results/marked-pane-three.result @@ -1,12 +1,12 @@ - │ │ - │ │ - │ │ - │ │ - │ │ - │ │ - │ │ - │ │ - │ │ - │ │ - │ │ - │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ diff --git a/regress/screen-redraw-results/scrollbar-floating.result b/regress/screen-redraw-results/scrollbar-floating.result index 51542ad0d..81c9dfbae 100644 --- a/regress/screen-redraw-results/scrollbar-floating.result +++ b/regress/screen-redraw-results/scrollbar-floating.result @@ -1,12 +1,12 @@ SB00 abcdefghij  SB01 abcdefghij  SB02 abcdefghij  -SB03 abc┌──────────────────┐  -SB04 abc│FLOAT02 abcdef  │  -SB05 abc│FLOAT03 abcdef  │  -SB06 abc│FLOAT04 abcdef  │  -SB07 abc│  │  -SB08 abc└──────────────────┘  +SB03 abc┌──────────────────┐  +SB04 abc│FLOAT02 abcdef  │  +SB05 abc│FLOAT03 abcdef  │  +SB06 abc│FLOAT04 abcdef  │  +SB07 abc│  │  +SB08 abc└──────────────────┘  SB09 abcdefghij  SB10 abcdefghij    diff --git a/regress/screen-redraw-results/scrollbar-split-left.result b/regress/screen-redraw-results/scrollbar-split-left.result index 9d602a4d6..5ac964ab2 100644 --- a/regress/screen-redraw-results/scrollbar-split-left.result +++ b/regress/screen-redraw-results/scrollbar-split-left.result @@ -1,10 +1,10 @@ - SB00 abcdefghij │ SBL00 abcdefghij - SB01 abcdefghij │ SBL01 abcdefghij - SB02 abcdefghij │ SBL02 abcdefghij - SB03 abcdefghij │ SBL03 abcdefghij - SB04 abcdefghij │ SBL04 abcdefghij - SB05 abcdefghij │ SBL05 abcdefghij - SB06 abcdefghij │ SBL06 abcdefghij + SB00 abcdefghij │ SBL00 abcdefghij + SB01 abcdefghij │ SBL01 abcdefghij + SB02 abcdefghij │ SBL02 abcdefghij + SB03 abcdefghij │ SBL03 abcdefghij + SB04 abcdefghij │ SBL04 abcdefghij + SB05 abcdefghij │ SBL05 abcdefghij + SB06 abcdefghij │ SBL06 abcdefghij  SB07 abcdefghij │ SBL07 abcdefghij  SB08 abcdefghij │ SBL08 abcdefghij  SB09 abcdefghij │ SBL09 abcdefghij diff --git a/regress/screen-redraw-results/scrollbar-split-right.result b/regress/screen-redraw-results/scrollbar-split-right.result index ab1d465ef..7946089cd 100644 --- a/regress/screen-redraw-results/scrollbar-split-right.result +++ b/regress/screen-redraw-results/scrollbar-split-right.result @@ -1,10 +1,10 @@ -SB00 abcdefghij  │SBR00 abcdefghij  -SB01 abcdefghij  │SBR01 abcdefghij  -SB02 abcdefghij  │SBR02 abcdefghij  -SB03 abcdefghij  │SBR03 abcdefghij  -SB04 abcdefghij  │SBR04 abcdefghij  -SB05 abcdefghij  │SBR05 abcdefghij  -SB06 abcdefghij  │SBR06 abcdefghij  +SB00 abcdefghij  │SBR00 abcdefghij  +SB01 abcdefghij  │SBR01 abcdefghij  +SB02 abcdefghij  │SBR02 abcdefghij  +SB03 abcdefghij  │SBR03 abcdefghij  +SB04 abcdefghij  │SBR04 abcdefghij  +SB05 abcdefghij  │SBR05 abcdefghij  +SB06 abcdefghij  │SBR06 abcdefghij  SB07 abcdefghij  │SBR07 abcdefghij  SB08 abcdefghij  │SBR08 abcdefghij  SB09 abcdefghij  │SBR09 abcdefghij  diff --git a/regress/screen-redraw-results/window-style-active.result b/regress/screen-redraw-results/window-style-active.result index 6210820aa..aa110d5ed 100644 --- a/regress/screen-redraw-results/window-style-active.result +++ b/regress/screen-redraw-results/window-style-active.result @@ -1,8 +1,8 @@ -STYLE00 abcdefghij │STYLE00 abcdefghij -STYLE01 abcdefghij │STYLE01 abcdefghij -STYLE02 abcdefghij │STYLE02 abcdefghij -STYLE03 abcdefghij │STYLE03 abcdefghij -STYLE04 abcdefghij │STYLE04 abcdefghij +STYLE00 abcdefghij │STYLE00 abcdefghij +STYLE01 abcdefghij │STYLE01 abcdefghij +STYLE02 abcdefghij │STYLE02 abcdefghij +STYLE03 abcdefghij │STYLE03 abcdefghij +STYLE04 abcdefghij │STYLE04 abcdefghij STYLE05 abcdefghij │STYLE05 abcdefghij STYLE06 abcdefghij │STYLE06 abcdefghij  │ diff --git a/regress/utf8-test.result b/regress/utf8-test.result index e700cb17f..67ed5cb52 100644 --- a/regress/utf8-test.result +++ b/regress/utf8-test.result @@ -90,7 +90,6 @@ You should see the Greek word 'kosme': "κόσμε" 2.3.2 U-0000E000 = ee 80 80 = "" | 2.3.3 U-0000FFFD = ef bf bd = "�" | 2.3.4 U-0010FFFF = f4 8f bf bf = "􏿿" | -2.3.5 U-00110000 = f4 90 80 80 = "�" | | 3 Malformed sequences | | diff --git a/screen-redraw.c b/screen-redraw.c index 1cd314bb7..21499d426 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1655,12 +1655,14 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags) if (wp != NULL) { if (wp->base.mode & MODE_SYNC) screen_write_stop_sync(wp); + screen_write_clear_dirty(wp); } else { TAILQ_FOREACH(loop, &scene->w->panes, entry) { if (!window_pane_is_visible(loop)) continue; if (loop->base.mode & MODE_SYNC) screen_write_stop_sync(loop); + screen_write_clear_dirty(loop); } } } diff --git a/screen-write.c b/screen-write.c index 0e5a50c97..cbf1988fa 100644 --- a/screen-write.c +++ b/screen-write.c @@ -38,6 +38,7 @@ static int screen_write_overwrite(struct screen_write_ctx *, struct grid_cell *, u_int); static int screen_write_combine(struct screen_write_ctx *, const struct grid_cell *); +static void screen_write_flush_dirty(struct window_pane *); struct screen_write_citem { u_int x; @@ -217,6 +218,48 @@ screen_write_pane_is_obscured(struct screen_write_ctx *ctx) return (0); } +/* Should we draw to the TTY? */ +static int +screen_write_should_draw_lines(struct screen_write_ctx *ctx, u_int y, u_int ny) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = ctx->s; + u_int sy = screen_size_y(s); + bitstr_t *bs; + + if (wp != NULL && (wp->flags & (PANE_REDRAW|PANE_DROP))) + return (0); + if (s->mode & MODE_SYNC) { + if (wp != NULL && y < sy && ny != 0) { + bs = wp->sync_dirty; + if (ny > sy - y) + ny = sy - y; + if (bs == NULL || wp->sync_dirty_size != sy) { + if (bs != NULL && wp->sync_dirty_size != sy) { + y = 0; + ny = sy; + } + free(bs); + + bs = wp->sync_dirty = bit_alloc(sy); + if (bs == NULL) + fatal("bit_alloc failed"); + wp->sync_dirty_size = sy; + } + bit_nset(bs, y, y + ny - 1); + } + return (0); + } + return (1); +} + +/* Should we draw this line to the TTY? */ +static int +screen_write_should_draw_line(struct screen_write_ctx *ctx, u_int y) +{ + return (screen_write_should_draw_lines(ctx, y, 1)); +} + /* Set up context for TTY command. */ static void screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, @@ -982,7 +1025,7 @@ screen_write_sync_callback(__unused int fd, __unused short events, void *arg) if (wp->base.mode & MODE_SYNC) { wp->base.mode &= ~MODE_SYNC; - wp->flags |= PANE_REDRAW; + screen_write_flush_dirty(wp); } } @@ -1007,13 +1050,15 @@ screen_write_start_sync(struct window_pane *wp) void screen_write_stop_sync(struct window_pane *wp) { - if (wp == NULL) + if (wp == NULL || (~wp->base.mode & MODE_SYNC)) return; if (event_initialized(&wp->sync_timer)) evtimer_del(&wp->sync_timer); wp->base.mode &= ~MODE_SYNC; + screen_write_flush_dirty(wp); + log_debug("%s: %%%u stopped sync mode", __func__, wp->id); } @@ -1167,9 +1212,6 @@ screen_write_redraw_line(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, struct visible_ranges *r; struct visible_range *ri; - if (s->mode & MODE_SYNC) - return; - r = window_visible_ranges(wp, xoff, yoff + yy, sx, NULL); for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; @@ -1208,6 +1250,44 @@ screen_write_redraw_line(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, } } +/* Redraw dirty lines. */ +static void +screen_write_flush_dirty(struct window_pane *wp) +{ + struct screen_write_ctx ctx; + struct tty_ctx ttyctx; + struct screen *s = &wp->base; + u_int y, sy = screen_size_y(s), lines = 0; + + if (wp->sync_dirty == NULL) + return; + + screen_write_start_pane(&ctx, wp, s); + screen_write_initctx(&ctx, &ttyctx, 1, 1); + + for (y = 0; y < sy; y++) { + if (bit_test(wp->sync_dirty, y)) { + screen_write_redraw_line(&ctx, &ttyctx, y); + lines++; + } + } + log_debug("%s: %%%u had %u dirty lines", __func__, wp->id, lines); + + screen_write_stop(&ctx); + screen_write_clear_dirty(wp); +} + +/* Clear any dirty lines. */ +void +screen_write_clear_dirty(struct window_pane *wp) +{ + if (wp != NULL && wp->sync_dirty != NULL) { + free(wp->sync_dirty); + wp->sync_dirty = NULL; + wp->sync_dirty_size = 0; + } +} + /* Redraw all visible cells in a pane. */ static void screen_write_redraw_pane(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) @@ -1250,7 +1330,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) screen_write_initctx(ctx, &ttyctx, 1, 1); - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_lines(ctx, 0, screen_size_y(s))) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_alignmenttest, &ttyctx); @@ -1291,7 +1371,7 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = nx; - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_line(ctx, s->cy)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_insertcharacter, &ttyctx); @@ -1332,7 +1412,7 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = nx; - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_line(ctx, s->cy)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_deletecharacter, &ttyctx); @@ -1373,7 +1453,7 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = nx; - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_line(ctx, s->cy)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_clearcharacter, &ttyctx); @@ -1414,7 +1494,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = ny; - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_lines(ctx, s->cy, sy - s->cy)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_insertline, &ttyctx); @@ -1441,7 +1521,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = ny; - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_lines(ctx, s->cy, s->rlower + 1 - s->cy)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_insertline, &ttyctx); @@ -1458,7 +1538,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) struct screen *s = ctx->s; struct grid *gd = s->grid; struct tty_ctx ttyctx; - u_int sy = screen_size_y(s); + u_int sy = screen_size_y(s), ry; if (ny == 0) ny = 1; @@ -1482,7 +1562,8 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = ny; - if (s->mode & MODE_SYNC) + ry = s->rlower + 1 - s->rupper; + if (!screen_write_should_draw_lines(ctx, s->rupper, ry)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_deleteline, &ttyctx); @@ -1493,8 +1574,9 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) return; } - if (ny > s->rlower + 1 - s->cy) - ny = s->rlower + 1 - s->cy; + ry = s->rlower + 1 - s->cy; + if (ny > ry) + ny = ry; if (ny == 0) return; @@ -1509,7 +1591,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = ny; - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_lines(ctx, s->cy, ry)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_deleteline, &ttyctx); @@ -1643,29 +1725,34 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; struct tty_ctx ttyctx; + u_int ry; + + if (s->cy != s->rupper) { + if (s->cy > 0) + screen_write_set_cursor(ctx, -1, s->cy - 1); + return; + } - if (s->cy == s->rupper) { #ifdef ENABLE_SIXEL - if (image_free_all(s) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; + if (image_free_all(s) && ctx->wp != NULL) + ctx->wp->flags |= PANE_REDRAW; #endif - grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); - screen_write_collect_flush(ctx, 0, __func__); + grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); + screen_write_collect_flush(ctx, 0, __func__); - screen_write_initctx(ctx, &ttyctx, 1, 1); - ttyctx.bg = bg; + screen_write_initctx(ctx, &ttyctx, 1, 1); + ttyctx.bg = bg; - if (s->mode & MODE_SYNC) - return; - if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { - tty_write(tty_cmd_reverseindex, &ttyctx); - return; - } + ry = s->rlower + 1 - s->rupper; + if (!screen_write_should_draw_lines(ctx, s->rupper, ry)) + return; + if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { + tty_write(tty_cmd_reverseindex, &ttyctx); + return; + } - screen_write_redraw_pane(ctx, &ttyctx); - } else if (s->cy > 0) - screen_write_set_cursor(ctx, -1, s->cy - 1); + screen_write_redraw_pane(ctx, &ttyctx); } /* Set scroll region. */ @@ -1715,20 +1802,24 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) ctx->bg = bg; } - if (s->cy == s->rlower) { + if (s->cy != s->rlower) { + if (s->cy < screen_size_y(s) - 1) + screen_write_set_cursor(ctx, -1, s->cy + 1); + return; + } + #ifdef ENABLE_SIXEL - if (rlower == screen_size_y(s) - 1) - redraw = image_scroll_up(s, 1); - else - redraw = image_check_line(s, rupper, rlower - rupper); - if (redraw && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; + if (rlower == screen_size_y(s) - 1) + redraw = image_scroll_up(s, 1); + else + redraw = image_check_line(s, rupper, rlower - rupper); + if (redraw && ctx->wp != NULL) + ctx->wp->flags |= PANE_REDRAW; #endif - grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); - screen_write_collect_scroll(ctx, bg); - ctx->scrolled++; - } else if (s->cy < screen_size_y(s) - 1) - screen_write_set_cursor(ctx, -1, s->cy + 1); + + grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); + screen_write_collect_scroll(ctx, bg); + ctx->scrolled++; } /* Scroll up. */ @@ -1768,7 +1859,7 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) struct screen *s = ctx->s; struct grid *gd = s->grid; struct tty_ctx ttyctx; - u_int i; + u_int i, ry; screen_write_initctx(ctx, &ttyctx, 1, 1); ttyctx.bg = bg; @@ -1789,7 +1880,8 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = lines; - if (s->mode & MODE_SYNC) + ry = s->rlower + 1 - s->rupper; + if (!screen_write_should_draw_lines(ctx, s->rupper, ry)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_scrolldown, &ttyctx); @@ -1842,7 +1934,7 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); screen_write_collect_flush(ctx, 0, __func__); - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_lines(ctx, s->cy, sy - s->cy)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) { tty_write(tty_cmd_clearendofscreen, &ttyctx); @@ -1917,7 +2009,7 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) screen_write_collect_clear(ctx, 0, s->cy); screen_write_collect_flush(ctx, 0, __func__); - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_lines(ctx, 0, s->cy + 1)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) { tty_write(tty_cmd_clearstartofscreen, &ttyctx); @@ -1990,7 +2082,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) screen_write_collect_clear(ctx, 0, sy); - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_lines(ctx, s->cy, sy - s->cy)) return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) { tty_write(tty_cmd_clearscreen, &ttyctx); @@ -2307,12 +2399,21 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, const char *from) { struct screen *s = ctx->s; + struct window_pane *wp = ctx->wp; u_int y, cx, cy, items = 0; struct screen_write_citem *ci, *tmp; struct screen_write_cline *cl; - if (s->mode & MODE_SYNC) + if (wp != NULL && (wp->flags & (PANE_REDRAW|PANE_DROP))) goto discard; + if (s->mode & MODE_SYNC) { + for (y = 0; y < screen_size_y(s); y++) { + cl = &s->write_list[y]; + if (!TAILQ_EMPTY(&cl->items)) + screen_write_should_draw_line(ctx, y); + } + goto discard; + } if (ctx->scrolled != 0) { if (!screen_write_collect_flush_scrolled(ctx)) @@ -2650,12 +2751,12 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) if (s->mode & MODE_INSERT) { screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = width; - if (~s->mode & MODE_SYNC) + if (screen_write_should_draw_line(ctx, s->cy)) tty_write(tty_cmd_insertcharacter, &ttyctx); } /* If not writing, done now. */ - if (skip || s->mode & MODE_SYNC) + if (skip || !screen_write_should_draw_line(ctx, s->cy)) return; /* Do a full line redraw if needed. */ @@ -2675,7 +2776,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) for (i = 0, vis = 0; i < r->used; i++) vis += r->ranges[i].nx; if (vis >= width) { - if (~s->mode & MODE_SYNC) + if (screen_write_should_draw_line(ctx, s->cy)) tty_write(tty_cmd_cell, &ttyctx); return; } @@ -2685,7 +2786,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) * spaces in the visible regions. */ utf8_set(&tmp_gc.data, ' '); - if (s->mode & MODE_SYNC) + if (!screen_write_should_draw_line(ctx, s->cy)) return; for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; @@ -2828,7 +2929,7 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc) ttyctx.cell = &last; if (force_wide) ttyctx.flags |= TTY_CTX_CELL_INVALIDATE; - if (~s->mode & MODE_SYNC) + if (screen_write_should_draw_line(ctx, cy)) tty_write(tty_cmd_cell, &ttyctx); screen_write_set_cursor(ctx, cx, cy); diff --git a/screen.c b/screen.c index 8e513758e..f3fdfcbd4 100644 --- a/screen.c +++ b/screen.c @@ -123,7 +123,7 @@ screen_reinit(struct screen *s) s->saved_cy = UINT_MAX; screen_reset_tabs(s); - + grid_check_is_clear(s->grid); grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8); screen_clear_selection(s); diff --git a/server-client.c b/server-client.c index f170396ec..1b750e398 100644 --- a/server-client.c +++ b/server-client.c @@ -2048,19 +2048,18 @@ server_client_reset_state(struct client *c) if (!window_position_is_visible(r, cx)) cursor = 0; - if (window_pane_scrollbar_overlay_visible(wp)) { - sb_w = wp->scrollbar_style.width; - if (sb_w > wp->sx) - sb_w = wp->sx; - if (sb_w != 0 && - w->sb_pos == - PANE_SCROLLBARS_LEFT) { - if (s->cx < sb_w) + if (window_pane_scrollbar_overlay_visible(wp)) { + sb_w = wp->scrollbar_style.width; + if (sb_w > wp->sx) + sb_w = wp->sx; + if (sb_w != 0 && + w->sb_pos == PANE_SCROLLBARS_LEFT) { + if (s->cx < sb_w) + cursor = 0; + } else if (sb_w != 0 && + s->cx >= wp->sx - sb_w) cursor = 0; - } else if (sb_w != 0 && - s->cx >= wp->sx - sb_w) - cursor = 0; - } + } if (status_at_line(c) == 0) cy += status_line_size(c); @@ -2089,10 +2088,10 @@ server_client_reset_state(struct client *c) mode |= MODE_MOUSE_ALL; } } - if (options_get_number(oo, "focus-follows-mouse") || - w->sb == PANE_SCROLLBARS_MODAL || - w->sb == PANE_SCROLLBARS_AUTOHIDE) - mode |= MODE_MOUSE_ALL; + if (options_get_number(oo, "focus-follows-mouse") || + w->sb == PANE_SCROLLBARS_MODAL || + w->sb == PANE_SCROLLBARS_AUTOHIDE) + mode |= MODE_MOUSE_ALL; else if (~mode & MODE_MOUSE_ALL) mode |= MODE_MOUSE_BUTTON; } diff --git a/spawn.c b/spawn.c index 59e107292..91010d775 100644 --- a/spawn.c +++ b/spawn.c @@ -104,7 +104,7 @@ spawn_window(struct spawn_context *sc, char **cause) sc->wp0 = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, sc->wp0, entry); - layout_free(w); + layout_free(w, 0); window_destroy_panes(w); TAILQ_INSERT_HEAD(&w->panes, sc->wp0, entry); diff --git a/tmux.1 b/tmux.1 index ea23cbaa8..1eb731ca6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6777,6 +6777,12 @@ results in replaces a .Nm colour by its six-digit hexadecimal RGB value. +If an argument of +.Ql f +or +.Ql b +is given, it will instead produce the SGR escape sequence to set the foreground +or background colour respectively. .Pp A limit may be placed on the length of the resultant string by prefixing it by an diff --git a/tmux.h b/tmux.h index 1175c3669..69b8f62b4 100644 --- a/tmux.h +++ b/tmux.h @@ -1295,11 +1295,8 @@ struct window_pane { #define PANE_UNSEENCHANGES 0x4000 #define PANE_REDRAWSCROLLBAR 0x8000 - /* Last floating position/size, saved when the pane is tiled. */ - int saved_float_xoff; - int saved_float_yoff; - u_int saved_float_sx; - u_int saved_float_sy; + bitstr_t *sync_dirty; + u_int sync_dirty_size; u_int sb_slider_y; u_int sb_slider_h; @@ -3307,6 +3304,7 @@ void colour_split_rgb(int, u_char *, u_char *, u_char *); int colour_force_rgb(int); int colour_dim(int, u_int); const char *colour_tostring(int); +const char *colour_toescape(struct client *, int, int); enum client_theme colour_totheme(int); int colour_fromstring(const char *); const char *colour_theme_option(u_int, enum client_theme); @@ -3331,6 +3329,7 @@ bitstr_t *fuzzy_match(const char *, const char *, u_int, u_int *); /* grid.c */ extern const struct grid_cell grid_default_cell; +void grid_check_is_clear(struct grid *); void grid_empty_line(struct grid *, u_int, u_int); void grid_set_tab(struct grid_cell *, u_int); int grid_cells_equal(const struct grid_cell *, const struct grid_cell *); @@ -3448,6 +3447,7 @@ void screen_write_mode_set(struct screen_write_ctx *, int); void screen_write_mode_clear(struct screen_write_ctx *, int); void screen_write_start_sync(struct window_pane *); void screen_write_stop_sync(struct window_pane *); +void screen_write_clear_dirty(struct window_pane *); void screen_write_cursorup(struct screen_write_ctx *, u_int); void screen_write_cursordown(struct screen_write_ctx *, u_int); void screen_write_cursorright(struct screen_write_ctx *, u_int); @@ -3683,7 +3683,7 @@ struct visible_ranges *window_visible_ranges(struct window_pane *, int, int, /* layout.c */ u_int layout_count_cells(struct layout_cell *); struct layout_cell *layout_create_cell(struct layout_cell *); -void layout_free_cell(struct layout_cell *); +void layout_free_cell(struct layout_cell *, int); void layout_print_cell(struct layout_cell *, const char *, u_int); void layout_destroy_cell(struct window *, struct layout_cell *, struct layout_cell **); @@ -3706,7 +3706,7 @@ void layout_resize_set_size(struct window *, struct layout_cell *, enum layout_type, u_int); struct layout_cell *layout_cell_get_neighbour(struct layout_cell *); void layout_init(struct window *, struct window_pane *); -void layout_free(struct window *); +void layout_free(struct window *, int); void layout_resize(struct window *, u_int, u_int); void layout_resize_pane(struct window_pane *, enum layout_type, int, int); diff --git a/tty-features.c b/tty-features.c index d8b3ce3b4..b1f8032d6 100644 --- a/tty-features.c +++ b/tty-features.c @@ -583,6 +583,19 @@ tty_default_features(int *feat, const char *name, u_int version) "hyperlinks," "usstyle" }, + { .name = "ghostty", + .features = TTY_FEATURES_BASE_MODERN_XTERM "," + "ccolour," + "cstyle," + "extkeys," + "focus," + "overline," + "hyperlinks," + "osc7," + "sync," + "usstyle," + "progressbar" + }, { .name = "XTerm", /* * xterm also supports DECSLRM and DECFRA, but they can be diff --git a/tty-keys.c b/tty-keys.c index d3a93f10f..f48fb1b71 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1663,6 +1663,8 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, tty_default_features(features, "foot", 0); else if (strncmp(tmp, "WezTerm ", 7) == 0) tty_default_features(features, "WezTerm", 0); + else if (strncmp(tmp, "ghostty ", 8) == 0) + tty_default_features(features, "ghostty", 0); log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); free(c->term_type); diff --git a/window.c b/window.c index 45206cd61..f5e9b6734 100644 --- a/window.c +++ b/window.c @@ -357,8 +357,8 @@ window_destroy(struct window *w) window_unzoom(w, 0); RB_REMOVE(windows, &windows, w); - layout_free_cell(w->layout_root); - layout_free_cell(w->saved_layout_root); + layout_free_cell(w->layout_root, 0); + layout_free_cell(w->saved_layout_root, 0); free(w->old_layout); window_destroy_panes(w); @@ -795,7 +795,7 @@ window_unzoom(struct window *w, int notify) return (-1); w->flags &= ~WINDOW_ZOOMED; - layout_free(w); + layout_free(w, 0); w->layout_root = w->saved_layout_root; w->saved_layout_root = NULL; @@ -1238,6 +1238,7 @@ window_pane_destroy(struct window_pane *wp) window_pane_clear_prompt(wp); window_pane_free_modes(wp); + screen_write_clear_dirty(wp); free(wp->searchstr); if (wp->fd != -1) {