From 26285e183e74739b9eae4cabd44b602376263f02 Mon Sep 17 00:00:00 2001 From: jsg Date: Tue, 16 Jun 2026 05:01:56 +0000 Subject: [PATCH 01/10] botton -> bottom --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 2f092030..535dcb73 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3368,7 +3368,7 @@ which may be one of: .It Li "top-left-centre" Ta "Centre of top-left quadrant" .It Li "top-right-centre" Ta "Centre of top-right quadrant" .It Li "bottom-left-centre" Ta "Centre of bottom-left quadrant" -.It Li "bottom-right-centre" Ta "Centre of botton-right quadrant" +.It Li "bottom-right-centre" Ta "Centre of bottom-right quadrant" .It Li "front" Ta "Front of floating panes" .It Li "back" Ta "Back of floating panes" .It Li "forward" Ta "Forward one floating pane" From 0890df5891cff2b11d6ff9edddb2d0b0bf3a6901 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jun 2026 07:06:32 +0000 Subject: [PATCH 02/10] Add layout_cell_is_tiled and layout_cell_has_tiled_child helper functions, from Dane Jensen. --- layout.c | 95 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 20 deletions(-) diff --git a/layout.c b/layout.c index 0e8a2a2a..1eb36477 100644 --- a/layout.c +++ b/layout.c @@ -26,11 +26,18 @@ /* * The window layout is a tree of cells each of which can be one of: a * left-right container for a list of cells, a top-bottom container for a list - * of cells, or a container for a window pane. + * of cells, or a container for a window pane. 'Node' will be used to refer to + * a cell which contains a list of cells, and 'leaf' to refer to a cell that + * contains a window pane. A leaf is considered to be 'tiled' if it is to be + * drawn as a part of the tiled layout. A 'neighbour' is a sibling that is also + * tiled. A cell's 'split' size refers to the side that is shortened when + * splitting it, determined by the parent's type. * * Each window has a pointer to the root of its layout tree (containing its * panes), every pane has a pointer back to the cell containing it, and each - * cell a pointer to its parent cell. + * cell a pointer to its parent cell. Every cell has a position in the root + * layout tree. This position is retained through cell state changes such as + * floating or hiding. */ static u_int layout_resize_check(struct window *, struct layout_cell *, @@ -240,6 +247,49 @@ layout_fix_zindexes(struct window *w, struct layout_cell *lc) } } +static int +layout_cell_is_tiled(struct layout_cell *lc) +{ + int is_leaf = lc->type == LAYOUT_WINDOWPANE; + int is_floating = lc->flags & LAYOUT_CELL_FLOATING; + + return is_leaf && !is_floating; +} + +static int +layout_cell_has_tiled_child(struct layout_cell *lc) +{ + struct layout_cell *lcchild; + + if (lc->type == LAYOUT_WINDOWPANE) + return (0); + + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + if (layout_cell_is_tiled(lcchild) || + layout_cell_has_tiled_child(lcchild)) + return (1); + } + return (0); +} + +static int +layout_cell_is_first_tiled(struct layout_cell *lc) +{ + struct layout_cell *lcchild, *lcparent = lc->parent; + + if (lcparent == NULL) + return (layout_cell_is_tiled(lc)); + + TAILQ_FOREACH(lcchild, &lcparent->cells, entry) { + if (layout_cell_is_tiled(lcchild) || + layout_cell_has_tiled_child(lcchild)) + break; + } + + return (lcchild == lc); +} + + /* Fix cell offsets for a child cell. */ static void layout_fix_offsets1(struct layout_cell *lc) @@ -250,7 +300,8 @@ layout_fix_offsets1(struct layout_cell *lc) if (lc->type == LAYOUT_LEFTRIGHT) { xoff = lc->xoff; TAILQ_FOREACH(lcchild, &lc->cells, entry) { - if (lcchild->flags & LAYOUT_CELL_FLOATING) + if (!layout_cell_is_tiled(lcchild) && + !layout_cell_has_tiled_child(lcchild)) continue; lcchild->xoff = xoff; lcchild->yoff = lc->yoff; @@ -261,7 +312,8 @@ layout_fix_offsets1(struct layout_cell *lc) } else { yoff = lc->yoff; TAILQ_FOREACH(lcchild, &lc->cells, entry) { - if (lcchild->flags & LAYOUT_CELL_FLOATING) + if (!layout_cell_is_tiled(lcchild) && + !layout_cell_has_tiled_child(lcchild)) continue; lcchild->xoff = lc->xoff; lcchild->yoff = yoff; @@ -292,22 +344,15 @@ layout_fix_offsets(struct window *w) static int layout_cell_is_top(struct window *w, struct layout_cell *lc) { - struct layout_cell *next, *edge; + struct layout_cell *next; while (lc != w->layout_root) { next = lc->parent; if (next == NULL) return (0); - if (next->type == LAYOUT_TOPBOTTOM) { - edge = TAILQ_FIRST(&next->cells); - while (edge != NULL) { - if (~edge->flags & LAYOUT_CELL_FLOATING) - break; - edge = TAILQ_NEXT(edge, entry); - } - if (lc != edge) - return (0); - } + if (next->type == LAYOUT_TOPBOTTOM && + !layout_cell_is_first_tiled(lc)) + return (0); lc = next; } return (1); @@ -505,13 +550,20 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc, /* Child cell runs in a different direction. */ if (lc->type != type) { TAILQ_FOREACH(lcchild, &lc->cells, entry) { - if (lcchild->flags & LAYOUT_CELL_FLOATING) + if (!layout_cell_is_tiled(lcchild) && + !layout_cell_has_tiled_child(lcchild)) continue; layout_resize_adjust(w, lcchild, type, change); } return; } + /* + * If a node doesn't contain any tiled cells, there is nothing to do. + */ + if (!layout_cell_has_tiled_child(lc)) + return; + /* * Child cell runs in the same direction. Adjust each child equally * until no further change is possible. @@ -520,7 +572,8 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc, TAILQ_FOREACH(lcchild, &lc->cells, entry) { if (change == 0) break; - if (lcchild->flags & LAYOUT_CELL_FLOATING) + if (!layout_cell_is_tiled(lcchild) && + !layout_cell_has_tiled_child(lcchild)) continue; if (change > 0) { layout_resize_adjust(w, lcchild, type, 1); @@ -582,7 +635,7 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc, lc->parent = lcparent->parent; if (lc->parent == NULL) { - if (~lc->flags & LAYOUT_CELL_FLOATING) { + if (!layout_cell_is_tiled(lc)) { lc->xoff = 0; lc->yoff = 0; } @@ -1009,7 +1062,8 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc) count = 0; previous = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) { - if (lcchild->flags & LAYOUT_CELL_FLOATING) + if (!layout_cell_is_tiled(lc) && + !layout_cell_has_tiled_child(lc)) continue; count++; if (lc->type == LAYOUT_LEFTRIGHT) @@ -1029,7 +1083,8 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc) /* Resize children into the new size. */ idx = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) { - if (lcchild->flags & LAYOUT_CELL_FLOATING) + if (!layout_cell_is_tiled(lc) && + !layout_cell_has_tiled_child(lc)) continue; if (lc->type == LAYOUT_TOPBOTTOM) { lcchild->sx = lc->sx; From 30e133d36aa158850f1b3961345c1515d4abf0fa Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jun 2026 07:07:49 +0000 Subject: [PATCH 03/10] Fix default window check in break-pane, from Dane Jensen. --- cmd-break-pane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 0d8d83fd..da15e8b5 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -114,7 +114,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) w->active = wp; w->latest = tc; - if (name != NULL) { + if (name == NULL) { newname = default_window_name(w); window_set_name(w, newname, WINDOW_NAME_FORBID); free(newname); From 38dc3ec0daad24776c0f8102edc92e6cf9422059 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 16 Jun 2026 09:48:04 +0100 Subject: [PATCH 04/10] Prefer libtinfow if it is available, GitHub issue 5224 from Lars Wendler. --- configure.ac | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index 093d855b..e2a5f169 100644 --- a/configure.ac +++ b/configure.ac @@ -297,24 +297,24 @@ fi # Look for ncurses or curses. Try pkg-config first then directly for the # library. PKG_CHECK_MODULES( - LIBTINFO, - tinfo, + LIBTINFOW, + tinfow, [ - AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS" - CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS" - LIBS="$LIBTINFO_LIBS $LIBS" + AM_CPPFLAGS="$LIBTINFOW_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$LIBTINFOW_CFLAGS $SAVED_CPPFLAGS" + LIBS="$LIBTINFOW_LIBS $LIBS" found_ncurses=yes ], found_ncurses=no ) if test "x$found_ncurses" = xno; then PKG_CHECK_MODULES( - LIBNCURSES, - ncurses, + LIBTINFO, + tinfo, [ - AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS" - CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS" - LIBS="$LIBNCURSES_LIBS $LIBS" + AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS" + LIBS="$LIBTINFO_LIBS $LIBS" found_ncurses=yes ], found_ncurses=no @@ -333,10 +333,23 @@ if test "x$found_ncurses" = xno; then found_ncurses=no ) fi +if test "x$found_ncurses" = xno; then + PKG_CHECK_MODULES( + LIBNCURSES, + ncurses, + [ + AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS" + CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS" + LIBS="$LIBNCURSES_LIBS $LIBS" + found_ncurses=yes + ], + found_ncurses=no + ) +fi if test "x$found_ncurses" = xno; then AC_SEARCH_LIBS( setupterm, - [tinfo terminfo ncurses ncursesw], + [tinfow tinfo terminfo ncursesw ncurses], found_ncurses=yes, found_ncurses=no ) From d2c23773ff8902611b2d56346868a90558203d8b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jun 2026 08:53:14 +0000 Subject: [PATCH 05/10] Fix missing border when drawing floating panes. From Michael Grant. --- screen-redraw.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 022f7bf0..a9a62680 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -129,7 +129,7 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, int hsplit = 0, vsplit = 0; int pane_status = window_pane_get_pane_status(wp); int pane_scrollbars = ctx->pane_scrollbars, sb_w = 0; - int sb_pos, sx = wp->sx, sy = wp->sy; + int sb_pos, sx = wp->sx, sy = wp->sy, left, right; enum layout_type split_type; if (pane_scrollbars != 0) @@ -151,20 +151,19 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, /* Floating pane borders. */ if (window_pane_is_floating(wp)) { + left = wp->xoff - 1; + right = wp->xoff + sx; + if (sb_pos == PANE_SCROLLBARS_LEFT) + left -= sb_w; + else + right += sb_w; if (py >= wp->yoff - 1 && py <= wp->yoff + sy) { - if (sb_pos == PANE_SCROLLBARS_LEFT) { - if (px == wp->xoff - 1 - sb_w) - return (SCREEN_REDRAW_BORDER_LEFT); - if (px == wp->xoff + sx) - return (SCREEN_REDRAW_BORDER_RIGHT); - } else { /* PANE_SCROLLBARS_RIGHT or none. */ - if (px == wp->xoff - 1) - return (SCREEN_REDRAW_BORDER_LEFT); - if (px == wp->xoff + sx + sb_w) - return (SCREEN_REDRAW_BORDER_RIGHT); - } + if (px == left) + return (SCREEN_REDRAW_BORDER_LEFT); + if (px == right) + return (SCREEN_REDRAW_BORDER_RIGHT); } - if (px >= wp->xoff && px <= wp->xoff + sx) { + if (px > left && px <= right) { if (py == wp->yoff - 1) return (SCREEN_REDRAW_BORDER_TOP); if (py == wp->yoff + sy) From bdc01f44de7d7a7232970548d1878fb6749f2de0 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jun 2026 08:57:07 +0000 Subject: [PATCH 06/10] Fix resizing floating pane with a left scrollbar, from Michael Grant. --- cmd-resize-pane.c | 24 ++++++++++++++++++------ server-client.c | 22 ++++++++++++++++------ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 72f871d7..5ea3b03b 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -226,6 +226,7 @@ cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m) struct window_pane *wp; struct layout_cell *lc; int y, ly, x, lx, sx, sy, new_sx, new_sy; + int scrollbars, sb_pos, left, right; int new_xoff, new_yoff, resizes = 0; wp = cmd_mouse_pane(m, NULL, &wl); @@ -237,6 +238,17 @@ cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m) lc = wp->layout_cell; sx = wp->sx; sy = wp->sy; + scrollbars = options_get_number(w->options, "pane-scrollbars"); + sb_pos = options_get_number(w->options, "pane-scrollbars-position"); + left = wp->xoff - 1; + right = wp->xoff + sx; + if (window_pane_show_scrollbar(wp, scrollbars) && + sb_pos == PANE_SCROLLBARS_LEFT) { + left -= wp->scrollbar_style.width + wp->scrollbar_style.pad; + } else if (window_pane_show_scrollbar(wp, scrollbars) && + sb_pos == PANE_SCROLLBARS_RIGHT) { + right += wp->scrollbar_style.width + wp->scrollbar_style.pad; + } y = m->y + m->oy; x = m->x + m->ox; if (m->statusat == 0 && y >= (int)m->statuslines) @@ -249,7 +261,7 @@ cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m) else if (m->statusat > 0 && ly >= m->statusat) ly = m->statusat - 1; - if ((lx == wp->xoff - 1 || lx == wp->xoff) && ly == wp->yoff - 1) { + if ((lx == left || lx == left + 1) && ly == wp->yoff - 1) { /* Top left corner. */ new_sx = lc->sx + (lx - x); if (new_sx < PANE_MINIMUM) @@ -261,7 +273,7 @@ cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m) new_yoff = y + 1; layout_set_size(lc, new_sx, new_sy, new_xoff, new_yoff); resizes++; - } else if ((lx == wp->xoff + sx + 1 || lx == wp->xoff + sx) && + } else if ((lx == right + 1 || lx == right) && ly == wp->yoff - 1) { /* Top right corner. */ new_sx = x - lc->xoff; @@ -273,7 +285,7 @@ cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m) new_yoff = y + 1; layout_set_size(lc, new_sx, new_sy, lc->xoff, new_yoff); resizes++; - } else if ((lx == wp->xoff - 1 || lx == wp->xoff) && + } else if ((lx == left || lx == left + 1) && ly == wp->yoff + sy) { /* Bottom left corner. */ new_sx = lc->sx + (lx - x); @@ -285,7 +297,7 @@ cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m) new_xoff = x + 1; layout_set_size(lc, new_sx, new_sy, new_xoff, lc->yoff); resizes++; - } else if ((lx == wp->xoff + sx + 1 || lx == wp->xoff + sx) && + } else if ((lx == right + 1 || lx == right) && ly == wp->yoff + sy) { /* Bottom right corner. */ new_sx = x - lc->xoff; @@ -296,14 +308,14 @@ cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m) new_sy = PANE_MINIMUM; layout_set_size(lc, new_sx, new_sy, lc->xoff, lc->yoff); resizes++; - } else if (lx == wp->xoff + sx + 1) { + } else if (lx == right) { /* Right border. */ new_sx = x - lc->xoff; if (new_sx < PANE_MINIMUM) return; layout_set_size(lc, new_sx, lc->sy, lc->xoff, lc->yoff); resizes++; - } else if (lx == wp->xoff - 1) { + } else if (lx == left) { /* Left border. */ new_sx = lc->sx + (lx - x); if (new_sx < PANE_MINIMUM) diff --git a/server-client.c b/server-client.c index 2bb031bd..579ed7d7 100644 --- a/server-client.c +++ b/server-client.c @@ -631,6 +631,9 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py, pane_status_line = wp->yoff + wp->sy; else pane_status_line = -1; /* not used */ + bdr_left = wp->xoff - 1; + if (sb_pos == PANE_SCROLLBARS_LEFT) + bdr_left -= sb_pad + sb_w; /* Check if point is within the pane or scrollbar. */ if (((pane_status != PANE_STATUS_OFF && @@ -661,7 +664,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py, return (KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN); } else if (window_pane_is_floating(wp) && window_pane_get_pane_lines(wp) != PANE_LINES_NONE && - (px == wp->xoff - 1 || + (px == bdr_left || py == wp->yoff - 1 || py == wp->yoff + (int)wp->sy)) { /* Floating pane left, bottom or top border. */ @@ -679,11 +682,20 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py, if (window_pane_is_floating(fwp) && window_pane_get_pane_lines(fwp) == PANE_LINES_NONE) continue; + if (window_pane_show_scrollbar(fwp, sb)) { + sb_w = fwp->scrollbar_style.width; + sb_pad = fwp->scrollbar_style.pad; + } else { + sb_w = 0; + sb_pad = 0; + } bdr_top = fwp->yoff - 1; bdr_bottom = fwp->yoff + fwp->sy; - if (sb_pos == PANE_SCROLLBARS_LEFT) + bdr_left = fwp->xoff - 1; + if (sb_pos == PANE_SCROLLBARS_LEFT) { + bdr_left -= sb_pad + sb_w; bdr_right = fwp->xoff + fwp->sx; - else { + } else { /* PANE_SCROLLBARS_RIGHT or none. */ bdr_right = fwp->xoff + fwp->sx + sb_pad + sb_w; } @@ -693,13 +705,11 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py, break; if (window_pane_is_floating(wp)) { /* Floating pane, check left border. */ - bdr_left = fwp->xoff - 1; if (px == bdr_left) break; } } - if (px >= fwp->xoff - 1 && - px <= fwp->xoff + (int)fwp->sx) { + if (px >= bdr_left && px <= fwp->xoff + (int)fwp->sx) { bdr_bottom = fwp->yoff + fwp->sy; if (py == bdr_bottom) break; From fcbb6a7384002b2a203262a4ac7deaa827647ea7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jun 2026 09:00:25 +0000 Subject: [PATCH 07/10] Initialize wname so it we are not freeing garbage if it is not used. --- cmd-new-window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-new-window.c b/cmd-new-window.c index c7a203e2..c65745a2 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -61,7 +61,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) struct session *s = target->s; struct winlink *wl = target->wl, *new_wl = NULL; int idx = target->idx, before; - char *cause = NULL, *cp, *expanded, *wname; + char *cause = NULL, *cp, *expanded, *wname = NULL; const char *template, *name; struct cmd_find_state fs; struct args_value *av; From 4b772fd783b6c52154afa3b733d1c33aff4e7480 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jun 2026 09:28:17 +0000 Subject: [PATCH 08/10] Allow rectangle selection to extend past end of current line to behave the same as vi with virtualedit=block set. From Mark Kelly in GitHub issue 5227. --- window-copy.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/window-copy.c b/window-copy.c index f06b179b..272e2e9e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -5403,8 +5403,12 @@ window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) u_int maxx; int allow_onemore; - allow_onemore = (data->screen.sel != NULL && data->rectflag); - if (cy < screen_size_y(s)) { + /* + * Allow rectangle selection to extend past end of current line to + * behave the same as vi with virtualedit=block set. + */ + if (!data->rectflag && cy < screen_size_y(s)) { + allow_onemore = (data->screen.sel != NULL && data->rectflag); py = screen_hsize(data->backing) + cy - data->oy; maxx = window_copy_cursor_limit(wme, py, allow_onemore); if (cx > maxx) From 9ad75e1dc3276d7fb6c7415fc79df407182d2f0f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 16 Jun 2026 10:47:35 +0000 Subject: [PATCH 09/10] Rename the visible ranges functions since they really relate to windows and put them nto a new file. --- Makefile | 1 + cmd-display-panes.c | 5 +- screen-redraw.c | 207 +--------------------------------------- screen-write.c | 31 +++--- server-client.c | 4 +- tmux.h | 8 +- window-visible.c | 224 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 251 insertions(+), 229 deletions(-) create mode 100644 window-visible.c diff --git a/Makefile b/Makefile index fa87707b..ce873ba0 100644 --- a/Makefile +++ b/Makefile @@ -130,6 +130,7 @@ SRCS= alerts.c \ window-clock.c \ window-copy.c \ window-customize.c \ + window-visible.c \ window-tree.c \ window.c \ xmalloc.c diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 53e7989b..56e13333 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -65,8 +65,7 @@ cmd_display_panes_put(struct screen_redraw_ctx *ctx, struct visible_range *ri; u_int i, j; - r = screen_redraw_get_visible_ranges(wp, ctx->ox + cx, ctx->oy + cy, - len, NULL); + r = window_visible_ranges(wp, ctx->ox + cx, ctx->oy + cy, len, NULL); for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; for (j = ri->px; j < ri->px + ri->nx; j++) { @@ -103,7 +102,7 @@ cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx, screen_write_stop(&sctx); free(expanded); - r = screen_redraw_get_visible_ranges(wp, px, wp->yoff, sx, NULL); + r = window_visible_ranges(wp, px, wp->yoff, sx, NULL); for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; tty_draw_line(tty, &screen, ri->px - px, 0, ri->nx, diff --git a/screen-redraw.c b/screen-redraw.c index a9a62680..11e24369 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -734,7 +734,7 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) } r = tty_check_overlay_range(tty, x, yoff, width); - r = screen_redraw_get_visible_ranges(wp, x, yoff, width, r); + r = window_visible_ranges(wp, x, yoff, width, r); if (ctx->statustop) yoff += ctx->statuslines; for (i = 0; i < r->used; i++) { @@ -1125,205 +1125,6 @@ screen_redraw_draw_status(struct screen_redraw_ctx *ctx) tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i, NULL); } -/* - * Check if a single character is within a visible range (not obscured by a - * floating pane). - */ -int -screen_redraw_is_visible(struct visible_ranges *r, u_int px) -{ - u_int i; - struct visible_range *ri; - - if (r == NULL) - return (1); - for (i = 0; i < r->used; i++) { - ri = &r->ranges[i]; - if (ri->nx != 0 && px >= ri->px && px < ri->px + ri->nx) - return (1); - } - return (0); -} - -/* - * Construct ranges array for the line at starting at px,py of width cells of - * base_wp that are unobsructed. All ranges are in window coordinates. - */ -struct visible_ranges * -screen_redraw_get_visible_ranges(struct window_pane *base_wp, int px, - int py, u_int width, struct visible_ranges *r) -{ - struct window_pane *wp; - struct window *w; - struct visible_range *ri; - static struct visible_ranges sr = { NULL, 0, 0 }; - int found_self, sb, sb_w, sb_pos, no_border; - int lb, rb, tb, bb, sx, ex; - u_int i, s; - - if (py < 0 || width == 0) - goto empty; - if (px < 0) { - if ((u_int)-px >= width) - goto empty; - width -= (u_int)-px; - px = 0; - } - - if (base_wp == NULL) { - if (r != NULL) - return (r); - if (sr.ranges == NULL) - sr.ranges = xcalloc(1, sizeof *sr.ranges); - sr.ranges[0].px = px; - sr.ranges[0].nx = width; - sr.size = 1; - sr.used = 1; - return (&sr); - } - - w = base_wp->window; - if ((u_int)py >= w->sy) - goto empty; - if (px + width > w->sx) - width = w->sx - px; - - if (r == NULL) { - /* Start with the entire width of the range. */ - server_client_ensure_ranges(&base_wp->r, 1); - r = &base_wp->r; - r->ranges[0].px = px; - r->ranges[0].nx = width; - r->used = 1; - } - - sb = options_get_number(w->options, "pane-scrollbars"); - sb_pos = options_get_number(w->options, "pane-scrollbars-position"); - - found_self = 0; - TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) { - if (wp == base_wp) { - found_self = 1; - continue; - } - - if (window_pane_is_floating(wp) && - window_pane_get_pane_lines(wp) == PANE_LINES_NONE) - no_border = 1; - else - no_border = 0; - if (no_border) { - tb = wp->yoff; - bb = wp->yoff + (int)wp->sy - 1; - } else { - tb = wp->yoff > 0 ? wp->yoff - 1 : 0; - bb = wp->yoff + (int)wp->sy; - } - if (!found_self || - !window_pane_is_visible(wp) || - py < tb || - py > bb) - continue; - if (!window_pane_is_floating(wp) && (py == tb || py == bb)) - continue; - - sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; - if (!window_pane_show_scrollbar(wp, sb)) - sb_w = sb_pos = 0; - - for (i = 0; i < r->used; i++) { - ri = &r->ranges[i]; - if (no_border) { - lb = wp->xoff; - rb = wp->xoff + (int)wp->sx - 1; - } else if (sb_pos == PANE_SCROLLBARS_LEFT) { - if (wp->xoff > sb_w) - lb = wp->xoff - 1 - sb_w; - else - lb = 0; - } else { /* PANE_SCROLLBARS_RIGHT or none. */ - if (wp->xoff > 0) - lb = wp->xoff - 1; - else - lb = 0; - } - if (!no_border) { - if (sb_pos == PANE_SCROLLBARS_LEFT) - rb = wp->xoff + (int)wp->sx; - else /* PANE_SCROLLBARS_RIGHT or none. */ - rb = wp->xoff + (int)wp->sx + sb_w; - } - if (lb < 0) - lb = 0; - if (rb < 0) - continue; - if (no_border && rb >= (int)w->sx) - rb = w->sx - 1; - else if (!no_border && rb > (int)w->sx) - rb = w->sx - 1; - - sx = ri->px; - ex = sx + ri->nx - 1; - if (lb > sx && lb <= ex && rb > ex) { - /* - * If the left edge of floating pane falls - * inside this range and right edge covers up - * to right of range, then shrink left edge of - * range. - */ - ri->nx = lb - sx; - } else if (rb >= sx && rb <= ex && lb <= sx) { - /* - * Else if the right edge of floating pane falls - * inside of this range and left edge covers - * the left of range, then move px forward to - * right edge of pane. - */ - ri->nx = ex - rb; - ri->px = rb + 1; - } else if (lb > sx && rb <= ex) { - /* - * Else if pane fully inside range then split - * into 2 ranges. - */ - server_client_ensure_ranges(r, r->used + 1); - for (s = r->used; s > i; s--) { - memcpy(&r->ranges[s], &r->ranges[s - 1], - sizeof *r->ranges); - } - ri = &r->ranges[i]; - r->ranges[i + 1].px = rb + 1; - r->ranges[i + 1].nx = ex - rb; - /* ri->px was copied, unchanged. */ - ri->nx = lb - sx; - r->used++; - } else if (lb <= sx && rb > ex) { - /* - * If floating pane completely covers this range - * then delete it (make it 0 length). - */ - ri->nx = 0; - } else { - /* - * The range is already obscured, do - * nothing. - */ - } - } - } - return (r); - -empty: - if (r == NULL) { - if (sr.ranges == NULL) - sr.ranges = xcalloc(1, sizeof *sr.ranges); - sr.size = 1; - sr.used = 0; - return (&sr); - } - r->used = 0; - return (r); -} /* Draw one pane. */ static void @@ -1418,7 +1219,7 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) /* Get visible ranges of line before we draw it. */ r = tty_check_overlay_range(tty, wx, wy, width); - r = screen_redraw_get_visible_ranges(wp, wx, wy, width, r); + r = window_visible_ranges(wp, wx, wy, width, r); for (k = 0; k < r->used; k++) { ri = &r->ranges[k]; if (ri->nx == 0) @@ -1588,7 +1389,7 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx, wy = sb_wy + j; /* window y coordinate */ py = sb_tty_y + j; /* tty y coordinate */ r = tty_check_overlay_range(tty, sb_x, wy, imax); - r = screen_redraw_get_visible_ranges(wp, sb_x, wy, imax, r); + r = window_visible_ranges(wp, sb_x, wy, imax, r); for (i = imin; i < imax; i++) { px = sb_x + ox + i; /* tty x coordinate */ wx = sb_x + i; /* window x coordinate */ @@ -1596,7 +1397,7 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx, px >= sx || px < 0 || wy < yoff - 1 || py >= sy || py < 0 || - !screen_redraw_is_visible(r, wx)) + !window_position_is_visible(r, wx)) continue; tty_cursor(tty, px, py); if ((sb_pos == PANE_SCROLLBARS_LEFT && diff --git a/screen-write.c b/screen-write.c index ff1a713f..df5b33d6 100644 --- a/screen-write.c +++ b/screen-write.c @@ -644,8 +644,8 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, break; s->cx = cx; screen_write_initctx(ctx, &ttyctx, 0, 0); - r = screen_redraw_get_visible_ranges(wp, xoff + s->cx, - s->cy + yoff, nx, NULL); + r = window_visible_ranges(wp, xoff + s->cx, s->cy + yoff, nx, + NULL); for (xx = px; xx < px + nx; xx++) { gl = grid_get_line(gd, yy); sgl = grid_get_line(s->grid, s->cy); @@ -657,7 +657,7 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, break; grid_view_set_cell(s->grid, s->cx, s->cy, &gc); - if (!screen_redraw_is_visible(r, xoff + s->cx)) + if (!window_position_is_visible(r, xoff + s->cx)) break; ttyctx.cell = &gc; ttyctx.flags &= (TTY_CTX_OVERLAY_SYNC|TTY_CTX_SYNC); @@ -1162,7 +1162,7 @@ screen_write_redraw_line(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, if (s->mode & MODE_SYNC) return; - r = screen_redraw_get_visible_ranges(wp, xoff, yoff + yy, sx, NULL); + r = window_visible_ranges(wp, xoff, yoff + yy, sx, NULL); for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; if (ri->nx == 0) @@ -1774,8 +1774,8 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) /* First line (containing the cursor). */ if (s->cx <= sx - 1) { - r = screen_redraw_get_visible_ranges(ctx->wp, xoff + s->cx, - yoff + s->cy, sx - s->cx, NULL); + r = window_visible_ranges(ctx->wp, xoff + s->cx, yoff + s->cy, + sx - s->cx, NULL); for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; if (ri->nx == 0) @@ -1788,8 +1788,7 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) /* Below cursor to bottom. */ for (y = s->cy + 1; y < sy; y++) { screen_write_set_cursor(ctx, 0, y); - r = screen_redraw_get_visible_ranges(ctx->wp, xoff, yoff + y, - sx, NULL); + r = window_visible_ranges(ctx->wp, xoff, yoff + y, sx, NULL); for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; if (ri->nx == 0) @@ -1846,8 +1845,7 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) /* Top to above the cursor. */ for (y = 0; y < s->cy; y++) { screen_write_set_cursor(ctx, 0, y); - r = screen_redraw_get_visible_ranges(ctx->wp, xoff, yoff + y, - sx, NULL); + r = window_visible_ranges(ctx->wp, xoff, yoff + y, sx, NULL); for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; if (ri->nx == 0) @@ -1859,8 +1857,7 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) /* Last line (containing the cursor). */ screen_write_set_cursor(ctx, 0, s->cy); - r = screen_redraw_get_visible_ranges(ctx->wp, xoff, yoff + ocy, - s->cx + 1, NULL); + r = window_visible_ranges(ctx->wp, xoff, yoff + ocy, s->cx + 1, NULL); for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; if (ri->nx == 0) @@ -1916,8 +1913,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) /* Clear every line. */ for (y = 0; y < sy; y++) { screen_write_set_cursor(ctx, 0, y); - r = screen_redraw_get_visible_ranges(ctx->wp, xoff, yoff + y, - sx, NULL); + r = window_visible_ranges(ctx->wp, xoff, yoff + y, sx, NULL); for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; if (ri->nx == 0) @@ -2138,7 +2134,7 @@ screen_write_collect_flush_line(struct screen_write_ctx *ctx, u_int y) if (y + yoff >= wsy) return (0); - r = screen_redraw_get_visible_ranges(wp, 0, y + yoff, wsx, NULL); + r = window_visible_ranges(wp, 0, y + yoff, wsx, NULL); TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { log_debug("collect list: x=%u (last %u), y=%u, used=%u", ci->x, last, y, ci->used); @@ -2528,8 +2524,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) xoff = wp->xoff; yoff = wp->yoff; } - r = screen_redraw_get_visible_ranges(wp, xoff + s->cx, s->cy + yoff, - width, NULL); + r = window_visible_ranges(wp, xoff + s->cx, s->cy + yoff, width, NULL); /* * Move the cursor. If not wrapping, stick at the last character and @@ -2701,7 +2696,7 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc) */ if (wp != NULL) yoff = wp->yoff; - r = screen_redraw_get_visible_ranges(wp, cx - n, cy + yoff, n, NULL); + r = window_visible_ranges(wp, cx - n, cy + yoff, n, NULL); for (i = 0, vis = 0; i < r->used; i++) vis += r->ranges[i].nx; if (vis < n) { diff --git a/server-client.c b/server-client.c index 579ed7d7..5972be61 100644 --- a/server-client.c +++ b/server-client.c @@ -1851,8 +1851,8 @@ server_client_reset_state(struct client *c) cx = wp->xoff + (int)s->cx - (int)ox; cy = wp->yoff + (int)s->cy - (int)oy; - r = screen_redraw_get_visible_ranges(wp, cx, cy, 1, NULL); - if (!screen_redraw_is_visible(r, cx)) + r = window_visible_ranges(wp, cx, cy, 1, NULL); + if (!window_position_is_visible(r, cx)) cursor = 0; if (status_at_line(c) == 0) diff --git a/tmux.h b/tmux.h index aeb555b6..fbf0e2ef 100644 --- a/tmux.h +++ b/tmux.h @@ -3339,9 +3339,6 @@ void screen_write_alternateoff(struct screen_write_ctx *, /* screen-redraw.c */ void screen_redraw_screen(struct client *); void screen_redraw_pane(struct client *, struct window_pane *, int); -int screen_redraw_is_visible(struct visible_ranges *, u_int); -struct visible_ranges *screen_redraw_get_visible_ranges(struct window_pane *, - int, int, u_int, struct visible_ranges *); /* screen.c */ void screen_init(struct screen *, u_int, u_int, u_int); @@ -3489,6 +3486,11 @@ struct style_range *window_pane_status_get_range(struct window_pane *, u_int, u_int); int window_pane_is_floating(struct window_pane *); +/* window-visible.c */ +int window_position_is_visible(struct visible_ranges *, u_int); +struct visible_ranges *window_visible_ranges(struct window_pane *, int, int, + u_int, struct visible_ranges *); + /* layout.c */ u_int layout_count_cells(struct layout_cell *); struct layout_cell *layout_create_cell(struct layout_cell *); diff --git a/window-visible.c b/window-visible.c new file mode 100644 index 00000000..7feb496d --- /dev/null +++ b/window-visible.c @@ -0,0 +1,224 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 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" + +/* + * Check if a single character is within a visible range (not obscured by a + * floating pane). + */ +int +window_position_is_visible(struct visible_ranges *r, u_int px) +{ + u_int i; + struct visible_range *ri; + + if (r == NULL) + return (1); + for (i = 0; i < r->used; i++) { + ri = &r->ranges[i]; + if (ri->nx != 0 && px >= ri->px && px < ri->px + ri->nx) + return (1); + } + return (0); +} + +/* + * Construct ranges array for the line at starting at px,py of width cells of + * base_wp that are unobsructed. All ranges are in window coordinates. + */ +struct visible_ranges * +window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width, + struct visible_ranges *r) +{ + struct window_pane *wp; + struct window *w; + struct visible_range *ri; + static struct visible_ranges sr = { NULL, 0, 0 }; + int found_self, sb, sb_w, sb_pos; + int lb, rb, tb, bb, sx, ex, no_border; + u_int i, s; + + if (py < 0 || width == 0) + goto empty; + if (px < 0) { + if ((u_int)-px >= width) + goto empty; + width -= (u_int)-px; + px = 0; + } + + if (base_wp == NULL) { + if (r != NULL) + return (r); + if (sr.ranges == NULL) + sr.ranges = xcalloc(1, sizeof *sr.ranges); + sr.ranges[0].px = px; + sr.ranges[0].nx = width; + sr.size = 1; + sr.used = 1; + return (&sr); + } + + w = base_wp->window; + if ((u_int)py >= w->sy) + goto empty; + if (px + width > w->sx) + width = w->sx - px; + + if (r == NULL) { + /* Start with the entire width of the range. */ + server_client_ensure_ranges(&base_wp->r, 1); + r = &base_wp->r; + r->ranges[0].px = px; + r->ranges[0].nx = width; + r->used = 1; + } + + sb = options_get_number(w->options, "pane-scrollbars"); + sb_pos = options_get_number(w->options, "pane-scrollbars-position"); + + found_self = 0; + TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) { + if (wp == base_wp) { + found_self = 1; + continue; + } + + if (window_pane_is_floating(wp) && + window_pane_get_pane_lines(wp) == PANE_LINES_NONE) + no_border = 1; + else + no_border = 0; + if (no_border) { + tb = wp->yoff; + bb = wp->yoff + (int)wp->sy - 1; + } else { + tb = wp->yoff > 0 ? wp->yoff - 1 : 0; + bb = wp->yoff + (int)wp->sy; + } + if (!found_self || + !window_pane_is_visible(wp) || + py < tb || + py > bb) + continue; + if (!window_pane_is_floating(wp) && (py == tb || py == bb)) + continue; + + sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; + if (!window_pane_show_scrollbar(wp, sb)) + sb_w = sb_pos = 0; + + for (i = 0; i < r->used; i++) { + ri = &r->ranges[i]; + if (no_border) { + lb = wp->xoff; + rb = wp->xoff + (int)wp->sx - 1; + } else if (sb_pos == PANE_SCROLLBARS_LEFT) { + if (wp->xoff > sb_w) + lb = wp->xoff - 1 - sb_w; + else + lb = 0; + } else { /* PANE_SCROLLBARS_RIGHT or none. */ + if (wp->xoff > 0) + lb = wp->xoff - 1; + else + lb = 0; + } + if (!no_border) { + if (sb_pos == PANE_SCROLLBARS_LEFT) + rb = wp->xoff + (int)wp->sx; + else /* PANE_SCROLLBARS_RIGHT or none. */ + rb = wp->xoff + (int)wp->sx + sb_w; + } + if (lb < 0) + lb = 0; + if (rb < 0) + continue; + if (no_border && rb >= (int)w->sx) + rb = w->sx - 1; + else if (!no_border && rb > (int)w->sx) + rb = w->sx - 1; + + sx = ri->px; + ex = sx + ri->nx - 1; + if (lb > sx && lb <= ex && rb > ex) { + /* + * If the left edge of floating pane falls + * inside this range and right edge covers up + * to right of range, then shrink left edge of + * range. + */ + ri->nx = lb - sx; + } else if (rb >= sx && rb <= ex && lb <= sx) { + /* + * Else if the right edge of floating pane falls + * inside of this range and left edge covers + * the left of range, then move px forward to + * right edge of pane. + */ + ri->nx = ex - rb; + ri->px = rb + 1; + } else if (lb > sx && rb <= ex) { + /* + * Else if pane fully inside range then split + * into 2 ranges. + */ + server_client_ensure_ranges(r, r->used + 1); + for (s = r->used; s > i; s--) { + memcpy(&r->ranges[s], &r->ranges[s - 1], + sizeof *r->ranges); + } + ri = &r->ranges[i]; + r->ranges[i + 1].px = rb + 1; + r->ranges[i + 1].nx = ex - rb; + /* ri->px was copied, unchanged. */ + ri->nx = lb - sx; + r->used++; + } else if (lb <= sx && rb > ex) { + /* + * If floating pane completely covers this range + * then delete it (make it 0 length). + */ + ri->nx = 0; + } else { + /* + * The range is already obscured, do + * nothing. + */ + } + } + } + return (r); + +empty: + if (r == NULL) { + if (sr.ranges == NULL) + sr.ranges = xcalloc(1, sizeof *sr.ranges); + sr.size = 1; + sr.used = 0; + return (&sr); + } + r->used = 0; + return (r); +} From 6549765930b28bff5358091c32dc89746ea96e60 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Tue, 16 Jun 2026 12:55:41 +0100 Subject: [PATCH 10/10] Makefile: add window-visible.c --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index ba9142fe..c4caedbd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -215,6 +215,7 @@ dist_tmux_SOURCES = \ window-copy.c \ window-customize.c \ window-tree.c \ + window-visible.c \ window.c \ xmalloc.c \ xmalloc.h