Fix cursor and redraw overlap with auto-hide scrollbars

Auto-hide scrollbars are drawn as overlays inside the pane rather than in
reserved columns. Avoid optimized pane scrolling/redraw paths writing through
a visible overlay scrollbar, and suppress the terminal cursor when it would be
placed in the visible overlay scrollbar column.

This prevents transient wrong-colour cells and cursor blocks appearing over
the scrollbar, especially when scrolling small floating panes.  This fixed
the green block issue.
This commit is contained in:
Michael Grant
2026-06-20 09:09:27 +02:00
parent 738083c4a4
commit fe1c3db1e5
5 changed files with 61 additions and 11 deletions

View File

@@ -2174,6 +2174,10 @@ screen_write_collect_flush_scrolled(struct screen_write_ctx *ctx)
screen_write_redraw_pane(ctx, &ttyctx); screen_write_redraw_pane(ctx, &ttyctx);
return 0; return 0;
} }
if (wp != NULL && window_pane_scrollbar_overlay_visible(wp)) {
wp->flags |= PANE_REDRAW;
return 0;
}
log_debug("%s: scrolled %u (region %u-%u)", __func__, ctx->scrolled, log_debug("%s: scrolled %u (region %u-%u)", __func__, ctx->scrolled,
s->rupper, s->rlower); s->rupper, s->rlower);
@@ -2187,7 +2191,7 @@ screen_write_collect_flush_scrolled(struct screen_write_ctx *ctx)
tty_write(tty_cmd_scrollup, &ttyctx); tty_write(tty_cmd_scrollup, &ttyctx);
if (wp != NULL) if (wp != NULL)
wp->flags |= PANE_REDRAWSCROLLBAR; window_pane_scrollbar_redraw(wp);
return 1; return 1;
} }

View File

@@ -1882,7 +1882,7 @@ server_client_reset_state(struct client *c)
struct screen *s = NULL; struct screen *s = NULL;
struct options *oo = c->session->options; struct options *oo = c->session->options;
int mode = 0, cursor, flags, pane_mode = 0; int mode = 0, cursor, flags, pane_mode = 0;
u_int cx = 0, cy = 0, ox, oy, sx, sy, n; u_int cx = 0, cy = 0, ox, oy, sx, sy, n, sb_w;
struct visible_ranges *r; struct visible_ranges *r;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
@@ -1942,6 +1942,21 @@ server_client_reset_state(struct client *c)
if (!window_position_is_visible(r, cx)) if (!window_position_is_visible(r, cx))
cursor = 0; 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 &&
options_get_number(w->options,
"pane-scrollbars-position") ==
PANE_SCROLLBARS_LEFT) {
if (s->cx < sb_w)
cursor = 0;
} else if (sb_w != 0 &&
s->cx >= wp->sx - sb_w)
cursor = 0;
}
if (status_at_line(c) == 0) if (status_at_line(c) == 0)
cy += status_line_size(c); cy += status_line_size(c);
} }

2
tmux.h
View File

@@ -3518,9 +3518,11 @@ int window_pane_show_scrollbar(struct window_pane *, int);
int window_pane_scrollbar_reserve(struct window_pane *, int); int window_pane_scrollbar_reserve(struct window_pane *, int);
int window_pane_scrollbar_visible(struct window_pane *, int); int window_pane_scrollbar_visible(struct window_pane *, int);
int window_pane_scrollbar_overlay(struct window_pane *, int); int window_pane_scrollbar_overlay(struct window_pane *, int);
int window_pane_scrollbar_overlay_visible(struct window_pane *);
void window_pane_scrollbar_show(struct window_pane *, int); void window_pane_scrollbar_show(struct window_pane *, int);
void window_pane_scrollbar_hide(struct window_pane *); void window_pane_scrollbar_hide(struct window_pane *);
void window_pane_scrollbar_start_timer(struct window_pane *); void window_pane_scrollbar_start_timer(struct window_pane *);
void window_pane_scrollbar_redraw(struct window_pane *);
int window_pane_get_bg(struct window_pane *); int window_pane_get_bg(struct window_pane *);
int window_pane_get_fg(struct window_pane *); int window_pane_get_fg(struct window_pane *);
int window_pane_get_fg_control_client(struct window_pane *); int window_pane_get_fg_control_client(struct window_pane *);

View File

@@ -5284,7 +5284,10 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
return; return;
} }
screen_write_start_pane(&ctx, wp, NULL); if (window_pane_scrollbar_overlay_visible(wp))
screen_write_start(&ctx, &data->screen);
else
screen_write_start_pane(&ctx, wp, NULL);
for (i = py; i < py + ny; i++) for (i = py; i < py + ny; i++)
window_copy_write_line(wme, &ctx, i); window_copy_write_line(wme, &ctx, i);
screen_write_cursormove(&ctx, screen_write_cursormove(&ctx,
@@ -5292,7 +5295,7 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
0); 0);
screen_write_stop(&ctx); screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR; window_pane_scrollbar_redraw(wp);
} }
static void static void
@@ -6580,7 +6583,10 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
return; return;
} }
screen_write_start_pane(&ctx, wp, NULL); if (window_pane_scrollbar_overlay_visible(wp))
screen_write_start(&ctx, &data->screen);
else
screen_write_start_pane(&ctx, wp, NULL);
screen_write_cursormove(&ctx, 0, 0, 0); screen_write_cursormove(&ctx, 0, 0, 0);
screen_write_deleteline(&ctx, ny, 8); screen_write_deleteline(&ctx, ny, 8);
window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny); window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny);
@@ -6595,7 +6601,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), data->cy, window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), data->cy,
0); 0);
screen_write_stop(&ctx); screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR; window_pane_scrollbar_redraw(wp);
} }
static void static void
@@ -6641,7 +6647,10 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
return; return;
} }
screen_write_start_pane(&ctx, wp, NULL); if (window_pane_scrollbar_overlay_visible(wp))
screen_write_start(&ctx, &data->screen);
else
screen_write_start_pane(&ctx, wp, NULL);
screen_write_cursormove(&ctx, 0, 0, 0); screen_write_cursormove(&ctx, 0, 0, 0);
screen_write_insertline(&ctx, ny, 8); screen_write_insertline(&ctx, ny, 8);
window_copy_write_lines(wme, &ctx, 0, ny); window_copy_write_lines(wme, &ctx, 0, ny);
@@ -6652,7 +6661,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
screen_write_cursormove(&ctx, window_copy_cursor_offset(wme, data->cx, screen_write_cursormove(&ctx, window_copy_cursor_offset(wme, data->cx,
screen_size_x(s)), data->cy, 0); screen_size_x(s)), data->cy, 0);
screen_write_stop(&ctx); screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR; window_pane_scrollbar_redraw(wp);
} }
static void static void

View File

@@ -1151,8 +1151,28 @@ window_pane_scrollbar_auto_hide(struct window_pane *wp)
"pane-scrollbars-auto-hide")); "pane-scrollbars-auto-hide"));
} }
static void int
window_pane_scrollbar_overlay_visible(struct window_pane *wp)
{
int sb;
sb = options_get_number(wp->window->options, "pane-scrollbars");
return (window_pane_scrollbar_overlay(wp, sb) &&
window_pane_scrollbar_visible(wp, sb));
}
void
window_pane_scrollbar_redraw(struct window_pane *wp) window_pane_scrollbar_redraw(struct window_pane *wp)
{
if (window_pane_scrollbar_overlay_visible(wp)) {
wp->flags |= PANE_REDRAW;
return;
}
wp->flags |= PANE_REDRAWSCROLLBAR;
}
static void
window_pane_scrollbar_redraw_visibility(struct window_pane *wp)
{ {
redraw_invalidate_scene(wp->window); redraw_invalidate_scene(wp->window);
wp->flags |= PANE_REDRAW; wp->flags |= PANE_REDRAW;
@@ -2066,7 +2086,7 @@ window_pane_scrollbar_show(struct window_pane *wp, int start_timer)
if (start_timer) if (start_timer)
window_pane_scrollbar_start_timer(wp); window_pane_scrollbar_start_timer(wp);
if (changed) if (changed)
window_pane_scrollbar_redraw(wp); window_pane_scrollbar_redraw_visibility(wp);
} }
void void
@@ -2078,7 +2098,7 @@ window_pane_scrollbar_hide(struct window_pane *wp)
if (!wp->sb_auto_visible) if (!wp->sb_auto_visible)
return; return;
wp->sb_auto_visible = 0; wp->sb_auto_visible = 0;
window_pane_scrollbar_redraw(wp); window_pane_scrollbar_redraw_visibility(wp);
} }
int int