summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@cvs.openbsd.org>2024-11-05 09:41:18 +0000
committerNicholas Marriott <nicm@cvs.openbsd.org>2024-11-05 09:41:18 +0000
commit3419b8a436d751400846bbf274c4f9f58d2128d5 (patch)
tree50a35b2f03b11f1d904cd258710a6d89e38801cd
parentff4058cdb37c54f478e2c1271634c78ea0a05490 (diff)
Add support for a scrollbar at the side of each pane. New options
pane-scrollbars turn them on or off, pane-scrollbars-position sets the position (left or right), and pane-scrollbars-style to set the colours. Mouse support will come later. From Michael Grant in GitHub issue 4221.
-rw-r--r--usr.bin/tmux/layout.c70
-rw-r--r--usr.bin/tmux/options-table.c35
-rw-r--r--usr.bin/tmux/options.c6
-rw-r--r--usr.bin/tmux/screen-redraw.c309
-rw-r--r--usr.bin/tmux/screen-write.c15
-rw-r--r--usr.bin/tmux/server-client.c59
-rw-r--r--usr.bin/tmux/tmux.144
-rw-r--r--usr.bin/tmux/tmux.h27
-rw-r--r--usr.bin/tmux/window-copy.c9
-rw-r--r--usr.bin/tmux/window.c35
10 files changed, 491 insertions, 118 deletions
diff --git a/usr.bin/tmux/layout.c b/usr.bin/tmux/layout.c
index 33d680d37fa..b89e9574f00 100644
--- a/usr.bin/tmux/layout.c
+++ b/usr.bin/tmux/layout.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: layout.c,v 1.48 2021/03/11 06:31:05 nicm Exp $ */
+/* $OpenBSD: layout.c,v 1.49 2024/11/05 09:41:17 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -275,7 +275,8 @@ layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
* the case for the most upper or lower panes only.
*/
static int
-layout_add_border(struct window *w, struct layout_cell *lc, int status)
+layout_add_horizontal_border(struct window *w, struct layout_cell *lc,
+ int status)
{
if (status == PANE_STATUS_TOP)
return (layout_cell_is_top(w, lc));
@@ -290,22 +291,41 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
{
struct window_pane *wp;
struct layout_cell *lc;
- int status;
+ int status, scrollbars, sb_pos;
+ u_int sx, sy, mode;
status = options_get_number(w->options, "pane-border-status");
+ scrollbars = options_get_number(w->options, "pane-scrollbars");
+ sb_pos = options_get_number(w->options, "pane-scrollbars-position");
+
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL || wp == skip)
continue;
wp->xoff = lc->xoff;
wp->yoff = lc->yoff;
+ sx = lc->sx;
+ sy = lc->sy;
- if (layout_add_border(w, lc, status)) {
+ if (layout_add_horizontal_border(w, lc, status)) {
if (status == PANE_STATUS_TOP)
wp->yoff++;
- window_pane_resize(wp, lc->sx, lc->sy - 1);
- } else
- window_pane_resize(wp, lc->sx, lc->sy);
+ sy--;
+ }
+
+ mode = window_pane_mode(wp);
+ if (scrollbars == PANE_SCROLLBARS_ALWAYS ||
+ (scrollbars == PANE_SCROLLBARS_MODAL &&
+ mode != WINDOW_PANE_NO_MODE)) {
+ if (sb_pos == PANE_SCROLLBARS_LEFT) {
+ sx = sx - PANE_SCROLLBARS_WIDTH;
+ wp->xoff = wp->xoff + PANE_SCROLLBARS_WIDTH;
+ } else /* sb_pos == PANE_SCROLLBARS_RIGHT */
+ sx = sx - PANE_SCROLLBARS_WIDTH;
+ wp->flags |= PANE_REDRAWSCROLLBAR;
+ }
+
+ window_pane_resize(wp, sx, sy);
}
}
@@ -337,17 +357,22 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
{
struct layout_cell *lcchild;
u_int available, minimum;
- int status;
+ int status, scrollbars;
status = options_get_number(w->options, "pane-border-status");
+ scrollbars = options_get_number(w->options, "pane-scrollbars");
+
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx;
- minimum = PANE_MINIMUM;
+ if (scrollbars)
+ minimum = PANE_MINIMUM + PANE_SCROLLBARS_WIDTH;
+ else
+ minimum = PANE_MINIMUM;
} else {
available = lc->sy;
- if (layout_add_border(w, lc, status))
+ if (layout_add_horizontal_border(w, lc, status))
minimum = PANE_MINIMUM + 1;
else
minimum = PANE_MINIMUM;
@@ -873,6 +898,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
u_int sx, sy, xoff, yoff, size1, size2, minimum;
u_int new_size, saved_size, resize_first = 0;
int full_size = (flags & SPAWN_FULLSIZE), status;
+ int scrollbars;
/*
* If full_size is specified, add a new cell at the top of the window
@@ -883,6 +909,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
else
lc = wp->layout_cell;
status = options_get_number(wp->window->options, "pane-border-status");
+ scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
/* Copy the old cell size. */
sx = lc->sx;
@@ -893,11 +920,15 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
/* Check there is enough space for the two new panes. */
switch (type) {
case LAYOUT_LEFTRIGHT:
- if (sx < PANE_MINIMUM * 2 + 1)
+ if (scrollbars)
+ minimum = PANE_MINIMUM * 2 + PANE_SCROLLBARS_WIDTH;
+ else
+ minimum = PANE_MINIMUM * 2 + 1;
+ if (sx < minimum)
return (NULL);
break;
case LAYOUT_TOPBOTTOM:
- if (layout_add_border(wp->window, lc, status))
+ if (layout_add_horizontal_border(wp->window, lc, status))
minimum = PANE_MINIMUM * 2 + 2;
else
minimum = PANE_MINIMUM * 2 + 1;
@@ -1054,7 +1085,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
{
struct layout_cell *lc;
u_int number, each, size, this;
- int change, changed, status;
+ int change, changed, status, scrollbars;
number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry)
@@ -1062,11 +1093,16 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
if (number <= 1)
return (0);
status = options_get_number(w->options, "pane-border-status");
+ scrollbars = options_get_number(w->options, "pane-scrollbars");
- if (parent->type == LAYOUT_LEFTRIGHT)
- size = parent->sx;
+ if (parent->type == LAYOUT_LEFTRIGHT) {
+ if (scrollbars)
+ size = parent->sx - PANE_SCROLLBARS_WIDTH;
+ else
+ size = parent->sx;
+ }
else if (parent->type == LAYOUT_TOPBOTTOM) {
- if (layout_add_border(w, parent, status))
+ if (layout_add_horizontal_border(w, parent, status))
size = parent->sy - 1;
else
size = parent->sy;
@@ -1087,7 +1123,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
change = each - (int)lc->sx;
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
} else if (parent->type == LAYOUT_TOPBOTTOM) {
- if (layout_add_border(w, lc, status))
+ if (layout_add_horizontal_border(w, lc, status))
this = each + 1;
else
this = each;
diff --git a/usr.bin/tmux/options-table.c b/usr.bin/tmux/options-table.c
index 5e729af7d74..469d1c45fb5 100644
--- a/usr.bin/tmux/options-table.c
+++ b/usr.bin/tmux/options-table.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: options-table.c,v 1.183 2024/10/21 12:42:06 nicm Exp $ */
+/* $OpenBSD: options-table.c,v 1.184 2024/11/05 09:41:17 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -64,6 +64,12 @@ static const char *options_table_cursor_style_list[] = {
"default", "blinking-block", "block", "blinking-underline", "underline",
"blinking-bar", "bar", NULL
};
+static const char *options_table_pane_scrollbars_list[] = {
+ "off", "modal", "on", NULL
+};
+static const char *options_table_pane_scrollbars_position_list[] = {
+ "right", "left", NULL
+};
static const char *options_table_pane_status_list[] = {
"off", "top", "bottom", NULL
};
@@ -1161,7 +1167,32 @@ const struct options_table_entry options_table[] = {
.text = "The default colour palette for colours zero to 255."
},
- { .name = "popup-style",
+ { .name = "pane-scrollbars",
+ .type = OPTIONS_TABLE_CHOICE,
+ .scope = OPTIONS_TABLE_WINDOW,
+ .choices = options_table_pane_scrollbars_list,
+ .default_num = PANE_SCROLLBARS_OFF,
+ .text = "Pane scrollbar state."
+ },
+
+ { .name = "pane-scrollbars-style",
+ .type = OPTIONS_TABLE_STRING,
+ .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
+ .default_str = "bg=black,fg=white",
+ .flags = OPTIONS_TABLE_IS_STYLE,
+ .separator = ",",
+ .text = "Style of the pane scrollbar."
+ },
+
+ { .name = "pane-scrollbars-position",
+ .type = OPTIONS_TABLE_CHOICE,
+ .scope = OPTIONS_TABLE_WINDOW,
+ .choices = options_table_pane_scrollbars_position_list,
+ .default_num = PANE_SCROLLBARS_RIGHT,
+ .text = "Pane scrollbar position."
+ },
+
+ { .name = "popup-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
diff --git a/usr.bin/tmux/options.c b/usr.bin/tmux/options.c
index e4799942288..8e2107df3cb 100644
--- a/usr.bin/tmux/options.c
+++ b/usr.bin/tmux/options.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: options.c,v 1.70 2024/07/22 15:27:42 nicm Exp $ */
+/* $OpenBSD: options.c,v 1.71 2024/11/05 09:41:17 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1171,7 +1171,9 @@ options_push_changes(const char *name)
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
colour_palette_from_option(&wp->palette, wp->options);
}
- if (strcmp(name, "pane-border-status") == 0) {
+ if (strcmp(name, "pane-border-status") == 0 ||
+ strcmp(name, "pane-scrollbars") == 0 ||
+ strcmp(name, "pane-scrollbars-position") == 0) {
RB_FOREACH(w, windows, &windows)
layout_fix_panes(w, NULL);
}
diff --git a/usr.bin/tmux/screen-redraw.c b/usr.bin/tmux/screen-redraw.c
index af3f1cee11d..cbda4a6954d 100644
--- a/usr.bin/tmux/screen-redraw.c
+++ b/usr.bin/tmux/screen-redraw.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: screen-redraw.c,v 1.99 2024/10/08 09:40:50 nicm Exp $ */
+/* $OpenBSD: screen-redraw.c,v 1.100 2024/11/05 09:41:17 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -30,6 +30,11 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *,
struct window_pane *);
static void screen_redraw_set_context(struct client *,
struct screen_redraw_ctx *);
+static void screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *);
+static void screen_redraw_draw_scrollbar(struct screen_redraw_ctx *,
+ struct window_pane *, int, int, int, u_int, u_int, u_int);
+static void screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *,
+ struct window_pane *);
#define START_ISOLATE "\342\201\246"
#define END_ISOLATE "\342\201\251"
@@ -113,9 +118,10 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
u_int px, u_int py)
{
struct options *oo = wp->window->options;
- int split = 0;
u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy;
- int pane_status = ctx->pane_status;
+ int hsplit = 0, vsplit = 0, pane_status = ctx->pane_status;
+ int pane_scrollbars = ctx->pane_scrollbars, sb_w = 0;
+ int sb_pos = ctx->pane_scrollbars_pos;
/* Inside pane. */
if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey)
@@ -125,62 +131,62 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
switch (options_get_number(oo, "pane-border-indicators")) {
case PANE_BORDER_COLOUR:
case PANE_BORDER_BOTH:
- split = 1;
+ hsplit = screen_redraw_two_panes(wp->window, 0);
+ vsplit = screen_redraw_two_panes(wp->window, 1);
break;
}
- /* Left/right borders. */
- if (pane_status == PANE_STATUS_OFF) {
- if (screen_redraw_two_panes(wp->window, 0) && split) {
- if (wp->xoff == 0 && px == wp->sx && py <= wp->sy / 2)
- return (SCREEN_REDRAW_BORDER_RIGHT);
- if (wp->xoff != 0 &&
- px == wp->xoff - 1 &&
- py > wp->sy / 2)
- return (SCREEN_REDRAW_BORDER_LEFT);
- } else {
- if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
- if (wp->xoff != 0 && px == wp->xoff - 1)
+ /* Are scrollbars enabled? */
+ if (pane_scrollbars == PANE_SCROLLBARS_ALWAYS ||
+ (pane_scrollbars == PANE_SCROLLBARS_MODAL &&
+ window_pane_mode(wp) != WINDOW_PANE_NO_MODE))
+ sb_w = PANE_SCROLLBARS_WIDTH;
+
+ /*
+ * Left/right borders. The wp->sy / 2 test is to colour only half the
+ * active window's border when there are two panes.
+ */
+ if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
+ if (sb_pos == PANE_SCROLLBARS_LEFT) {
+ if (wp->xoff - sb_w == 0 && px == wp->sx + sb_w)
+ if (!hsplit || (hsplit && py <= wp->sy / 2))
+ return (SCREEN_REDRAW_BORDER_RIGHT);
+ if (wp->xoff - sb_w != 0 && px == wp->xoff - sb_w - 1)
+ if (!hsplit || (hsplit && py > wp->sy / 2))
return (SCREEN_REDRAW_BORDER_LEFT);
- if (px == ex)
+ } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
+ if (wp->xoff == 0 && px == wp->sx + sb_w)
+ if (!hsplit || (hsplit && py <= wp->sy / 2))
return (SCREEN_REDRAW_BORDER_RIGHT);
- }
- }
- } else {
- if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
if (wp->xoff != 0 && px == wp->xoff - 1)
- return (SCREEN_REDRAW_BORDER_LEFT);
- if (px == ex)
- return (SCREEN_REDRAW_BORDER_RIGHT);
+ if (!hsplit || (hsplit && py > wp->sy / 2))
+ return (SCREEN_REDRAW_BORDER_LEFT);
}
}
/* Top/bottom borders. */
- if (pane_status == PANE_STATUS_OFF) {
- if (screen_redraw_two_panes(wp->window, 1) && split) {
- if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2)
- return (SCREEN_REDRAW_BORDER_BOTTOM);
- if (wp->yoff != 0 &&
- py == wp->yoff - 1 &&
- px > wp->sx / 2)
- return (SCREEN_REDRAW_BORDER_TOP);
- } else {
- if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
+ if (vsplit && pane_status == PANE_STATUS_OFF && sb_w == 0) {
+ if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2)
+ return (SCREEN_REDRAW_BORDER_BOTTOM);
+ if (wp->yoff != 0 && py == wp->yoff - 1 && px > wp->sx / 2)
+ return (SCREEN_REDRAW_BORDER_TOP);
+ } else {
+ if (sb_pos == PANE_SCROLLBARS_LEFT) {
+ if ((wp->xoff - sb_w == 0 || px >= wp->xoff - sb_w) &&
+ (px <= ex || (sb_w != 0 && px - 1 == ex))) {
+ if (wp->yoff != 0 && py == wp->yoff - 1)
+ return (SCREEN_REDRAW_BORDER_TOP);
+ if (py == ey)
+ return (SCREEN_REDRAW_BORDER_BOTTOM);
+ }
+ } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
+ if ((wp->xoff == 0 || px >= wp->xoff) &&
+ (px <= ex || (sb_w != 0 && px - 1 == ex))) {
if (wp->yoff != 0 && py == wp->yoff - 1)
return (SCREEN_REDRAW_BORDER_TOP);
if (py == ey)
return (SCREEN_REDRAW_BORDER_BOTTOM);
}
- }
- } else if (pane_status == PANE_STATUS_TOP) {
- if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
- if (wp->yoff != 0 && py == wp->yoff - 1)
- return (SCREEN_REDRAW_BORDER_TOP);
- }
- } else {
- if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
- if (py == ey)
- return (SCREEN_REDRAW_BORDER_BOTTOM);
}
}
@@ -243,8 +249,12 @@ screen_redraw_type_of_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py)
return (CELL_OUTSIDE);
/*
- * Construct a bitmask of whether the cells to the left (bit 4), right,
+ * Construct a bitmask of whether the cells to the left (bit 8), right,
* top, and bottom (bit 1) of this cell are borders.
+ *
+ * bits 8 4 2 1: 2
+ * 8 + 4
+ * 1
*/
if (px == 0 || screen_redraw_cell_border(ctx, px - 1, py))
borders |= 8;
@@ -313,8 +323,10 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py,
struct window_pane *wp, *active;
int pane_status = ctx->pane_status;
u_int sx = w->sx, sy = w->sy;
- int border;
+ int border, pane_scrollbars = ctx->pane_scrollbars;
u_int right, line;
+ int sb_pos = ctx->pane_scrollbars_pos;
+ int sb_w = PANE_SCROLLBARS_WIDTH;
*wpp = NULL;
@@ -351,6 +363,35 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py,
goto next2;
*wpp = wp;
+ /* Check if CELL_SCROLLBAR */
+ if (pane_scrollbars == PANE_SCROLLBARS_ALWAYS ||
+ (pane_scrollbars == PANE_SCROLLBARS_MODAL &&
+ window_pane_mode(wp) != WINDOW_PANE_NO_MODE)) {
+
+ if (pane_status == PANE_STATUS_TOP)
+ line = wp->yoff - 1;
+ else
+ line = wp->yoff + wp->sy;
+
+ /*
+ * Check if py could lie within a scrollbar. If the
+ * pane is at the top then py == 0 to sy; if the pane
+ * is not at the top, then yoff to yoff + sy.
+ */
+ if ((pane_status && py != line) ||
+ (wp->yoff == 0 && py < wp->sy) ||
+ (py >= wp->yoff && py < wp->yoff + wp->sy)) {
+ /* Check if px lies within a scrollbar. */
+ if ((sb_pos == PANE_SCROLLBARS_RIGHT &&
+ (px >= wp->xoff + wp->sx &&
+ px < wp->xoff + wp->sx + sb_w)) ||
+ (sb_pos == PANE_SCROLLBARS_LEFT &&
+ (px >= wp->xoff - sb_w &&
+ px < wp->xoff)))
+ return (CELL_SCROLLBAR);
+ }
+ }
+
/*
* If definitely inside, return. If not on border, skip.
* Otherwise work out the cell.
@@ -510,14 +551,13 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
/* Update status line and change flags if unchanged. */
static uint64_t
-screen_redraw_update(struct client *c, uint64_t flags)
+screen_redraw_update(struct screen_redraw_ctx *ctx, uint64_t flags)
{
+ struct client *c = ctx->c;
struct window *w = c->session->curw->window;
struct window_pane *wp;
- struct options *wo = w->options;
int redraw;
enum pane_lines lines;
- struct screen_redraw_ctx ctx;
if (c->message_string != NULL)
redraw = status_message_redraw(c);
@@ -531,17 +571,17 @@ screen_redraw_update(struct client *c, uint64_t flags)
if (c->overlay_draw != NULL)
flags |= CLIENT_REDRAWOVERLAY;
- if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) {
- screen_redraw_set_context(c, &ctx);
- lines = options_get_number(wo, "pane-border-lines");
+ if (ctx->pane_status != PANE_STATUS_OFF) {
+ lines = ctx->pane_lines;
redraw = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
- if (screen_redraw_make_pane_status(c, wp, &ctx, lines))
+ if (screen_redraw_make_pane_status(c, wp, ctx, lines))
redraw = 1;
}
if (redraw)
flags |= CLIENT_REDRAWBORDERS;
}
+
return (flags);
}
@@ -568,6 +608,10 @@ screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx)
ctx->pane_status = options_get_number(wo, "pane-border-status");
ctx->pane_lines = options_get_number(wo, "pane-border-lines");
+ ctx->pane_scrollbars = options_get_number(wo, "pane-scrollbars");
+ ctx->pane_scrollbars_pos = options_get_number(wo,
+ "pane-scrollbars-position");
+
tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);
log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
@@ -585,11 +629,12 @@ screen_redraw_screen(struct client *c)
if (c->flags & CLIENT_SUSPENDED)
return;
- flags = screen_redraw_update(c, c->flags);
+ screen_redraw_set_context(c, &ctx);
+
+ flags = screen_redraw_update(&ctx, c->flags);
if ((flags & CLIENT_ALLREDRAWFLAGS) == 0)
return;
- screen_redraw_set_context(c, &ctx);
tty_sync_start(&c->tty);
tty_update_mode(&c->tty, c->tty.mode, NULL);
@@ -598,10 +643,12 @@ screen_redraw_screen(struct client *c)
screen_redraw_draw_borders(&ctx);
if (ctx.pane_status != PANE_STATUS_OFF)
screen_redraw_draw_pane_status(&ctx);
+ screen_redraw_draw_pane_scrollbars(&ctx);
}
if (flags & CLIENT_REDRAWWINDOW) {
log_debug("%s: redrawing panes", c->name);
screen_redraw_draw_panes(&ctx);
+ screen_redraw_draw_pane_scrollbars(&ctx);
}
if (ctx.statuslines != 0 &&
(flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) {
@@ -616,20 +663,35 @@ screen_redraw_screen(struct client *c)
tty_reset(&c->tty);
}
-/* Redraw a single pane. */
+/* Redraw a single pane and its scrollbar. */
void
-screen_redraw_pane(struct client *c, struct window_pane *wp)
+screen_redraw_pane(struct client *c, struct window_pane *wp,
+ int redraw_scrollbar_only)
{
- struct screen_redraw_ctx ctx;
+ struct screen_redraw_ctx ctx;
+ int pane_scrollbars, mode;
if (!window_pane_visible(wp))
return;
+ mode = window_pane_mode(wp);
screen_redraw_set_context(c, &ctx);
tty_sync_start(&c->tty);
tty_update_mode(&c->tty, c->tty.mode, NULL);
- screen_redraw_draw_pane(&ctx, wp);
+ if (!redraw_scrollbar_only)
+ screen_redraw_draw_pane(&ctx, wp);
+
+ /*
+ * Redraw scrollbar if needed. Always redraw scrollbar in a mode because
+ * if redrawing a pane, it's because pane has scrolled.
+ */
+ pane_scrollbars = ctx.pane_scrollbars;
+ if (pane_scrollbars == PANE_SCROLLBARS_MODAL &&
+ mode == WINDOW_PANE_NO_MODE)
+ pane_scrollbars = PANE_SCROLLBARS_OFF;
+ if (pane_scrollbars != PANE_SCROLLBARS_OFF)
+ screen_redraw_draw_pane_scrollbar(&ctx, wp);
tty_reset(&c->tty);
}
@@ -675,8 +737,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
const struct grid_cell *tmp;
struct overlay_ranges r;
u_int cell_type, x = ctx->ox + i, y = ctx->oy + j;
- int arrows = 0, border;
- int isolates;
+ int arrows = 0, border, isolates;
if (c->overlay_check != NULL) {
c->overlay_check(c, c->overlay_data, x, y, 1, &r);
@@ -685,7 +746,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
}
cell_type = screen_redraw_check_cell(ctx, x, y, &wp);
- if (cell_type == CELL_INSIDE)
+ if (cell_type == CELL_INSIDE || cell_type == CELL_SCROLLBAR)
return;
if (wp == NULL) {
@@ -870,3 +931,127 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette);
}
}
+
+/* Draw the panes scrollbars */
+static void
+screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *ctx)
+{
+ struct client *c = ctx->c;
+ struct window *w = c->session->curw->window;
+ struct window_pane *wp;
+
+ log_debug("%s: %s @%u", __func__, c->name, w->id);
+
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ switch (ctx->pane_scrollbars) {
+ case PANE_SCROLLBARS_OFF:
+ return;
+ case PANE_SCROLLBARS_MODAL:
+ if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE)
+ return;
+ break;
+ case PANE_SCROLLBARS_ALWAYS:
+ break;
+ }
+ if (window_pane_visible(wp))
+ screen_redraw_draw_pane_scrollbar(ctx, wp);
+ }
+}
+
+/* Draw pane scrollbar. */
+void
+screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *ctx,
+ struct window_pane *wp)
+{
+ struct screen *s = wp->screen;
+ double percent_view;
+ u_int sb = ctx->pane_scrollbars, total_height, sb_h = wp->sy;
+ u_int sb_pos = ctx->pane_scrollbars_pos, slider_h, slider_y;
+ u_int sb_w = PANE_SCROLLBARS_WIDTH, cm_y, cm_size;
+ int sb_x, sb_y = (int)(wp->yoff - ctx->oy); /* sb top */
+
+ if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) {
+ if (sb == PANE_SCROLLBARS_MODAL)
+ return;
+ /* Show slider at the bottom of the scrollbar. */
+ total_height = screen_size_y(s) + screen_hsize(s);
+ percent_view = (double)sb_h / total_height;
+ slider_h = (double)sb_h * percent_view;
+ slider_y = sb_h - slider_h;
+ } else {
+ if (TAILQ_FIRST(&wp->modes) == NULL)
+ return;
+ if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0)
+ return;
+ total_height = cm_size + sb_h;
+ percent_view = (double)sb_h / (cm_size + sb_h);
+ slider_h = (double)sb_h * percent_view;
+ slider_y = (sb_h + 1) * ((double)cm_y / total_height);
+ }
+
+ if (sb_pos == PANE_SCROLLBARS_LEFT)
+ sb_x = (int)wp->xoff - sb_w - ctx->ox;
+ else
+ sb_x = (int)wp->xoff + wp->sx - ctx->ox;
+
+ if (slider_h < 1)
+ slider_h = 1;
+ if (slider_y >= sb_h)
+ slider_y = sb_h - 1;
+
+ screen_redraw_draw_scrollbar(ctx, wp, sb_pos, sb_x, sb_y, sb_h,
+ slider_h, slider_y);
+}
+
+static void
+screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
+ struct window_pane *wp, int sb_pos, int sb_x, int sb_y, u_int sb_h,
+ u_int slider_h, u_int slider_y)
+{
+ struct client *c = ctx->c;
+ struct window *w = wp->window;
+ struct tty *tty = &c->tty;
+ struct grid_cell gc, slgc, *gcp;
+ u_int i, j, sb_w = PANE_SCROLLBARS_WIDTH;
+ u_int pad_col = 0;
+ int px, py, ox = ctx->ox, oy = ctx->oy;
+ int sb_pad = PANE_SCROLLBARS_PADDING, sx = ctx->sx;
+ int sy = ctx->sy, xoff = wp->xoff, yoff = wp->yoff;
+
+ /* Set up default style. */
+ style_apply(&gc, w->options, "pane-scrollbars-style", NULL);
+ utf8_set(&gc.data, ' ');
+
+ /* Set up style for slider. */
+ memcpy(&slgc, &gc, sizeof slgc);
+ slgc.bg = gc.fg;
+
+ if (sb_pad != 0) {
+ if (sb_pos == PANE_SCROLLBARS_RIGHT)
+ pad_col = 0;
+ else
+ pad_col = sb_w - 1;
+ }
+
+ for (i = 0; i < sb_w; i++) {
+ for (j = 0; j < sb_h; j++) {
+ px = sb_x + i;
+ py = sb_y + j;
+ if (px < xoff - ox - 1 || px >= sx || px < 0 ||
+ py < yoff - oy - 1 || py >= sy || py < 0)
+ continue;
+ tty_cursor(tty, px, py);
+ if (sb_pad && i == pad_col) {
+ tty_cell(tty, &grid_default_cell,
+ &grid_default_cell, NULL, NULL);
+ } else {
+ if (j >= slider_y && j < slider_y + slider_h)
+ gcp = &slgc;
+ else
+ gcp = &gc;
+ tty_cell(tty, gcp, &grid_default_cell, NULL,
+ NULL);
+ }
+ }
+ }
+}
diff --git a/usr.bin/tmux/screen-write.c b/usr.bin/tmux/screen-write.c
index 47de30213e1..039cab62dd8 100644
--- a/usr.bin/tmux/screen-write.c
+++ b/usr.bin/tmux/screen-write.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: screen-write.c,v 1.228 2024/10/25 15:00:18 nicm Exp $ */
+/* $OpenBSD: screen-write.c,v 1.229 2024/11/05 09:41:17 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -151,7 +151,7 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
*/
log_debug("%s: adding %%%u to deferred redraw", __func__,
wp->id);
- wp->flags |= PANE_REDRAW;
+ wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR);
return (-1);
}
@@ -1178,13 +1178,14 @@ 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);
if (ny == 0)
ny = 1;
if (s->cy < s->rupper || s->cy > s->rlower) {
- if (ny > screen_size_y(s) - s->cy)
- ny = screen_size_y(s) - s->cy;
+ if (ny > sy - s->cy)
+ ny = sy - s->cy;
if (ny == 0)
return;
@@ -1376,13 +1377,14 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct grid_line *gl;
+ u_int rupper = s->rupper, rlower = s->rlower;
gl = grid_get_line(gd, gd->hsize + s->cy);
if (wrapped)
gl->flags |= GRID_LINE_WRAPPED;
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
- s->rupper, s->rlower);
+ rupper, rlower);
if (bg != ctx->bg) {
screen_write_collect_flush(ctx, 1, __func__);
@@ -1700,6 +1702,9 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
ttyctx.num = ctx->scrolled;
ttyctx.bg = ctx->bg;
tty_write(tty_cmd_scrollup, &ttyctx);
+
+ if (ctx->wp != NULL)
+ ctx->wp->flags |= PANE_REDRAWSCROLLBAR;
}
ctx->scrolled = 0;
ctx->bg = 8;
diff --git a/usr.bin/tmux/server-client.c b/usr.bin/tmux/server-client.c
index 75c2f1824a1..1493b6726a5 100644
--- a/usr.bin/tmux/server-client.c
+++ b/usr.bin/tmux/server-client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server-client.c,v 1.414 2024/10/08 06:29:44 nicm Exp $ */
+/* $OpenBSD: server-client.c,v 1.415 2024/11/05 09:41:17 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2230,7 +2230,7 @@ server_client_loop(void)
server_client_check_pane_resize(wp);
server_client_check_pane_buffer(wp);
}
- wp->flags &= ~PANE_REDRAW;
+ wp->flags &= ~(PANE_REDRAW|PANE_REDRAWSCROLLBAR);
}
check_window_name(w);
}
@@ -2662,7 +2662,7 @@ server_client_check_redraw(struct client *c)
struct window_pane *wp;
int needed, tty_flags, mode = tty->mode;
uint64_t client_flags = 0;
- int redraw;
+ int redraw_pane, redraw_scrollbar_only;
u_int bit = 0;
struct timeval tv = { .tv_usec = 1000 };
static struct event ev;
@@ -2671,12 +2671,13 @@ server_client_check_redraw(struct client *c)
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
- log_debug("%s: redraw%s%s%s%s%s", c->name,
+ log_debug("%s: redraw%s%s%s%s%s%s", c->name,
(c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
(c->flags & CLIENT_REDRAWSTATUS) ? " status" : "",
(c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "",
(c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "",
- (c->flags & CLIENT_REDRAWPANES) ? " panes" : "");
+ (c->flags & CLIENT_REDRAWPANES) ? " panes" : "",
+ (c->flags & CLIENT_REDRAWSCROLLBARS) ? " scrollbars" : "");
}
/*
@@ -2691,11 +2692,15 @@ server_client_check_redraw(struct client *c)
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->flags & PANE_REDRAW) {
needed = 1;
+ client_flags |= CLIENT_REDRAWPANES;
break;
}
+ if (wp->flags & PANE_REDRAWSCROLLBAR) {
+ needed = 1;
+ client_flags |= CLIENT_REDRAWSCROLLBARS;
+ /* no break - later panes may need redraw */
+ }
}
- if (needed)
- client_flags |= CLIENT_REDRAWPANES;
}
if (needed && (left = EVBUFFER_LENGTH(tty->out)) != 0) {
log_debug("%s: redraw deferred (%zu left)", c->name, left);
@@ -2708,23 +2713,30 @@ server_client_check_redraw(struct client *c)
if (~c->flags & CLIENT_REDRAWWINDOW) {
TAILQ_FOREACH(wp, &w->panes, entry) {
- if (wp->flags & PANE_REDRAW) {
+ if (wp->flags & (PANE_REDRAW)) {
log_debug("%s: pane %%%u needs redraw",
c->name, wp->id);
c->redraw_panes |= (1 << bit);
+ } else if (wp->flags & PANE_REDRAWSCROLLBAR) {
+ log_debug("%s: pane %%%u scrollbar "
+ "needs redraw", c->name, wp->id);
+ c->redraw_scrollbars |= (1 << bit);
}
if (++bit == 64) {
/*
* If more that 64 panes, give up and
* just redraw the window.
*/
- client_flags &= CLIENT_REDRAWPANES;
+ client_flags &= ~(CLIENT_REDRAWPANES|
+ CLIENT_REDRAWSCROLLBARS);
client_flags |= CLIENT_REDRAWWINDOW;
break;
}
}
if (c->redraw_panes != 0)
c->flags |= CLIENT_REDRAWPANES;
+ if (c->redraw_scrollbars != 0)
+ c->flags |= CLIENT_REDRAWSCROLLBARS;
}
c->flags |= client_flags;
return;
@@ -2740,19 +2752,32 @@ server_client_check_redraw(struct client *c)
* needs to be redrawn.
*/
TAILQ_FOREACH(wp, &w->panes, entry) {
- redraw = 0;
+ redraw_pane = 0;
+ redraw_scrollbar_only = 0;
if (wp->flags & PANE_REDRAW)
- redraw = 1;
- else if (c->flags & CLIENT_REDRAWPANES)
- redraw = !!(c->redraw_panes & (1 << bit));
+ redraw_pane = 1;
+ else if (c->flags & CLIENT_REDRAWPANES) {
+ if (c->redraw_panes & (1 << bit))
+ redraw_pane = 1;
+ } else if (c->flags & CLIENT_REDRAWSCROLLBARS) {
+ if (c->redraw_scrollbars & (1 << bit))
+ redraw_scrollbar_only = 1;
+ }
bit++;
- if (!redraw)
+ if (!redraw_pane && !redraw_scrollbar_only)
continue;
- log_debug("%s: redrawing pane %%%u", __func__, wp->id);
- screen_redraw_pane(c, wp);
+ if (redraw_scrollbar_only) {
+ log_debug("%s: redrawing (scrollbar only) pane "
+ "%%%u", __func__, wp->id);
+ } else {
+ log_debug("%s: redrawing pane %%%u", __func__,
+ wp->id);
+ }
+ screen_redraw_pane(c, wp, redraw_scrollbar_only);
}
c->redraw_panes = 0;
- c->flags &= ~CLIENT_REDRAWPANES;
+ c->redraw_scrollbars = 0;
+ c->flags &= ~(CLIENT_REDRAWPANES|CLIENT_REDRAWSCROLLBARS);
}
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1
index 6e7aebfbebd..2c70a2c4e17 100644
--- a/usr.bin/tmux/tmux.1
+++ b/usr.bin/tmux/tmux.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.967 2024/10/17 17:22:01 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.968 2024/11/05 09:41:17 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: October 17 2024 $
+.Dd $Mdocdate: November 5 2024 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -5026,6 +5026,46 @@ and
.Ql heavy
will fall back to standard ACS line drawing when UTF-8 is not supported.
.Pp
+.It Xo Ic pane-scrollbars
+.Op Ic off | modal | on
+.Xc
+When enabled, a character based scrollbar appears on the left or right
+of each pane.
+A filled section of the scrollbar, known as the
+.Ql slider ,
+represents the position and size of the visible part of the pane content.
+.Pp
+If set to
+.Ic on
+the scrollbar is visible all the time.
+If set to
+.Ic modal
+the scrollbar only appears when the pane is in copy mode or view mode.
+When the scrollbar is visible, the pane is narrowed by the width of the
+scrollbar and the text in the pane is reflowed.
+If set to
+.Ic modal ,
+the pane is narrowed only when the scrollbar is visible.
+.Pp
+See also
+.Xr pane-scrollbars-style .
+.Pp
+.It Ic pane-scrollbars-style Ar style
+Set the scrollbars style.
+For how to specify
+.Ar style ,
+see the
+.Sx STYLES
+section.
+The foreground colour is used for the slider, the background for the rest of the
+scrollbar.
+Attributes are ignored.
+.Pp
+.It Xo Ic pane-scrollbars-position
+.Op Ic left | right
+.Xc
+Sets which side of the pane to display pane scrollbars on.
+.Pp
.It Ic window-status-activity-style Ar style
Set status line style for windows with an activity alert.
For how to specify
diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h
index dac5d950e4e..5b571fbe11c 100644
--- a/usr.bin/tmux/tmux.h
+++ b/usr.bin/tmux/tmux.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.1237 2024/10/25 15:32:51 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.1238 2024/11/05 09:41:17 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -739,6 +739,7 @@ struct colour_palette {
#define CELL_RIGHTJOIN 10
#define CELL_JOIN 11
#define CELL_OUTSIDE 12
+#define CELL_SCROLLBAR 13
/* Cell borders. */
#define CELL_BORDERS " xqlkmjwvtun~"
@@ -985,6 +986,9 @@ struct screen_redraw_ctx {
int pane_status;
enum pane_lines pane_lines;
+ int pane_scrollbars;
+ int pane_scrollbars_pos;
+
struct grid_cell no_pane_gc;
int no_pane_gc_set;
@@ -1103,6 +1107,7 @@ struct window_pane {
#define PANE_EMPTY 0x800
#define PANE_STYLECHANGED 0x1000
#define PANE_UNSEENCHANGES 0x2000
+#define PANE_REDRAWSCROLLBAR 0x4000
int argc;
char **argv;
@@ -1245,6 +1250,19 @@ TAILQ_HEAD(winlink_stack, winlink);
#define PANE_STATUS_TOP 1
#define PANE_STATUS_BOTTOM 2
+/* Pane scrollbars option. */
+#define PANE_SCROLLBARS_OFF 0
+#define PANE_SCROLLBARS_MODAL 1
+#define PANE_SCROLLBARS_ALWAYS 2
+
+/* Pane scrollbars position option. */
+#define PANE_SCROLLBARS_RIGHT 0
+#define PANE_SCROLLBARS_LEFT 1
+
+/* Pane scrollbars width and padding. */
+#define PANE_SCROLLBARS_WIDTH 1
+#define PANE_SCROLLBARS_PADDING 0
+
/* Layout direction. */
enum layout_type {
LAYOUT_LEFTRIGHT,
@@ -1880,13 +1898,15 @@ struct client {
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
#define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
+#define CLIENT_REDRAWSCROLLBARS 0x4000000000ULL
#define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \
CLIENT_REDRAWSTATUSALWAYS| \
CLIENT_REDRAWBORDERS| \
CLIENT_REDRAWOVERLAY| \
- CLIENT_REDRAWPANES)
+ CLIENT_REDRAWPANES| \
+ CLIENT_REDRAWSCROLLBARS)
#define CLIENT_UNATTACHEDFLAGS \
(CLIENT_DEAD| \
CLIENT_SUSPENDED| \
@@ -1913,6 +1933,7 @@ struct client {
key_code last_key;
uint64_t redraw_panes;
+ uint64_t redraw_scrollbars;
int message_ignore_keys;
int message_ignore_styles;
@@ -3014,7 +3035,7 @@ 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 *);
+void screen_redraw_pane(struct client *, struct window_pane *, int);
/* screen.c */
void screen_init(struct screen *, u_int, u_int, u_int);
diff --git a/usr.bin/tmux/window-copy.c b/usr.bin/tmux/window-copy.c
index e1bdea65b5c..148b32f5189 100644
--- a/usr.bin/tmux/window-copy.c
+++ b/usr.bin/tmux/window-copy.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: window-copy.c,v 1.362 2024/10/28 08:16:51 nicm Exp $ */
+/* $OpenBSD: window-copy.c,v 1.363 2024/11/05 09:41:17 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -4319,6 +4319,8 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
window_copy_write_line(wme, &ctx, i);
screen_write_cursormove(&ctx, data->cx, data->cy, 0);
screen_write_stop(&ctx);
+
+ wp->flags |= PANE_REDRAWSCROLLBAR;
}
static void
@@ -5367,8 +5369,7 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
py = hsize + data->cy - data->oy;
grid_reader_start(&gr, back_s->grid, px, py);
- grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0,
- /* stop_at_eol= */ 1);
+ grid_reader_cursor_previous_word(&gr, separators, 0, 1);
grid_reader_get_cursor(&gr, &px, &py);
*ppx = px;
*ppy = py;
@@ -5481,6 +5482,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1);
screen_write_cursormove(&ctx, data->cx, data->cy, 0);
screen_write_stop(&ctx);
+ wp->flags |= PANE_REDRAWSCROLLBAR;
}
static void
@@ -5514,6 +5516,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
window_copy_write_line(wme, &ctx, 1);
screen_write_cursormove(&ctx, data->cx, data->cy, 0);
screen_write_stop(&ctx);
+ wp->flags |= PANE_REDRAWSCROLLBAR;
}
static void
diff --git a/usr.bin/tmux/window.c b/usr.bin/tmux/window.c
index ab0b49e9a21..e916410608c 100644
--- a/usr.bin/tmux/window.c
+++ b/usr.bin/tmux/window.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: window.c,v 1.295 2024/10/05 12:10:16 nicm Exp $ */
+/* $OpenBSD: window.c,v 1.296 2024/11/05 09:41:17 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -584,11 +584,31 @@ struct window_pane *
window_get_active_at(struct window *w, u_int x, u_int y)
{
struct window_pane *wp;
+ int pane_scrollbars;
+ u_int sb_pos, sb_w, xoff, sx;
+
+ pane_scrollbars = options_get_number(w->options, "pane-scrollbars");
+ sb_pos = options_get_number(w->options, "pane-scrollbars-position");
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp))
continue;
- if (x < wp->xoff || x > wp->xoff + wp->sx)
+
+ if (pane_scrollbars == PANE_SCROLLBARS_ALWAYS ||
+ (pane_scrollbars == PANE_SCROLLBARS_MODAL &&
+ window_pane_mode(wp) != WINDOW_PANE_NO_MODE))
+ sb_w = PANE_SCROLLBARS_WIDTH;
+ else
+ sb_w = 0;
+
+ if (sb_pos == PANE_SCROLLBARS_LEFT) {
+ xoff = wp->xoff - sb_w;
+ sx = wp->sx + sb_w;
+ } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
+ xoff = wp->xoff;
+ sx = wp->sx + sb_w;
+ }
+ if (x < xoff || x > xoff + sx)
continue;
if (y < wp->yoff || y > wp->yoff + wp->sy)
continue;
@@ -1086,6 +1106,7 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
struct args *args)
{
struct window_mode_entry *wme;
+ struct window *w = wp->window;
if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode)
return (1);
@@ -1106,9 +1127,10 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
wme->screen = wme->mode->init(wme, fs, args);
}
-
wp->screen = wme->screen;
- wp->flags |= (PANE_REDRAW|PANE_CHANGED);
+
+ wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
+ layout_fix_panes(w, NULL);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
@@ -1121,6 +1143,7 @@ void
window_pane_reset_mode(struct window_pane *wp)
{
struct window_mode_entry *wme, *next;
+ struct window *w = wp->window;
if (TAILQ_EMPTY(&wp->modes))
return;
@@ -1141,7 +1164,9 @@ window_pane_reset_mode(struct window_pane *wp)
if (next->mode->resize != NULL)
next->mode->resize(next, wp->sx, wp->sy);
}
- wp->flags |= (PANE_REDRAW|PANE_CHANGED);
+
+ wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
+ layout_fix_panes(w, NULL);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);