window: add floating-pane damage/redraw helpers

window_pane_redraw_floating() reports damage for only a floating pane's
old and new rectangle (via redraw_damage_window(), grown by one cell to
cover its border frame - see the "floating" case in screen-redraw.c),
instead of the caller falling back to a full window redraw. Any other
pane whose *scrollbar strip* - not its whole body - intersects either
rectangle still gets PANE_REDRAWSCROLLBAR directly, since scrollbars
aren't covered by the damage system.
This commit is contained in:
Michael Grant
2026-08-22 16:32:21 +01:00
parent d6074895bb
commit f255f089fc
2 changed files with 118 additions and 0 deletions

4
tmux.h
View File

@@ -3831,6 +3831,10 @@ int window_pane_get_pane_status(struct window_pane *);
struct style_range *window_pane_status_get_range(struct window_pane *, u_int,
u_int);
int window_pane_is_floating(struct window_pane *);
int window_pane_intersects(struct window_pane *, u_int, u_int,
u_int, u_int);
void window_pane_redraw_floating(struct window *,
struct window_pane *, int, int, int, int);
/* window-border.c */
void window_set_fill_cells(struct window *);

114
window.c
View File

@@ -2821,3 +2821,117 @@ window_pane_is_floating(struct window_pane *wp)
return (0);
return (1);
}
/* Does a pane's current rectangle intersect a window-coordinate rectangle? */
int
window_pane_intersects(struct window_pane *wp, u_int x, u_int y, u_int sx,
u_int sy)
{
int ix = (int)x, iy = (int)y, isx = (int)sx, isy = (int)sy;
return (wp->xoff < ix + isx && wp->xoff + (int)wp->sx > ix &&
wp->yoff < iy + isy && wp->yoff + (int)wp->sy > iy);
}
/*
* Report damage for a floating pane's rectangle, grown by one cell on every
* side - a floating pane draws its border frame at xoff-1/yoff-1 through
* xoff+sx/yoff+sy (see the "floating" case in screen-redraw.c), one cell
* outside its own content area, so damage for just the content area leaves
* the frame's previous position undrawn as the pane moves. If a scrollbar
* is reserved, its side of the frame is pushed out further still by its
* width and padding (also matched in screen-redraw.c), so grow that side
* to match.
*/
static void
window_pane_damage_floating(struct window *w, struct window_pane *wp,
int xoff, int yoff, int sx, int sy)
{
int x0, x1, y0, y1, sb_left = 0, sb_right = 0;
if (window_pane_scrollbar_reserve(wp)) {
if (w->sb_pos == PANE_SCROLLBARS_LEFT)
sb_left = wp->scrollbar_style.width +
wp->scrollbar_style.pad;
else
sb_right = wp->scrollbar_style.width +
wp->scrollbar_style.pad;
}
x0 = xoff - 1 - sb_left;
x1 = xoff + sx + sb_right;
y0 = yoff - 1;
y1 = yoff + sy;
if (x0 < 0)
x0 = 0;
if (y0 < 0)
y0 = 0;
if (x1 < x0 || y1 < y0)
return;
redraw_damage_window(w, (u_int)x0, (u_int)y0, (u_int)(x1 - x0) + 1,
(u_int)(y1 - y0) + 1);
}
/*
* Whether a pane's scrollbar strip - not its whole body - intersects a
* window-coordinate rectangle. A reserved scrollbar occupies a strip of
* scrollbar_style.width+pad columns just outside the pane's own content
* area (see the scrollbar-reserve case in layout_fix_panes(), layout.c),
* on whichever side w->sb_pos points to.
*/
static int
window_pane_scrollbar_intersects(struct window *w, struct window_pane *loop,
u_int x, u_int y, u_int sx, u_int sy)
{
int sb_x, sb_w, ix = (int)x, iy = (int)y, isx = (int)sx;
int isy = (int)sy;
if (!window_pane_scrollbar_reserve(loop))
return (0);
sb_w = loop->scrollbar_style.width + loop->scrollbar_style.pad;
if (w->sb_pos == PANE_SCROLLBARS_LEFT)
sb_x = (int)loop->xoff - sb_w;
else
sb_x = (int)loop->xoff + (int)loop->sx;
return (sb_x < ix + isx && sb_x + sb_w > ix &&
(int)loop->yoff < iy + isy && (int)loop->yoff + (int)loop->sy > iy);
}
/*
* Report damage for only a floating pane's old and new area, rather than
* the whole window - a floating pane move or resize only disturbs what it
* was covering and what it now covers. Scrollbars aren't covered by the
* damage system, so a pane whose *scrollbar strip* (not its whole body)
* intersects either area is still flagged directly for a scrollbar redraw.
* Checking the whole pane body here, rather than just its narrow scrollbar
* strip, meant merely dragging over a pane's ordinary content set
* PANE_REDRAWSCROLLBAR on every such pane on every motion event, triggering
* a needless scrollbar redraw (and the redraw pass it forces) each time
* even though the scrollbar itself never moved.
*
* Shared by every command that drags a floating pane around by the mouse:
* resize-pane's own border drag (cmd-resize-pane.c), move-pane -M's
* alternate Alt-drag (cmd-join-pane.c), and split-window/new-pane's
* interactive resize of a newly-created floating pane (cmd-split-window.c).
*/
void
window_pane_redraw_floating(struct window *w, struct window_pane *wp,
int old_xoff, int old_yoff, int old_sx, int old_sy)
{
struct window_pane *loop;
window_pane_damage_floating(w, wp, old_xoff, old_yoff, old_sx,
old_sy);
window_pane_damage_floating(w, wp, wp->xoff, wp->yoff, wp->sx,
wp->sy);
TAILQ_FOREACH(loop, &w->panes, entry) {
if (window_pane_scrollbar_intersects(w, loop,
(u_int)old_xoff, (u_int)old_yoff, (u_int)old_sx,
(u_int)old_sy) ||
window_pane_scrollbar_intersects(w, loop, wp->xoff,
wp->yoff, wp->sx, wp->sy))
loop->flags |= PANE_REDRAWSCROLLBAR;
}
}