Add a vertical (side) status line, GitHub issue 5468 from John DeWyze.

This commit is contained in:
nicm
2026-10-16 07:59:06 +00:00
committed by tmux update bot
parent b5d46cb60d
commit 32069f8ced
20 changed files with 1258 additions and 264 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-display-menu.c,v 1.57 2026/10/15 22:16:32 nicm Exp $ */ /* $OpenBSD: cmd-display-menu.c,v 1.58 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -104,7 +104,15 @@ cmd_display_menu_set_mouse_pos(struct client *tc, struct mouse_event *m,
return; return;
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
*mouse_x = m->x + ox; if (m->sideat == 0) {
if (m->x >= m->sidewidth)
*mouse_x = m->x - m->sidewidth + ox;
else
*mouse_x = ox;
} else if (m->sideat > 0 && m->x >= (u_int)m->sideat)
*mouse_x = ox + sx - 1;
else
*mouse_x = m->x + ox;
if (m->statusat == 0) { if (m->statusat == 0) {
if (m->y >= m->statuslines) if (m->y >= m->statuslines)
*mouse_y = m->y - m->statuslines + oy; *mouse_y = m->y - m->statuslines + oy;
@@ -118,40 +126,24 @@ cmd_display_menu_set_mouse_pos(struct client *tc, struct mouse_event *m,
format_add(ft, "popup_mouse_y", "%ld", *mouse_y); format_add(ft, "popup_mouse_y", "%ld", *mouse_y);
} }
static int static void
cmd_display_menu_get_menu_pos(struct client *tc, struct cmdq_item *item, cmd_display_menu_set_status_pos(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h) struct format_tree *ft, u_int w, u_int h)
{ {
struct cmd_find_state *target = cmdq_get_target(item); struct mouse_event *m = &cmdq_get_event(item)->m;
struct key_event *event = cmdq_get_event(item); struct winlink *wl = cmdq_get_target(item)->wl;
struct session *s = tc->session; struct window *window = wl->window;
struct winlink *wl = target->wl; struct style_ranges *ranges;
struct window *window = target->w; struct style_range *sr = NULL, *clicked;
struct window_pane *wp = target->wp; u_int line, lines, ox, oy, sx, sy, x, y, sidey;
struct style_ranges *ranges = NULL; int position, side;
struct style_range *sr = NULL;
const char *xp, *yp;
char *p;
u_int line, ox, oy, sx, sy, lines, position;
long n, max_x, max_y, mouse_x = 0, mouse_y = 0;
struct format_tree *ft;
if (event->m.valid)
window = tc->session->curw->window;
max_x = window->sx > w ? window->sx - w : 0;
max_y = window->sy > h ? window->sy - h : 0;
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
if (m->valid)
ft = format_create_from_target(item); window = tc->session->curw->window;
cmd_display_menu_set_mouse_pos(tc, &event->m, ft, &mouse_x, &mouse_y);
format_add(ft, "popup_last_x", "%u", window->menu_last_px);
format_add(ft, "popup_last_y", "%u", window->menu_last_py + h);
lines = status_line_size(tc); lines = status_line_size(tc);
position = options_get_number(s->options, "status-position"); position = status_at_line(tc);
if (status_at_line(tc) != -1 && lines != 0) { if (position != -1 && lines != 0) {
for (line = 0; line < lines; line++) { for (line = 0; line < lines; line++) {
ranges = &tc->status.entries[line].ranges; ranges = &tc->status.entries[line].ranges;
TAILQ_FOREACH(sr, ranges, entry) { TAILQ_FOREACH(sr, ranges, entry) {
@@ -163,23 +155,75 @@ cmd_display_menu_get_menu_pos(struct client *tc, struct cmdq_item *item,
if (sr != NULL) if (sr != NULL)
break; break;
} }
if (position == 0)
y = h;
else
y = window->sy;
if (sr != NULL) { if (sr != NULL) {
format_add(ft, "popup_window_status_line_x", "%u", format_add(ft, "popup_window_status_line_x", "%u",
sr->start + ox); sr->start + ox);
if (position == 0) { format_add(ft, "popup_window_status_line_y", "%u", y);
format_add(ft, "popup_window_status_line_y",
"%u", h);
} else {
format_add(ft, "popup_window_status_line_y",
"%u", window->sy);
}
} }
if (position == 0) format_add(ft, "popup_status_line_y", "%u", y);
format_add(ft, "popup_status_line_y", "%u", h);
else
format_add(ft, "popup_status_line_y", "%u", window->sy);
} }
side = status_side_at_column(tc);
if (side == -1)
return;
sidey = (position == 0 ? lines : 0);
if (m->valid && m->x >= (u_int)side &&
m->x < side + status_side_size(tc) &&
m->y >= sidey && m->y - sidey < status_side_rows(tc)) {
y = m->y - sidey;
clicked = status_side_get_range(tc, m->x - side, y);
if (clicked == NULL || clicked->type != STYLE_RANGE_WINDOW ||
clicked->argument != (u_int)wl->idx)
return;
sr = clicked;
} else {
if (sr != NULL)
return;
ranges = &tc->side_status.ranges;
TAILQ_FOREACH(sr, ranges, entry) {
if (sr->type == STYLE_RANGE_WINDOW &&
sr->argument == (u_int)wl->idx)
break;
}
if (sr == NULL)
return;
}
x = ox;
if (side != 0 && sx > w)
x += sx - w;
format_add(ft, "popup_window_status_line_x", "%u", x);
format_add(ft, "popup_window_status_line_y", "%u", oy + sr->y + h);
}
static int
cmd_display_menu_get_menu_pos(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct window *window = target->w;
struct window_pane *wp = target->wp;
const char *xp, *yp;
char *p;
long n, max_x, max_y, mouse_x = 0, mouse_y = 0;
struct format_tree *ft;
if (event->m.valid)
window = tc->session->curw->window;
max_x = window->sx > w ? window->sx - w : 0;
max_y = window->sy > h ? window->sy - h : 0;
ft = format_create_from_target(item);
cmd_display_menu_set_mouse_pos(tc, &event->m, ft, &mouse_x, &mouse_y);
format_add(ft, "popup_last_x", "%u", window->menu_last_px);
format_add(ft, "popup_last_y", "%u", window->menu_last_py + h);
cmd_display_menu_set_status_pos(tc, item, ft, w, h);
format_add(ft, "popup_width", "%u", w); format_add(ft, "popup_width", "%u", w);
format_add(ft, "popup_height", "%u", h); format_add(ft, "popup_height", "%u", h);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-join-pane.c,v 1.78 2026/10/09 13:12:14 nicm Exp $ */ /* $OpenBSD: cmd-join-pane.c,v 1.79 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2011 George Nachman <tmux@georgester.com> * Copyright (c) 2011 George Nachman <tmux@georgester.com>
@@ -325,11 +325,19 @@ cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
y -= m->statuslines; y -= m->statuslines;
else if (m->statusat > 0 && y >= m->statusat) else if (m->statusat > 0 && y >= m->statusat)
y = m->statusat - 1; y = m->statusat - 1;
if (m->sideat == 0 && x >= (int)m->sidewidth)
x -= m->sidewidth;
else if (m->sideat > 0 && x >= m->sideat)
x = m->sideat - 1;
ly = m->ly + m->oy; lx = m->lx + m->ox; ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly >= (int)m->statuslines) if (m->statusat == 0 && ly >= (int)m->statuslines)
ly -= m->statuslines; ly -= m->statuslines;
else if (m->statusat > 0 && ly >= m->statusat) else if (m->statusat > 0 && ly >= m->statusat)
ly = m->statusat - 1; ly = m->statusat - 1;
if (m->sideat == 0 && lx >= (int)m->sidewidth)
lx -= m->sidewidth;
else if (m->sideat > 0 && lx >= m->sideat)
lx = m->sideat - 1;
if (x != lx || y != ly) { if (x != lx || y != ly) {
oxoff = wp->xoff; oxoff = wp->xoff;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-resize-pane.c,v 1.71 2026/10/09 13:12:14 nicm Exp $ */ /* $OpenBSD: cmd-resize-pane.c,v 1.72 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -267,11 +267,19 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
y -= m->statuslines; y -= m->statuslines;
else if (m->statusat > 0 && y >= m->statusat) else if (m->statusat > 0 && y >= m->statusat)
y = m->statusat - 1; y = m->statusat - 1;
if (m->sideat == 0 && x >= (int)m->sidewidth)
x -= m->sidewidth;
else if (m->sideat > 0 && x >= m->sideat)
x = m->sideat - 1;
ly = m->ly + m->oy; lx = m->lx + m->ox; ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly >= (int)m->statuslines) if (m->statusat == 0 && ly >= (int)m->statuslines)
ly -= m->statuslines; ly -= m->statuslines;
else if (m->statusat > 0 && ly >= m->statusat) else if (m->statusat > 0 && ly >= m->statusat)
ly = m->statusat - 1; ly = m->statusat - 1;
if (m->sideat == 0 && lx >= (int)m->sidewidth)
lx -= m->sidewidth;
else if (m->sideat > 0 && lx >= m->sideat)
lx = m->sideat - 1;
if ((lx == left || lx == left + 1) && ly == wp->yoff - 1) { if ((lx == left || lx == left + 1) && ly == wp->yoff - 1) {
/* Top left corner. */ /* Top left corner. */
@@ -381,11 +389,19 @@ cmd_resize_pane_mouse_resize_tiled(struct client *c, struct mouse_event *m)
y -= m->statuslines; y -= m->statuslines;
else if (m->statusat > 0 && y >= (u_int)m->statusat) else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1; y = m->statusat - 1;
if (m->sideat == 0 && x >= m->sidewidth)
x -= m->sidewidth;
else if (m->sideat > 0 && x >= (u_int)m->sideat)
x = m->sideat - 1;
ly = m->ly + m->oy; lx = m->lx + m->ox; ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly >= m->statuslines) if (m->statusat == 0 && ly >= m->statuslines)
ly -= m->statuslines; ly -= m->statuslines;
else if (m->statusat > 0 && ly >= (u_int)m->statusat) else if (m->statusat > 0 && ly >= (u_int)m->statusat)
ly = m->statusat - 1; ly = m->statusat - 1;
if (m->sideat == 0 && lx >= m->sidewidth)
lx -= m->sidewidth;
else if (m->sideat > 0 && lx >= (u_int)m->sideat)
lx = m->sideat - 1;
for (i = 0; i < nitems(cells); i++) { for (i = 0; i < nitems(cells); i++) {
lc = layout_search_by_border(w->layout_root, lx + offsets[i][0], lc = layout_search_by_border(w->layout_root, lx + offsets[i][0],

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-split-window.c,v 1.154 2026/10/15 15:42:35 nicm Exp $ */ /* $OpenBSD: cmd-split-window.c,v 1.155 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -377,6 +377,10 @@ cmd_split_window_mouse_resize(struct client *c, struct mouse_event *m)
y -= m->statuslines; y -= m->statuslines;
else if (m->statusat > 0 && y >= m->statusat) else if (m->statusat > 0 && y >= m->statusat)
y = m->statusat - 1; y = m->statusat - 1;
if (m->sideat == 0 && x >= (int)m->sidewidth)
x -= m->sidewidth;
else if (m->sideat > 0 && x >= m->sideat)
x = m->sideat - 1;
lines = window_pane_get_pane_lines(wp); lines = window_pane_get_pane_lines(wp);
border = (lines != PANE_LINES_NONE); border = (lines != PANE_LINES_NONE);

4
cmd.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd.c,v 1.189 2026/07/17 08:29:34 nicm Exp $ */ /* $OpenBSD: cmd.c,v 1.190 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -778,6 +778,8 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
if (m->statusat == 0 && y >= m->statuslines) if (m->statusat == 0 && y >= m->statuslines)
y -= m->statuslines; y -= m->statuslines;
if (m->sideat == 0 && x >= m->sidewidth)
x -= m->sidewidth;
if ((int)x < wp->xoff || (int)x >= wp->xoff + (int)wp->sx) if ((int)x < wp->xoff || (int)x >= wp->xoff + (int)wp->sx)
return (-1); return (-1);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: format-draw.c,v 1.36 2026/10/15 22:34:26 nicm Exp $ */ /* $OpenBSD: format-draw.c,v 1.37 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -39,6 +39,30 @@ struct format_range {
}; };
TAILQ_HEAD(format_ranges, format_range); TAILQ_HEAD(format_ranges, format_range);
/* The style at a point in a format. */
struct format_style {
struct style sy;
struct grid_cell cur_def;
struct grid_cell base_def;
int fill;
};
/* A single line of a format. */
struct format_line {
char *chunk;
struct format_style fs; /* at the start of the line */
int in_list;
int has_focus;
int marker; /* 0 = none, 1 = up, 2 = down */
};
/* State when splitting a format into lines. */
struct format_lines {
struct format_line *lines;
u_int nlines;
struct format_line current;
};
/* Does this range match this style? */ /* Does this range match this style? */
static int static int
format_is_type(struct format_range *fr, struct style *sy) format_is_type(struct format_range *fr, struct style *sy)
@@ -704,11 +728,42 @@ format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
screen_write_cell(ctx, &sy->gc); screen_write_cell(ctx, &sy->gc);
} }
/* Draw a format to a screen. */ /* Set up the style at the start of a format. */
void static void
format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, format_init_style(struct format_style *fs, const struct grid_cell *base)
{
memcpy(&fs->base_def, base, sizeof fs->base_def);
memcpy(&fs->cur_def, base, sizeof fs->cur_def);
style_set(&fs->sy, base);
fs->fill = -1;
}
/*
* Update the defaults if a style pushed, popped or set them. The previous cell
* is the one in use before the style.
*/
static void
format_update_defaults(struct style *sy, const struct grid_cell *previous,
struct grid_cell *cur_def, struct grid_cell *base_def)
{
if (sy->default_type == STYLE_DEFAULT_PUSH) {
memcpy(cur_def, previous, sizeof *cur_def);
sy->default_type = STYLE_DEFAULT_BASE;
} else if (sy->default_type == STYLE_DEFAULT_POP) {
memcpy(cur_def, base_def, sizeof *cur_def);
sy->default_type = STYLE_DEFAULT_BASE;
} else if (sy->default_type == STYLE_DEFAULT_SET) {
memcpy(base_def, previous, sizeof *base_def);
memcpy(cur_def, previous, sizeof *cur_def);
sy->default_type = STYLE_DEFAULT_BASE;
}
}
/* Draw a format to a screen, starting with a style. */
static void
format_draw1(struct screen_write_ctx *octx, const struct format_style *fs,
u_int available, const char *expanded, struct style_ranges *srs, u_int available, const char *expanded, struct style_ranges *srs,
int default_colours) int flags)
{ {
enum { LEFT, enum { LEFT,
CENTRE, CENTRE,
@@ -738,9 +793,9 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
RIGHT, RIGHT,
ABSOLUTE_CENTRE }; ABSOLUTE_CENTRE };
int focus_start = -1, focus_end = -1; int focus_start = -1, focus_end = -1;
int list_state = -1, fill = -1, even; int list_state = -1, fill, even;
enum style_align list_align = STYLE_ALIGN_DEFAULT; enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct grid_cell gc, current_default, base_default; struct grid_cell gc, cur_def, base_def;
struct style sy, saved_sy; struct style sy, saved_sy;
struct utf8_data *ud = &sy.gc.data; struct utf8_data *ud = &sy.gc.data;
const char *cp, *end, *link_uri; const char *cp, *end, *link_uri;
@@ -750,10 +805,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
struct format_ranges frs; struct format_ranges frs;
struct style_range *sr; struct style_range *sr;
memcpy(&base_default, base, sizeof base_default); memcpy(&base_def, &fs->base_def, sizeof base_def);
memcpy(&current_default, base, sizeof current_default); memcpy(&cur_def, &fs->cur_def, sizeof cur_def);
base = &base_default; memcpy(&sy, &fs->sy, sizeof sy);
style_set(&sy, &current_default); fill = fs->fill;
TAILQ_INIT(&frs); TAILQ_INIT(&frs);
log_debug("%s: %s", __func__, expanded); log_debug("%s: %s", __func__, expanded);
@@ -765,13 +820,43 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
for (i = 0; i < TOTAL; i++) { for (i = 0; i < TOTAL; i++) {
screen_init(&s[i], size, 1, 0); screen_init(&s[i], size, 1, 0);
screen_write_start(&ctx[i], &s[i]); screen_write_start(&ctx[i], &s[i]);
screen_write_clearendofline(&ctx[i], current_default.bg); screen_write_clearendofline(&ctx[i], cur_def.bg);
width[i] = 0; width[i] = 0;
} }
/* /*
* Walk the string and add to the corresponding screens, * Treat the starting style like a parsed style: apply the flags and
* parsing styles as we go. * begin with its alignment, hyperlink and range. It is only different
* from the base for a line during a multiline format, which is drawn
* without its list.
*/
if (flags & FORMAT_DRAW_DEFAULT_COLOURS) {
sy.gc.bg = base_def.bg;
sy.gc.fg = base_def.fg;
}
if (flags & FORMAT_DRAW_NO_LIST)
sy.list = STYLE_LIST_OFF;
current = last = map[sy.align];
link_uri = style_link(&sy);
if (link_uri != NULL && hl != NULL)
sy.gc.link = hyperlinks_put(hl, link_uri, link_uri);
else
sy.gc.link = 0;
if (srs != NULL && sy.range_type != STYLE_RANGE_NONE) {
fr = xcalloc(1, sizeof *fr);
fr->index = current;
fr->s = &s[current];
fr->start = s[current].cx;
fr->type = sy.range_type;
fr->argument = sy.range_argument;
strlcpy(fr->string, sy.range_string, sizeof fr->string);
}
/*
* Walk the string and add to the corresponding screens, parsing styles
* as we go.
*/ */
cp = expanded; cp = expanded;
while (*cp != '\0') { while (*cp != '\0') {
@@ -844,7 +929,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
} }
tmp = xstrndup(cp + 2, end - (cp + 2)); tmp = xstrndup(cp + 2, end - (cp + 2));
style_copy(&saved_sy, &sy); style_copy(&saved_sy, &sy);
if (style_parse(&sy, &current_default, tmp) != 0) { if (style_parse(&sy, &cur_def, tmp) != 0) {
log_debug("%s: invalid style '%s'", __func__, tmp); log_debug("%s: invalid style '%s'", __func__, tmp);
free(tmp); free(tmp);
cp = end + 1; cp = end + 1;
@@ -853,10 +938,12 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
log_debug("%s: style '%s' -> '%s'", __func__, tmp, log_debug("%s: style '%s' -> '%s'", __func__, tmp,
style_tostring(&sy)); style_tostring(&sy));
free(tmp); free(tmp);
if (default_colours) { if (flags & FORMAT_DRAW_DEFAULT_COLOURS) {
sy.gc.bg = base->bg; sy.gc.bg = base_def.bg;
sy.gc.fg = base->fg; sy.gc.fg = base_def.fg;
} }
if (flags & FORMAT_DRAW_NO_LIST)
sy.list = STYLE_LIST_OFF;
/* /*
* Resolve any hyperlink and store it in the cell. The URI * Resolve any hyperlink and store it in the cell. The URI
@@ -874,20 +961,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
fill = sy.fill; fill = sy.fill;
/* If this style pushed or popped the default, update it. */ /* If this style pushed or popped the default, update it. */
if (sy.default_type == STYLE_DEFAULT_PUSH) { format_update_defaults(&sy, &saved_sy.gc, &cur_def, &base_def);
memcpy(&current_default, &saved_sy.gc,
sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
} else if (sy.default_type == STYLE_DEFAULT_POP) {
memcpy(&current_default, base, sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
} else if (sy.default_type == STYLE_DEFAULT_SET) {
memcpy(&base_default, &saved_sy.gc,
sizeof base_default);
memcpy(&current_default, &saved_sy.gc,
sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
}
/* Check the list state. */ /* Check the list state. */
switch (sy.list) { switch (sy.list) {
@@ -1122,6 +1196,321 @@ out:
screen_write_cursormove(octx, ocx, ocy, 0); screen_write_cursormove(octx, ocx, ocy, 0);
} }
/* Draw a format to a screen. */
void
format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
u_int available, const char *expanded, struct style_ranges *srs,
int flags)
{
struct format_style fs;
format_init_style(&fs, base);
format_draw1(octx, &fs, available, expanded, srs, flags);
}
/* Add a line between start and end to the line array. */
static void
format_draw_lines_add(struct format_lines *fls, const char *start,
const char *end, const char *suffix)
{
struct format_line *fl;
fls->lines = xreallocarray(fls->lines, fls->nlines + 1,
sizeof *fls->lines);
fl = &fls->lines[fls->nlines++];
memcpy(fl, &fls->current, sizeof *fl);
xasprintf(&fl->chunk, "%.*s%s", (int)(end - start), start, suffix);
}
/*
* End the current line and carry the current style state over to the start of
* the next line. A marker is a single line so marker state does not carry.
*/
static void
format_draw_lines_break(struct format_lines *fls, struct format_style *fs,
enum style_list nomarker, const char *start, const char *end)
{
/* Close any open range at the break; the carried style reopens it. */
if (fs->sy.range_type != STYLE_RANGE_NONE)
format_draw_lines_add(fls, start, end, "#[norange]");
else
format_draw_lines_add(fls, start, end, "");
if (fs->sy.list == STYLE_LIST_LEFT_MARKER ||
fs->sy.list == STYLE_LIST_RIGHT_MARKER)
fs->sy.list = nomarker;
/* The next line starts with the style at the break. */
memset(&fls->current, 0, sizeof fls->current);
memcpy(&fls->current.fs, fs, sizeof fls->current.fs);
}
/* A line is classified by the list state its text was drawn under. */
static void
format_draw_lines_text(struct format_lines *fls, const struct style *sy)
{
struct format_line *fl = &fls->current;
switch (sy->list) {
case STYLE_LIST_ON:
fl->in_list = 1;
break;
case STYLE_LIST_FOCUS:
fl->in_list = 1;
fl->has_focus = 1;
break;
case STYLE_LIST_LEFT_MARKER:
if (fl->marker == 0)
fl->marker = 1;
break;
case STYLE_LIST_RIGHT_MARKER:
if (fl->marker == 0)
fl->marker = 2;
break;
case STYLE_LIST_OFF:
break;
}
}
/*
* Split an expanded format into lines at nl styles and literal newlines,
* walking the string and parsing styles the same way as format_draw.
*/
static void
format_draw_lines_split(struct format_lines *fls, const char *expanded,
const struct grid_cell *base)
{
struct format_style fs;
struct style saved_sy;
enum style_list nomarker = STYLE_LIST_OFF;
const char *cp, *start, *end;
char *tmp;
u_int n;
format_init_style(&fs, base);
memcpy(&fls->current.fs, &fs, sizeof fls->current.fs);
cp = start = expanded;
while (*cp != '\0') {
/* Handle sequences of # in the same way as format_draw. */
if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
format_draw_lines_text(fls, &fs.sy);
for (n = 1; cp[n] == '#'; n++)
/* nothing */;
if (cp[n] != '[') {
cp += n;
continue;
}
if ((n % 2) == 0)
cp += (n + 1);
else
cp += (n - 1);
continue;
}
/* Is this not a style? */
if (cp[0] != '#' || cp[1] != '[' || fs.sy.ignore) {
if (*cp == '\n') {
format_draw_lines_break(fls, &fs, nomarker,
start, cp);
cp++;
start = cp;
} else {
format_draw_lines_text(fls, &fs.sy);
cp++;
}
continue;
}
/* This is a style. Work out where the end is and parse it. */
end = format_skip(cp + 2, "]");
if (end == NULL)
break;
tmp = xstrndup(cp + 2, end - (cp + 2));
style_copy(&saved_sy, &fs.sy);
if (style_parse(&fs.sy, &fs.cur_def, tmp) != 0) {
free(tmp);
cp = end + 1;
continue;
}
free(tmp);
/* If this style has a fill colour, store it for later. */
if (fs.sy.fill != 8)
fs.fill = fs.sy.fill;
/* If this style pushed or popped the default, update it. */
format_update_defaults(&fs.sy, &saved_sy.gc, &fs.cur_def,
&fs.base_def);
/* Track the last list state that was not a marker. */
if (fs.sy.list != STYLE_LIST_LEFT_MARKER &&
fs.sy.list != STYLE_LIST_RIGHT_MARKER)
nomarker = fs.sy.list;
/*
* End the line after the style if it has a line break, so the
* rest of the style (such as norange) applies to the line.
*/
if (fs.sy.nl) {
fs.sy.nl = 0;
format_draw_lines_break(fls, &fs, nomarker, start,
end + 1);
cp = end + 1;
start = cp;
continue;
}
cp = end + 1;
}
if (cp != start || fls->nlines == 0)
format_draw_lines_add(fls, start, cp, "");
}
/* Draw one line and give any ranges it produces the line's y position. */
static void
format_draw_line(struct screen_write_ctx *octx,
const struct format_line *fl, u_int ox, u_int width, u_int py,
struct style_ranges *srs, int flags)
{
struct style_ranges line_srs;
struct style_range *sr, *sr1;
/* The list styles have already been used to choose the lines. */
flags |= FORMAT_DRAW_NO_LIST;
screen_write_cursormove(octx, ox, py, 0);
if (srs == NULL) {
format_draw1(octx, &fl->fs, width, fl->chunk, NULL, flags);
return;
}
style_ranges_init(&line_srs);
format_draw1(octx, &fl->fs, width, fl->chunk, &line_srs, flags);
TAILQ_FOREACH_SAFE(sr, &line_srs, entry, sr1) {
TAILQ_REMOVE(&line_srs, sr, entry);
sr->y = py;
TAILQ_INSERT_TAIL(srs, sr, entry);
}
}
/* Draw a format across multiple lines. */
u_int
format_draw_lines(struct screen_write_ctx *octx, const struct grid_cell *base,
u_int ox, u_int width, u_int height, const char *expanded,
struct style_ranges *srs, int flags)
{
struct format_lines fls;
struct format_line *fl;
u_int *top, *list, *bottom;
u_int n_top = 0, n_list = 0, n_bottom = 0;
u_int vis_top, vis_list, vis_bottom, lstart, f;
u_int i, y, ocy = octx->s->cy;
int first_list = -1, last_list = -1;
int up = -1, down = -1;
if (height == 0)
return (0);
memset(&fls, 0, sizeof fls);
format_draw_lines_split(&fls, expanded, base);
/* Find the list section and the marker lines. */
for (i = 0; i < fls.nlines; i++) {
if (fls.lines[i].marker == 1) {
if (up == -1)
up = i;
continue;
}
if (fls.lines[i].marker == 2) {
if (down == -1)
down = i;
continue;
}
if (fls.lines[i].in_list) {
if (first_list == -1)
first_list = i;
last_list = i;
}
}
/* Partition the lines into above, inside and below the list. */
top = xreallocarray(NULL, fls.nlines, sizeof *top);
list = xreallocarray(NULL, fls.nlines, sizeof *list);
bottom = xreallocarray(NULL, fls.nlines, sizeof *bottom);
for (i = 0; i < fls.nlines; i++) {
if (fls.lines[i].marker != 0)
continue;
if (first_list == -1 || (int)i < first_list)
top[n_top++] = i;
else if ((int)i <= last_list)
list[n_list++] = i;
else
bottom[n_bottom++] = i;
}
/* Trim the list first, then the bottom, then the top. */
vis_top = n_top;
vis_list = n_list;
vis_bottom = n_bottom;
while (vis_top + vis_list + vis_bottom > height) {
if (vis_list > 0)
vis_list--;
else if (vis_bottom > 0)
vis_bottom--;
else
vis_top--;
}
/* Scroll the list to keep the focus line visible. */
f = 0;
for (i = 0; i < n_list; i++) {
if (fls.lines[list[i]].has_focus) {
f = i;
break;
}
}
if (vis_list == 0 || f < vis_list / 2)
lstart = 0;
else
lstart = f - vis_list / 2;
if (lstart + vis_list > n_list)
lstart = n_list - vis_list;
/* Draw the lines. */
y = 0;
for (i = 0; i < vis_top; i++) {
fl = &fls.lines[top[i]];
format_draw_line(octx, fl, ox, width, ocy + y, srs, flags);
y++;
}
/* Markers replace the first and last list lines but never the focus. */
for (i = 0; i < vis_list; i++) {
fl = &fls.lines[list[lstart + i]];
if (lstart + i != f) {
if (i == 0 && lstart > 0 && up != -1)
fl = &fls.lines[up];
else if (i == vis_list - 1 &&
lstart + vis_list < n_list && down != -1)
fl = &fls.lines[down];
}
format_draw_line(octx, fl, ox, width, ocy + y, srs, flags);
y++;
}
for (i = 0; i < vis_bottom; i++) {
fl = &fls.lines[bottom[i]];
format_draw_line(octx, fl, ox, width, ocy + y, srs, flags);
y++;
}
for (i = 0; i < fls.nlines; i++)
free(fls.lines[i].chunk);
free(fls.lines);
free(top);
free(list);
free(bottom);
return (y);
}
/* Get width, taking #[] into account. */ /* Get width, taking #[] into account. */
u_int u_int
format_width(const char *expanded) format_width(const char *expanded)
@@ -1158,8 +1547,8 @@ format_width(const char *expanded)
} }
/* /*
* Trim on the left, taking #[] into account. Note, we copy the whole set of * Trim on the left, taking #[] into account. Copies the whole set of unescaped
* unescaped #s, but only add their escaped size to width. This is because the * #s, but only adds their escaped size to width. This is because the
* format_draw function will actually do the escaping. * format_draw function will actually do the escaping.
*/ */
char * char *

109
format.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: format.c,v 1.429 2026/10/15 07:25:07 nicm Exp $ */ /* $OpenBSD: format.c,v 1.430 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1366,27 +1366,44 @@ format_cb_mouse_line(struct format_tree *ft)
return (format_grid_line(gd, gd->hsize + y)); return (format_grid_line(gd, gd->hsize + y));
} }
/* Get mouse status position: 0 horizontal, 1 side, -1 unavailable. */
static int
format_mouse_status_at(struct format_tree *ft, u_int *x, u_int *y)
{
struct mouse_event *m = &ft->m;
if (!m->valid)
return (-1);
if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED))
return (-1);
*x = m->x;
*y = m->y;
if (m->statusat == 0 && m->y < m->statuslines)
return (0);
if (m->statusat > 0 && m->y >= (u_int)m->statusat) {
*y -= m->statusat;
return (0);
}
if (m->sideat == -1 ||
m->x < (u_int)m->sideat ||
m->x >= m->sideat + m->sidewidth)
return (-1);
*x -= m->sideat;
if (m->statusat == 0)
*y -= m->statuslines;
return (1);
}
/* Callback for mouse_status_line. */ /* Callback for mouse_status_line. */
static void * static void *
format_cb_mouse_status_line(struct format_tree *ft) format_cb_mouse_status_line(struct format_tree *ft)
{ {
char *value; u_int x, y;
u_int y;
if (!ft->m.valid) if (format_mouse_status_at(ft, &x, &y) == -1)
return (NULL); return (NULL);
if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED)) return (format_printf("%u", y));
return (NULL);
if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) {
y = ft->m.y;
} else if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) {
y = ft->m.y - ft->m.statusat;
} else
return (NULL);
xasprintf(&value, "%u", y);
return (value);
} }
/* Callback for mouse_status_range. */ /* Callback for mouse_status_range. */
@@ -1395,22 +1412,16 @@ format_cb_mouse_status_range(struct format_tree *ft)
{ {
struct style_range *sr; struct style_range *sr;
u_int x, y; u_int x, y;
int side;
if (!ft->m.valid) side = format_mouse_status_at(ft, &x, &y);
return (NULL); if (side == -1)
if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED))
return (NULL); return (NULL);
if (side)
sr = status_side_get_range(ft->c, x, y);
else
sr = status_get_range(ft->c, x, y);
if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) {
x = ft->m.x;
y = ft->m.y;
} else if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) {
x = ft->m.x;
y = ft->m.y - ft->m.statusat;
} else
return (NULL);
sr = status_get_range(ft->c, x, y);
if (sr == NULL) if (sr == NULL)
return (NULL); return (NULL);
switch (sr->type) { switch (sr->type) {
@@ -2064,6 +2075,24 @@ format_cb_mouse_utf8_flag(struct format_tree *ft)
return (NULL); return (NULL);
} }
/* Callback for mouse_absolute_x. */
static void *
format_cb_mouse_absolute_x(struct format_tree *ft)
{
if (!ft->m.valid)
return (NULL);
return (format_printf("%u", ft->m.x));
}
/* Callback for mouse_absolute_y. */
static void *
format_cb_mouse_absolute_y(struct format_tree *ft)
{
if (!ft->m.valid)
return (NULL);
return (format_printf("%u", ft->m.y));
}
/* Callback for mouse_x. */ /* Callback for mouse_x. */
static void * static void *
format_cb_mouse_x(struct format_tree *ft) format_cb_mouse_x(struct format_tree *ft)
@@ -2073,15 +2102,11 @@ format_cb_mouse_x(struct format_tree *ft)
if (!ft->m.valid) if (!ft->m.valid)
return (NULL); return (NULL);
if (format_mouse_status_at(ft, &x, &y) != -1)
return (format_printf("%u", x));
wp = cmd_mouse_pane(&ft->m, NULL, NULL); wp = cmd_mouse_pane(&ft->m, NULL, NULL);
if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0) if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
return (format_printf("%u", x)); return (format_printf("%u", x));
if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) {
if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines)
return (format_printf("%u", ft->m.x));
if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat)
return (format_printf("%u", ft->m.x));
}
return (NULL); return (NULL);
} }
@@ -2094,15 +2119,11 @@ format_cb_mouse_y(struct format_tree *ft)
if (!ft->m.valid) if (!ft->m.valid)
return (NULL); return (NULL);
if (format_mouse_status_at(ft, &x, &y) != -1)
return (format_printf("%u", y));
wp = cmd_mouse_pane(&ft->m, NULL, NULL); wp = cmd_mouse_pane(&ft->m, NULL, NULL);
if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0) if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
return (format_printf("%u", y)); return (format_printf("%u", y));
if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) {
if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines)
return (format_printf("%u", ft->m.y));
if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat)
return (format_printf("%u", ft->m.y - ft->m.statusat));
}
return (NULL); return (NULL);
} }
@@ -3761,6 +3782,12 @@ static const struct format_table_entry format_table[] = {
{ "last_window_index", FORMAT_TABLE_STRING, { "last_window_index", FORMAT_TABLE_STRING,
format_cb_last_window_index format_cb_last_window_index
}, },
{ "mouse_absolute_x", FORMAT_TABLE_STRING,
format_cb_mouse_absolute_x
},
{ "mouse_absolute_y", FORMAT_TABLE_STRING,
format_cb_mouse_absolute_y
},
{ "mouse_all_flag", FORMAT_TABLE_STRING, { "mouse_all_flag", FORMAT_TABLE_STRING,
format_cb_mouse_all_flag format_cb_mouse_all_flag
}, },

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: key-bindings.c,v 1.194 2026/10/08 07:50:05 nicm Exp $ */ /* $OpenBSD: key-bindings.c,v 1.195 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -534,6 +534,10 @@ key_bindings_init(void)
"bind -n MouseDown1Status { switch-client -t= }", "bind -n MouseDown1Status { switch-client -t= }",
"bind -n C-MouseDown1Status { swap-window -t@ }", "bind -n C-MouseDown1Status { swap-window -t@ }",
/* Mouse button 1 drag on side status border. */
"bind -n MouseDrag1StatusBorder { if -F '#{e|>=:#{client_width},3}' { run -C \"set -t '#{session_id}' side-status-width #{?#{==:#{side-status},left},#{e|+:#{?#{e|<:#{mouse_absolute_x},1},1,#{?#{e|>:#{mouse_absolute_x},#{e|-:#{client_width},2}},#{e|-:#{client_width},2},#{mouse_absolute_x}}},1},#{e|-:#{client_width},#{?#{e|<:#{mouse_absolute_x},1},1,#{?#{e|>:#{mouse_absolute_x},#{e|-:#{client_width},2}},#{e|-:#{client_width},2},#{mouse_absolute_x}}}}}\" } }",
"bind -n DoubleClick1StatusBorder { set -u side-status-width }",
/* Mouse button 1 down on default pane-border-format */ /* Mouse button 1 down on default pane-border-format */
"bind -n MouseDown1Control9 { display-menu -t= -xM -yM -O -T 'Kill pane #{pane_index}?' 'Yes' 'y' { kill-pane -t= } 'No' 'n' {}}", "bind -n MouseDown1Control9 { display-menu -t= -xM -yM -O -T 'Kill pane #{pane_index}?' 'Yes' 'y' { kill-pane -t= } 'No' 'n' {}}",
"bind -n MouseDown1Control8 { resize-pane -Z }", "bind -n MouseDown1Control8 { resize-pane -Z }",

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: mode-tree.c,v 1.104 2026/10/12 18:23:25 nicm Exp $ */ /* $OpenBSD: mode-tree.c,v 1.105 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -957,16 +957,19 @@ mode_tree_draw(struct mode_tree_data *mtd)
} }
} else { } else {
screen_write_clearendofline(&ctx, gc.bg); screen_write_clearendofline(&ctx, gc.bg);
format_draw(&ctx, &gc, prefix_width, prefix, NULL, 1); format_draw(&ctx, &gc, prefix_width, prefix, NULL,
FORMAT_DRAW_DEFAULT_COLOURS);
if (left != 0) { if (left != 0) {
screen_write_cursormove(&ctx, prefix_width, screen_write_cursormove(&ctx, prefix_width,
i - mtd->offset, 0); i - mtd->offset, 0);
format_draw(&ctx, &gc, left, text, NULL, 1); format_draw(&ctx, &gc, left, text, NULL,
FORMAT_DRAW_DEFAULT_COLOURS);
if (mti->text != NULL && width < w) { if (mti->text != NULL && width < w) {
screen_write_cursormove(&ctx, width, screen_write_cursormove(&ctx, width,
i - mtd->offset, 0); i - mtd->offset, 0);
format_draw(&ctx, &gc, w - width, format_draw(&ctx, &gc, w - width,
mti->text, NULL, 1); mti->text, NULL,
FORMAT_DRAW_DEFAULT_COLOURS);
} }
} }
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: options-table.c,v 1.253 2026/10/15 13:08:43 nicm Exp $ */ /* $OpenBSD: options-table.c,v 1.254 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -54,6 +54,9 @@ static const char *options_table_status_justify_list[] = {
static const char *options_table_status_position_list[] = { static const char *options_table_status_position_list[] = {
"top", "bottom", NULL "top", "bottom", NULL
}; };
static const char *options_table_side_status_list[] = {
"off", "left", "right", NULL
};
static const char *options_table_bell_action_list[] = { static const char *options_table_bell_action_list[] = {
"none", "any", "current", "other", NULL "none", "any", "current", "other", NULL
}; };
@@ -244,6 +247,50 @@ static const char *options_table_status_format_default[] = {
NULL NULL
}; };
/* Side status line format. */
#define OPTIONS_TABLE_SIDE_STATUS_WINDOW \
"#[range=window|#{window_index} #{E:window-status-style}]" \
"#I:#W#{?window_flags,#{window_flags}, }" \
"#[norange default]#[nl]"
#define OPTIONS_TABLE_SIDE_STATUS_CURRENT \
"#[range=window|#{window_index} list=focus " \
"#{?#{!=:#{E:window-status-current-style},default}," \
"#{E:window-status-current-style}," \
"#{E:window-status-style}" \
"}" \
"]" \
"#I:#W#{?window_flags,#{window_flags}, }" \
"#[norange list=on default]#[nl]"
#define OPTIONS_TABLE_SIDE_STATUS_BRANCH \
"#[acs fg=themelightgrey]#{?window_end_flag,mq,tq}+" \
"#[noacs default] "
#define OPTIONS_TABLE_SIDE_STATUS_FORMAT1 \
"#[list=on]" \
"#[list=left-marker]#[acs]-#[noacs]#[nl]" \
"#[list=right-marker]#[acs].#[noacs]#[nl]" \
"#{?#{e|>:#{server_sessions},1}," \
"#{S:" \
"#[range=session|#{session_id}]" \
"#{session_name}" \
"#[norange]#[nl]" \
"#{?#{==:#{session_name},#{client_session}}," \
"#{W:" \
OPTIONS_TABLE_SIDE_STATUS_BRANCH \
OPTIONS_TABLE_SIDE_STATUS_WINDOW \
"," \
OPTIONS_TABLE_SIDE_STATUS_BRANCH \
OPTIONS_TABLE_SIDE_STATUS_CURRENT \
"}" \
",}" \
"}" \
"," \
"#{W:" \
OPTIONS_TABLE_SIDE_STATUS_WINDOW \
"," \
OPTIONS_TABLE_SIDE_STATUS_CURRENT \
"}" \
"}"
/* Helpers for hook options. */ /* Helpers for hook options. */
#define OPTIONS_TABLE_HOOK(hook_name, default_value, hook_text) \ #define OPTIONS_TABLE_HOOK(hook_name, default_value, hook_text) \
{ .name = hook_name, \ { .name = hook_name, \
@@ -998,6 +1045,40 @@ const struct options_table_entry options_table[] = {
.text = "Format of the terminal title to set." .text = "Format of the terminal title to set."
}, },
{ .name = "side-status",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_side_status_list,
.default_num = 0,
.text = "Whether to draw a vertical status line at the left or "
"right of the terminal."
},
{ .name = "side-status-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = OPTIONS_TABLE_SIDE_STATUS_FORMAT1,
.text = "Format for the side status line."
},
{ .name = "side-status-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "fg=themegreen",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the side status line."
},
{ .name = "side-status-width",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SESSION,
.minimum = 1,
.maximum = SHRT_MAX,
.default_num = 14,
.text = "Width of the side status line."
},
{ .name = "silence-action", { .name = "silence-action",
.type = OPTIONS_TABLE_CHOICE, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: options.c,v 1.92 2026/07/27 19:15:58 nicm Exp $ */ /* $OpenBSD: options.c,v 1.93 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1407,10 +1407,13 @@ options_push_changes(const char *name)
} }
} }
if (strcmp(name, "status") == 0 || if (strcmp(name, "status") == 0 ||
strcmp(name, "status-interval") == 0) strcmp(name, "status-interval") == 0 ||
strcmp(name, "side-status") == 0)
status_timer_start_all(); status_timer_start_all();
if (strcmp(name, "status") == 0 || if (strcmp(name, "status") == 0 ||
strcmp(name, "status-position") == 0 || strcmp(name, "status-position") == 0 ||
strcmp(name, "side-status") == 0 ||
strcmp(name, "side-status-width") == 0 ||
strcmp(name, "pane-border-indicators") == 0 || strcmp(name, "pane-border-indicators") == 0 ||
strcmp(name, "pane-border-lines") == 0 || strcmp(name, "pane-border-lines") == 0 ||
strcmp(name, "pane-border-status") == 0 || strcmp(name, "pane-border-status") == 0 ||

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: resize.c,v 1.61 2026/10/15 22:28:41 nicm Exp $ */ /* $OpenBSD: resize.c,v 1.62 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -196,7 +196,7 @@ clients_calculate_size(int type, int current, struct client *c,
if (w == NULL || if (w == NULL ||
!control_get_window_size(loop, w->id, &cx, &cy) || !control_get_window_size(loop, w->id, &cx, &cy) ||
cx == 0 || cy == 0) { cx == 0 || cy == 0) {
cx = loop->tty.sx; cx = loop->tty.sx - status_side_size(loop);
cy = loop->tty.sy - status_line_size(loop); cy = loop->tty.sy - status_line_size(loop);
} }
@@ -301,7 +301,7 @@ default_window_size(struct client *c, struct session *s, struct window *w,
* client and no window, use the default size as for manual type. * client and no window, use the default size as for manual type.
*/ */
if (type == WINDOW_SIZE_LATEST && c != NULL && !ignore_client_size(c)) { if (type == WINDOW_SIZE_LATEST && c != NULL && !ignore_client_size(c)) {
*sx = c->tty.sx; *sx = c->tty.sx - status_side_size(c);
*sy = c->tty.sy - status_line_size(c); *sy = c->tty.sy - status_line_size(c);
*xpixel = c->tty.xpixel; *xpixel = c->tty.xpixel;
*ypixel = c->tty.ypixel; *ypixel = c->tty.ypixel;
@@ -478,6 +478,10 @@ recalculate_sizes_now(int now)
c->flags |= CLIENT_STATUSOFF; c->flags |= CLIENT_STATUSOFF;
else else
c->flags &= ~CLIENT_STATUSOFF; c->flags &= ~CLIENT_STATUSOFF;
if (s->sidestatusat != -1 && c->tty.sx <= s->sidestatuswidth)
c->flags |= CLIENT_SIDESTATUSOFF;
else
c->flags &= ~CLIENT_SIDESTATUSOFF;
} }
/* Walk each window and adjust the size. */ /* Walk each window and adjust the size. */

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: screen-redraw.c,v 1.168 2026/10/14 13:03:47 nicm Exp $ */ /* $OpenBSD: screen-redraw.c,v 1.169 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -100,6 +100,7 @@ enum redraw_phase {
#define REDRAW_PANE_SCROLLBAR 0x20 #define REDRAW_PANE_SCROLLBAR 0x20
#define REDRAW_STATUS 0x40 #define REDRAW_STATUS 0x40
#define REDRAW_MENU 0x80 #define REDRAW_MENU 0x80
#define REDRAW_SIDE_STATUS 0x100
/* Draw everything. */ /* Draw everything. */
#define REDRAW_ALL 0x7fffffff #define REDRAW_ALL 0x7fffffff
@@ -267,6 +268,7 @@ struct redraw_draw_ctx {
struct window_pane *marked; struct window_pane *marked;
u_int status_lines; u_int status_lines;
u_int side_left;
enum pane_lines pane_lines; enum pane_lines pane_lines;
struct grid_cell default_gc; struct grid_cell default_gc;
@@ -311,7 +313,7 @@ redraw_get_window_offset(struct client *c, u_int *ox, u_int *oy, u_int *sx,
tty_window_offset(&c->tty, ox, oy, sx, sy); tty_window_offset(&c->tty, ox, oy, sx, sy);
tty_sx = c->tty.sx; tty_sx = c->tty.sx - status_side_size(c);
tty_sy = c->tty.sy - status_line_size(c); tty_sy = c->tty.sy - status_line_size(c);
if (*sx < tty_sx) if (*sx < tty_sx)
*sx = tty_sx; *sx = tty_sx;
@@ -804,23 +806,24 @@ redraw_mark_pane_borders(struct redraw_build_ctx *bctx, struct window_pane *wp,
static void static void
redraw_mark_pane(struct redraw_build_ctx *bctx, struct window_pane *wp) redraw_mark_pane(struct redraw_build_ctx *bctx, struct window_pane *wp)
{ {
int sb_w = 0, sb_left = 0, overlay = 0; struct style *sb_style = &wp->scrollbar_style;
int sb_w = 0, sb_left = 0, overlay = 0;
if (!window_pane_is_visible(wp)) if (!window_pane_is_visible(wp))
return; return;
if (window_pane_scrollbar_visible(wp)) { if (window_pane_scrollbar_visible(wp)) {
overlay = window_pane_scrollbar_overlay(wp); overlay = window_pane_scrollbar_overlay(wp);
if (overlay) { if (!overlay)
sb_w = wp->scrollbar_style.width + sb_w = sb_style->width + sb_style->pad;
wp->scrollbar_style.pad; else {
sb_w = sb_style->width + sb_style->pad;
if (sb_w > (int)wp->sx) { if (sb_w > (int)wp->sx) {
sb_w = wp->scrollbar_style.width; sb_w = sb_style->width;
if (sb_w > (int)wp->sx) if (sb_w > (int)wp->sx)
sb_w = wp->sx; sb_w = wp->sx;
} }
} else }
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
} }
if (sb_w != 0 && bctx->w->sb_pos == PANE_SCROLLBARS_LEFT) if (sb_w != 0 && bctx->w->sb_pos == PANE_SCROLLBARS_LEFT)
sb_left = 1; sb_left = 1;
@@ -1301,7 +1304,7 @@ redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
style_ctx.palette = &wp->palette; style_ctx.palette = &wp->palette;
style_ctx.hyperlinks = s->hyperlinks; style_ctx.hyperlinks = s->hyperlinks;
px = span->data.p.px + (x - span->x); px = span->data.p.px + (x - dctx->side_left - span->x);
py = span->data.p.py; py = span->data.p.py;
tty_draw_line(tty, s, px, py, n, x, y, &style_ctx); tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
} }
@@ -1455,7 +1458,7 @@ redraw_draw_status_span(struct redraw_draw_ctx *dctx,
struct screen *s = &wp->status_screen; struct screen *s = &wp->status_screen;
u_int px, sx = screen_size_x(s); u_int px, sx = screen_size_x(s);
px = span->data.st.offset + (x - span->x); px = span->data.st.offset + (x - dctx->side_left - span->x);
if (px < sx) { if (px < sx) {
if (n > sx - px) if (n > sx - px)
n = sx - px; n = sx - px;
@@ -1517,7 +1520,7 @@ redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx,
sb_w = sb_style->width; sb_w = sb_style->width;
sb_pad = sb_style->pad; sb_pad = sb_style->pad;
off = x - span->x; off = x - dctx->side_left - span->x;
tty_cursor(tty, x, y); tty_cursor(tty, x, y);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
@@ -1551,7 +1554,7 @@ redraw_draw_menu_span(struct redraw_draw_ctx *dctx,
struct screen *s = menu_screen(span->data.m.md); struct screen *s = menu_screen(span->data.m.md);
u_int px; u_int px;
px = span->data.m.px + (x - span->x); px = span->data.m.px + (x - dctx->side_left - span->x);
tty_draw_line(tty, s, px, span->data.m.py, n, x, y, NULL); tty_draw_line(tty, s, px, span->data.m.py, n, x, y, NULL);
} }
@@ -1562,6 +1565,7 @@ redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
{ {
struct redraw_span_data *data = &span->data; struct redraw_span_data *data = &span->data;
enum redraw_span_type type = data->type; enum redraw_span_type type = data->type;
u_int x = dctx->side_left + span->x;
u_int width = span->width; u_int width = span->width;
if (type == REDRAW_SPAN_STATUS && ~data->st.wp->flags & PANE_NEWSTATUS) if (type == REDRAW_SPAN_STATUS && ~data->st.wp->flags & PANE_NEWSTATUS)
@@ -1572,21 +1576,21 @@ redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
switch (span->data.type) { switch (span->data.type) {
case REDRAW_SPAN_PANE: case REDRAW_SPAN_PANE:
if (phase == REDRAW_TEXT) if (phase == REDRAW_TEXT)
redraw_draw_pane_span(dctx, span, span->x, y, width); redraw_draw_pane_span(dctx, span, x, y, width);
break; break;
case REDRAW_SPAN_BORDER: case REDRAW_SPAN_BORDER:
case REDRAW_SPAN_EMPTY: case REDRAW_SPAN_EMPTY:
case REDRAW_SPAN_OUTSIDE: case REDRAW_SPAN_OUTSIDE:
redraw_draw_border_span(dctx, span, span->x, y, width); redraw_draw_border_span(dctx, span, x, y, width);
break; break;
case REDRAW_SPAN_STATUS: case REDRAW_SPAN_STATUS:
redraw_draw_status_span(dctx, span, span->x, y, width); redraw_draw_status_span(dctx, span, x, y, width);
break; break;
case REDRAW_SPAN_SCROLLBAR: case REDRAW_SPAN_SCROLLBAR:
redraw_draw_scrollbar_span(dctx, span, span->x, y, width); redraw_draw_scrollbar_span(dctx, span, x, y, width);
break; break;
case REDRAW_SPAN_MENU: case REDRAW_SPAN_MENU:
redraw_draw_menu_span(dctx, span, span->x, y, width); redraw_draw_menu_span(dctx, span, x, y, width);
break; break;
} }
} }
@@ -1794,6 +1798,9 @@ redraw_set_draw_context(struct redraw_draw_ctx *dctx,
dctx->flags |= REDRAW_STATUS_TOP; dctx->flags |= REDRAW_STATUS_TOP;
dctx->status_lines = lines; dctx->status_lines = lines;
if (status_side_at_column(c) == 0)
dctx->side_left = status_side_size(c);
if ((c->flags & CLIENT_UTF8) && tty_term_has(tty->term, TTYC_BIDI)) if ((c->flags & CLIENT_UTF8) && tty_term_has(tty->term, TTYC_BIDI))
dctx->flags |= REDRAW_ISOLATES; dctx->flags |= REDRAW_ISOLATES;
} }
@@ -1827,6 +1834,7 @@ redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
int ox = scene->ox, oy = scene->oy; int ox = scene->ox, oy = scene->oy;
int sx = scene->sx, sy = scene->sy; int sx = scene->sx, sy = scene->sy;
int line, cy, px, offset, width, wy; int line, cy, px, offset, width, wy;
u_int side_left = dctx->side_left;
if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0) if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0)
return; return;
@@ -1857,7 +1865,7 @@ redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
width = sx - px; width = sx - px;
redraw_make_pane_prompt(wp, &screen); redraw_make_pane_prompt(wp, &screen);
tty_draw_line(tty, &screen, offset, 0, width, px, cy, NULL); tty_draw_line(tty, &screen, offset, 0, width, side_left + px, cy, NULL);
screen_free(&screen); screen_free(&screen);
} }
@@ -1872,7 +1880,8 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
struct screen *sl; struct screen *sl;
struct redraw_scene *scene; struct redraw_scene *scene;
struct window_pane *loop; struct window_pane *loop;
u_int width, i, y, lines; u_int width, i, y, lines, side_width;
int side_x;
struct redraw_span *first; struct redraw_span *first;
int redraw; int redraw;
@@ -1895,6 +1904,16 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
} }
} }
if (flags & REDRAW_SIDE_STATUS) {
if (!status_side_redraw(c) &&
(~c->flags & CLIENT_REDRAWSTATUSALWAYS) &&
!REDRAW_IS_ALL(flags)) {
flags &= ~REDRAW_SIDE_STATUS;
if (flags == 0)
return;
}
}
if (log_get_level() != 0) { if (log_get_level() != 0) {
log_debug("%s: starting @%u redraw (%s)", c->name, w->id, log_debug("%s: starting @%u redraw (%s)", c->name, w->id,
redraw_flags_to_string(flags)); redraw_flags_to_string(flags));
@@ -1974,6 +1993,21 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
if (w->menu != NULL && (flags & REDRAW_MENU)) if (w->menu != NULL && (flags & REDRAW_MENU))
redraw_draw_menu_lines(&dctx); redraw_draw_menu_lines(&dctx);
if (flags & REDRAW_SIDE_STATUS) {
side_width = status_side_size(c);
side_x = status_side_at_column(c);
if (side_width != 0 && side_x != -1) {
if (dctx.flags & REDRAW_STATUS_TOP)
y = dctx.status_lines;
else
y = 0;
for (i = 0; i < status_side_rows(c); i++) {
tty_draw_line(tty, &c->side_status.screen, 0, i,
side_width, side_x, y + i, NULL);
}
}
}
if (flags & REDRAW_STATUS) { if (flags & REDRAW_STATUS) {
lines = dctx.status_lines; lines = dctx.status_lines;
if (c->message_string != NULL || c->prompt != NULL) if (c->message_string != NULL || c->prompt != NULL)
@@ -2039,7 +2073,8 @@ redraw_screen(struct client *c)
if (c->flags & CLIENT_REDRAWBORDERS) if (c->flags & CLIENT_REDRAWBORDERS)
flags |= (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS); flags |= (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS);
if (c->flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS)) if (c->flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))
flags |= (REDRAW_STATUS|REDRAW_PANE_STATUS); flags |= (REDRAW_STATUS|REDRAW_PANE_STATUS|
REDRAW_SIDE_STATUS);
if (c->flags & CLIENT_REDRAWMENU) if (c->flags & CLIENT_REDRAWMENU)
flags |= REDRAW_MENU; flags |= REDRAW_MENU;
if (c->session->curw->window->menu != NULL) if (c->session->curw->window->menu != NULL)
@@ -2093,6 +2128,7 @@ redraw_damage_draw_pane_prompt(struct redraw_draw_ctx *dctx,
struct tty *tty = &scene->c->tty; struct tty *tty = &scene->c->tty;
struct screen screen; struct screen screen;
u_int px = span->data.p.px, width, prompt_y; u_int px = span->data.p.px, width, prompt_y;
u_int x = dctx->side_left + span->x;
if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0) if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0)
return; return;
@@ -2107,7 +2143,7 @@ redraw_damage_draw_pane_prompt(struct redraw_draw_ctx *dctx,
width = span->width; width = span->width;
if (width > screen_size_x(&screen) - px) if (width > screen_size_x(&screen) - px)
width = screen_size_x(&screen) - px; width = screen_size_x(&screen) - px;
tty_draw_line(tty, &screen, px, 0, width, span->x, y, NULL); tty_draw_line(tty, &screen, px, 0, width, x, y, NULL);
} }
screen_free(&screen); screen_free(&screen);
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: screen-write.c,v 1.301 2026/10/14 13:36:37 nicm Exp $ */ /* $OpenBSD: screen-write.c,v 1.302 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -185,6 +185,8 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
if (status_at_line(c) == 0) if (status_at_line(c) == 0)
ttyctx->yoff += status_line_size(c); ttyctx->yoff += status_line_size(c);
if (status_side_at_column(c) == 0)
ttyctx->xoff += status_side_size(c);
return (1); return (1);
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: server-client.c,v 1.522 2026/10/15 08:21:11 nicm Exp $ */ /* $OpenBSD: server-client.c,v 1.523 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -192,6 +192,7 @@ server_client_create(int fd)
c->theme = THEME_UNKNOWN; c->theme = THEME_UNKNOWN;
status_init(c); status_init(c);
status_side_init(c);
c->flags |= CLIENT_FOCUSED; c->flags |= CLIENT_FOCUSED;
c->keytable = key_bindings_get_table("root", 1); c->keytable = key_bindings_get_table("root", 1);
@@ -419,6 +420,7 @@ server_client_lost(struct client *c)
tty_term_free_list(c->term_caps, c->term_ncaps); tty_term_free_list(c->term_caps, c->term_ncaps);
status_free(c); status_free(c);
status_side_free(c);
input_cancel_requests(c); input_cancel_requests(c);
free(c->title); free(c->title);
@@ -748,15 +750,123 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
return (KEYC_MOUSE_LOCATION_NOWHERE); return (KEYC_MOUSE_LOCATION_NOWHERE);
} }
/* Check for mouse events on the status lines. */
static int
server_client_check_mouse_status(struct client *c, struct mouse_event *m,
enum key_code_type type, u_int x, u_int y,
enum key_code_mouse_location *loc)
{
struct winlink *fwl;
struct window_pane *fwp;
struct session *s = c->session, *fs;
struct style_range *sr;
u_int n, sidey, separator;
*loc = KEYC_MOUSE_LOCATION_NOWHERE;
m->statusat = status_at_line(c);
m->statuslines = status_line_size(c);
m->sideat = status_side_at_column(c);
m->sidewidth = status_side_size(c);
if (c->tty.mouse_drag_flag != 0 &&
c->tty.mouse_drag_location == KEYC_MOUSE_LOCATION_STATUS_BORDER) {
*loc = KEYC_MOUSE_LOCATION_STATUS_BORDER;
return (0);
}
if (m->statusat != -1 &&
y >= (u_int)m->statusat &&
y < m->statusat + m->statuslines) {
sr = status_get_range(c, x, y - m->statusat);
} else {
if (m->sideat == -1 ||
x < (u_int)m->sideat ||
x >= m->sideat + m->sidewidth)
return (0);
if (m->statusat == 0)
sidey = m->statuslines;
else
sidey = 0;
if (y < sidey || y - sidey >= status_side_rows(c))
return (0);
separator = m->sideat;
if (separator == 0)
separator = m->sidewidth - 1;
if (m->sidewidth > 1 && x == separator) {
*loc = KEYC_MOUSE_LOCATION_STATUS_BORDER;
if (type == KEYC_TYPE_MOUSEDRAG &&
c->tty.mouse_drag_flag == 0)
c->tty.mouse_drag_status = 1;
return (0);
}
sr = status_side_get_range(c, x - m->sideat, y - sidey);
}
if (type == KEYC_TYPE_MOUSEDRAG && c->tty.mouse_drag_flag == 0)
c->tty.mouse_drag_status = 1;
if (sr == NULL) {
*loc = KEYC_MOUSE_LOCATION_STATUS_DEFAULT;
return (0);
}
switch (sr->type) {
case STYLE_RANGE_NONE:
return (-1);
case STYLE_RANGE_LEFT:
log_debug("mouse range: left");
*loc = KEYC_MOUSE_LOCATION_STATUS_LEFT;
break;
case STYLE_RANGE_RIGHT:
log_debug("mouse range: right");
*loc = KEYC_MOUSE_LOCATION_STATUS_RIGHT;
break;
case STYLE_RANGE_PANE:
fwp = window_pane_find_by_id(sr->argument);
if (fwp == NULL)
return (-1);
m->wp = sr->argument;
log_debug("mouse range: pane %%%u", m->wp);
*loc = KEYC_MOUSE_LOCATION_STATUS;
break;
case STYLE_RANGE_WINDOW:
fwl = winlink_find_by_index(&s->windows, sr->argument);
if (fwl == NULL)
return (-1);
m->w = fwl->window->id;
log_debug("mouse range: window @%u", m->w);
*loc = KEYC_MOUSE_LOCATION_STATUS;
break;
case STYLE_RANGE_SESSION:
fs = session_find_by_id(sr->argument);
if (fs == NULL)
return (-1);
m->s = sr->argument;
log_debug("mouse range: session $%u", m->s);
*loc = KEYC_MOUSE_LOCATION_STATUS;
break;
case STYLE_RANGE_USER:
log_debug("mouse range: user");
*loc = KEYC_MOUSE_LOCATION_STATUS;
break;
case STYLE_RANGE_CONTROL:
n = sr->argument; /* parsing keeps this < 10 */
log_debug("mouse range: control %u", n);
*loc = KEYC_MOUSE_LOCATION_CONTROL0 + n;
break;
}
return (0);
}
/* Check for mouse keys. */ /* Check for mouse keys. */
static key_code static key_code
server_client_check_mouse(struct client *c, struct key_event *event) server_client_check_mouse(struct client *c, struct key_event *event)
{ {
struct mouse_event *m = &event->m; struct mouse_event *m = &event->m;
struct session *s = c->session, *fs; struct session *s = c->session;
struct window *w = s->curw->window; struct window *w = s->curw->window;
struct winlink *fwl; struct window_pane *wp, *lwp = NULL;
struct window_pane *wp, *fwp, *lwp = NULL;
u_int x, y, sx, sy, px, py, n, sl_mpos = 0; u_int x, y, sx, sy, px, py, n, sl_mpos = 0;
u_int b, bn; u_int b, bn;
int ignore = 0; int ignore = 0;
@@ -858,69 +968,8 @@ have_event:
m->wp = -1; m->wp = -1;
m->ignore = ignore; m->ignore = ignore;
/* Is this on the status line? */ if (server_client_check_mouse_status(c, m, type, x, y, &loc) != 0)
m->statusat = status_at_line(c); return (KEYC_UNKNOWN);
m->statuslines = status_line_size(c);
if (m->statusat != -1 &&
y >= (u_int)m->statusat &&
y < m->statusat + m->statuslines) {
if (type == KEYC_TYPE_MOUSEDRAG && c->tty.mouse_drag_flag == 0)
c->tty.mouse_drag_status = 1;
sr = status_get_range(c, x, y - m->statusat);
if (sr == NULL) {
loc = KEYC_MOUSE_LOCATION_STATUS_DEFAULT;
} else {
switch (sr->type) {
case STYLE_RANGE_NONE:
return (KEYC_UNKNOWN);
case STYLE_RANGE_LEFT:
log_debug("mouse range: left");
loc = KEYC_MOUSE_LOCATION_STATUS_LEFT;
break;
case STYLE_RANGE_RIGHT:
log_debug("mouse range: right");
loc = KEYC_MOUSE_LOCATION_STATUS_RIGHT;
break;
case STYLE_RANGE_PANE:
fwp = window_pane_find_by_id(sr->argument);
if (fwp == NULL)
return (KEYC_UNKNOWN);
m->wp = sr->argument;
log_debug("mouse range: pane %%%u", m->wp);
loc = KEYC_MOUSE_LOCATION_STATUS;
break;
case STYLE_RANGE_WINDOW:
fwl = winlink_find_by_index(&s->windows,
sr->argument);
if (fwl == NULL)
return (KEYC_UNKNOWN);
m->w = fwl->window->id;
log_debug("mouse range: window @%u", m->w);
loc = KEYC_MOUSE_LOCATION_STATUS;
break;
case STYLE_RANGE_SESSION:
fs = session_find_by_id(sr->argument);
if (fs == NULL)
return (KEYC_UNKNOWN);
m->s = sr->argument;
log_debug("mouse range: session $%u", m->s);
loc = KEYC_MOUSE_LOCATION_STATUS;
break;
case STYLE_RANGE_USER:
log_debug("mouse range: user");
loc = KEYC_MOUSE_LOCATION_STATUS;
break;
case STYLE_RANGE_CONTROL:
n = sr->argument; /* parsing keeps this < 10 */
log_debug("mouse range: control %u", n);
loc = KEYC_MOUSE_LOCATION_CONTROL0 + n;
break;
}
}
}
/* /*
* Not on status line. Adjust position and check for border, pane, or * Not on status line. Adjust position and check for border, pane, or
@@ -933,7 +982,12 @@ have_event:
m->w = lwp->window->id; m->w = lwp->window->id;
} }
} else if (loc == KEYC_MOUSE_LOCATION_NOWHERE) { } else if (loc == KEYC_MOUSE_LOCATION_NOWHERE) {
px = x; if (m->sideat == 0 && x >= m->sidewidth)
px = x - m->sidewidth;
else if (m->sideat > 0 && x >= (u_int)m->sideat)
px = m->sideat - 1;
else
px = x;
if (m->statusat == 0 && y >= m->statuslines) if (m->statusat == 0 && y >= m->statuslines)
py = y - m->statuslines; py = y - m->statuslines;
else if (m->statusat > 0 && y >= (u_int)m->statusat) else if (m->statusat > 0 && y >= (u_int)m->statusat)
@@ -1097,6 +1151,7 @@ have_event:
* where the user grabbed. * where the user grabbed.
*/ */
if (c->tty.mouse_drag_flag == 0) { if (c->tty.mouse_drag_flag == 0) {
c->tty.mouse_drag_location = loc;
if (c->tty.mouse_drag_status) { if (c->tty.mouse_drag_status) {
c->tty.mouse_drag_x = x; c->tty.mouse_drag_x = x;
c->tty.mouse_drag_y = y; c->tty.mouse_drag_y = y;
@@ -1627,18 +1682,29 @@ server_client_handle_menu_key(struct client *c, struct key_event *event)
m = &new_event.m; m = &new_event.m;
m->statusat = status_at_line(c); m->statusat = status_at_line(c);
m->statuslines = status_line_size(c); m->statuslines = status_line_size(c);
m->sideat = status_side_at_column(c);
m->sidewidth = status_side_size(c);
tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
m->x += ox; if (m->sideat == 0 && m->x < m->sidewidth)
if (m->statusat == 0) { m->x = m->y = UINT_MAX;
if (m->y < m->statuslines) else if (m->sideat > 0 && m->x >= (u_int)m->sideat)
m->x = m->y = UINT_MAX;
else {
if (m->sideat == 0)
m->x -= m->sidewidth;
m->x += ox;
if (m->statusat == 0) {
if (m->y < m->statuslines)
m->x = m->y = UINT_MAX;
else
m->y = m->y - m->statuslines + oy;
} else if (m->statusat > 0 &&
m->y >= (u_int)m->statusat)
m->x = m->y = UINT_MAX; m->x = m->y = UINT_MAX;
else else
m->y = m->y - m->statuslines + oy; m->y += oy;
} else if (m->statusat > 0 && m->y >= (u_int)m->statusat) }
m->x = m->y = UINT_MAX;
else
m->y += oy;
} }
if (menu_key(c, w->menu, &new_event) == 1) if (menu_key(c, w->menu, &new_event) == 1)
@@ -2042,6 +2108,8 @@ server_client_prompt_cursor(struct client *c, struct window_pane *wp, int *mode,
if (window_position_is_visible(r, *cx)) { if (window_position_is_visible(r, *cx)) {
if (status_at_line(c) == 0) if (status_at_line(c) == 0)
*cy += status_line_size(c); *cy += status_line_size(c);
if (status_side_at_column(c) == 0)
*cx += status_side_size(c);
*mode |= MODE_CURSOR; *mode |= MODE_CURSOR;
} }
return (1); return (1);
@@ -2110,6 +2178,8 @@ server_client_reset_state(struct client *c)
cy -= oy; cy -= oy;
if (status_at_line(c) == 0) if (status_at_line(c) == 0)
cy += status_line_size(c); cy += status_line_size(c);
if (status_side_at_column(c) == 0)
cx += status_side_size(c);
} }
prompt = 1; prompt = 1;
} else { } else {
@@ -2149,6 +2219,8 @@ server_client_reset_state(struct client *c)
if (status_at_line(c) == 0) if (status_at_line(c) == 0)
cy += status_line_size(c); cy += status_line_size(c);
if (status_side_at_column(c) == 0)
cx += status_side_size(c);
} }
if (!cursor) if (!cursor)

211
status.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: status.c,v 1.274 2026/09/21 10:22:31 nicm Exp $ */ /* $OpenBSD: status.c,v 1.275 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -68,7 +68,9 @@ status_timer_start(struct client *c)
else else
evtimer_set(&c->status.timer, status_timer_callback, c); evtimer_set(&c->status.timer, status_timer_callback, c);
if (s != NULL && options_get_number(s->options, "status")) if (s != NULL &&
(options_get_number(s->options, "status") ||
options_get_number(s->options, "side-status")))
status_timer_callback(-1, 0, c); status_timer_callback(-1, 0, c);
} }
@@ -86,13 +88,28 @@ status_timer_start_all(void)
void void
status_update_cache(struct session *s) status_update_cache(struct session *s)
{ {
s->statuslines = options_get_number(s->options, "status"); struct options *oo = s->options;
s->statuslines = options_get_number(oo, "status");
if (s->statuslines == 0) if (s->statuslines == 0)
s->statusat = -1; s->statusat = -1;
else if (options_get_number(s->options, "status-position") == 0) else if (options_get_number(oo, "status-position") == 0)
s->statusat = 0; s->statusat = 0;
else else
s->statusat = 1; s->statusat = 1;
switch (options_get_number(oo, "side-status")) {
case 1:
s->sidestatusat = 0;
break;
case 2:
s->sidestatusat = 1;
break;
default:
s->sidestatusat = -1;
break;
}
s->sidestatuswidth = options_get_number(oo, "side-status-width");
} }
/* Get screen line of status line. -1 means off. */ /* Get screen line of status line. -1 means off. */
@@ -121,6 +138,44 @@ status_line_size(struct client *c)
return (s->statuslines); return (s->statuslines);
} }
/* Get width of side status line for client's session. 0 means off. */
u_int
status_side_size(struct client *c)
{
struct session *s = c->session;
if (c->flags & (CLIENT_SIDESTATUSOFF|CLIENT_CONTROL))
return (0);
if (s == NULL || s->sidestatusat == -1)
return (0);
return (s->sidestatuswidth);
}
/* Get starting column of side status line. -1 means off. */
int
status_side_at_column(struct client *c)
{
struct session *s = c->session;
u_int width = status_side_size(c);
if (width == 0)
return (-1);
if (s->sidestatusat == 0)
return (0);
return (c->tty.sx - width);
}
/* Get number of rows of side status line for client. */
u_int
status_side_rows(struct client *c)
{
u_int lines = status_line_size(c);
if (status_side_size(c) == 0 || c->tty.sy <= lines)
return (0);
return (c->tty.sy - lines);
}
/* Get the prompt line number for client's session. 1 means at the bottom. */ /* Get the prompt line number for client's session. 1 means at the bottom. */
u_int u_int
status_prompt_line_at(struct client *c) status_prompt_line_at(struct client *c)
@@ -313,6 +368,154 @@ status_redraw(struct client *c)
return (force || changed); return (force || changed);
} }
/* Prepare side status line. */
void
status_side_init(struct client *c)
{
struct side_status_line *ss = &c->side_status;
screen_init(&ss->screen, 1, 1, 0);
ss->linex = -1;
style_ranges_init(&ss->ranges);
}
/* Free side status line. */
void
status_side_free(struct client *c)
{
struct side_status_line *ss = &c->side_status;
style_ranges_free(&ss->ranges);
free(ss->expanded);
screen_free(&ss->screen);
}
/*
* Get the column of the ACS line separating the side status line from the
* window area, or -1 if there is none. The line takes the edge column
* nearest the window area out of the side status width.
*/
static int
status_side_line_at(struct client *c)
{
struct session *s = c->session;
u_int width = status_side_size(c);
if (width < 2)
return (-1);
if (s->sidestatusat == 0)
return (width - 1);
return (0);
}
/* Get side status range for position. */
struct style_range *
status_side_get_range(struct client *c, u_int x, u_int y)
{
struct side_status_line *ss = &c->side_status;
/* On a right side status line, content starts after the line. */
if (status_side_line_at(c) == 0) {
if (x == 0)
return (NULL);
x--;
}
return (style_ranges_get_range_at(&ss->ranges, x, y));
}
/* Draw side status line for client. Returns 1 if changed. */
int
status_side_redraw(struct client *c)
{
struct side_status_line *ss = &c->side_status;
struct session *s = c->session;
struct screen_write_ctx ctx;
struct grid_cell gc, lgc;
struct format_tree *ft;
char *expanded;
u_int width, rows, n;
int flags, force = 0, linex;
log_debug("%s enter", __func__);
/* No side status line? */
width = status_side_size(c);
rows = status_side_rows(c);
if (width == 0 || rows == 0)
return (0);
/* Create format tree. */
flags = FORMAT_STATUS;
if (c->flags & CLIENT_STATUSFORCE)
flags |= FORMAT_FORCE;
ft = format_create(c, NULL, FORMAT_NONE, flags);
format_defaults(ft, c, NULL, NULL, NULL);
/* Set up default colour. */
style_apply(&gc, s->options, "side-status-style", ft);
if (!grid_cells_equal(&gc, &ss->style)) {
force = 1;
memcpy(&ss->style, &gc, sizeof ss->style);
}
/* Resize the target screen. */
if (screen_size_x(&ss->screen) != width ||
screen_size_y(&ss->screen) != rows) {
screen_resize(&ss->screen, width, rows, 0);
force = 1;
}
/* The line moves if the side status line changes side. */
linex = status_side_line_at(c);
if (linex != ss->linex) {
force = 1;
ss->linex = linex;
}
/* Expand the format. */
expanded = format_expand_time(ft,
options_get_string(s->options, "side-status-format"));
format_free(ft);
/* Skip the redraw if nothing has changed. */
if (!force &&
ss->expanded != NULL &&
strcmp(expanded, ss->expanded) == 0) {
free(expanded);
log_debug("%s exit: unchanged", __func__);
return (0);
}
/* Draw the rows over a blank background. */
screen_write_start(&ctx, &ss->screen);
screen_write_cursormove(&ctx, 0, 0, 0);
for (n = 0; n < width * rows; n++)
screen_write_putc(&ctx, &gc, ' ');
style_ranges_free(&ss->ranges);
/* Draw the line on the edge next to the window area. */
if (linex != -1) {
memcpy(&lgc, &gc, sizeof lgc);
lgc.attr |= GRID_ATTR_CHARSET;
for (n = 0; n < rows; n++) {
screen_write_cursormove(&ctx, linex, n, 0);
screen_write_putc(&ctx, &lgc, 'x');
}
width--;
}
screen_write_cursormove(&ctx, 0, 0, 0);
format_draw_lines(&ctx, &gc, linex == 0 ? 1 : 0, width, rows, expanded,
&ss->ranges, 0);
screen_write_stop(&ctx);
free(ss->expanded);
ss->expanded = expanded;
log_debug("%s exit: changed", __func__);
return (1);
}
/* Escape # characters in a string so format_draw treats them as literal. */ /* Escape # characters in a string so format_draw treats them as literal. */
static char * static char *
status_message_escape(const char *s) status_message_escape(const char *s)

25
style.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: style.c,v 1.47 2026/06/29 17:08:52 nicm Exp $ */ /* $OpenBSD: style.c,v 1.48 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -41,10 +41,10 @@ static struct style style_default = {
STYLE_RANGE_NONE, 0, "", STYLE_RANGE_NONE, 0, "",
STYLE_WIDTH_DEFAULT, 0, STYLE_PAD_DEFAULT, STYLE_WIDTH_DEFAULT, 0, STYLE_PAD_DEFAULT,
0,
0,
STYLE_DEFAULT_BASE, STYLE_DEFAULT_BASE
0
}; };
/* /*
@@ -110,6 +110,8 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->default_type = STYLE_DEFAULT_POP; sy->default_type = STYLE_DEFAULT_POP;
else if (strcasecmp(tmp, "set-default") == 0) else if (strcasecmp(tmp, "set-default") == 0)
sy->default_type = STYLE_DEFAULT_SET; sy->default_type = STYLE_DEFAULT_SET;
else if (strcasecmp(tmp, "nl") == 0)
sy->nl = 1;
else if (strcasecmp(tmp, "nolist") == 0) else if (strcasecmp(tmp, "nolist") == 0)
sy->list = STYLE_LIST_OFF; sy->list = STYLE_LIST_OFF;
else if (strncasecmp(tmp, "list=", 5) == 0) { else if (strncasecmp(tmp, "list=", 5) == 0) {
@@ -577,3 +579,18 @@ style_ranges_get_range(struct style_ranges *srs, u_int x)
} }
return (NULL); return (NULL);
} }
/* Get range for position and row from style ranges. */
struct style_range *
style_ranges_get_range_at(struct style_ranges *srs, u_int x, u_int y)
{
struct style_range *sr;
if (srs == NULL)
return (NULL);
TAILQ_FOREACH(sr, srs, entry) {
if (x >= sr->start && x < sr->end && y == sr->y)
return (sr);
}
return (NULL);
}

43
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1187 2026/10/15 08:07:17 nicm Exp $ .\" $OpenBSD: tmux.1,v 1.1188 2026/10/16 07:59:06 nicm Exp $
.\" .\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\" .\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd $Mdocdate: October 15 2026 $ .Dd $Mdocdate: October 16 2026 $
.Dt TMUX 1 .Dt TMUX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -5638,6 +5638,41 @@ is on.
Formats are expanded, see the Formats are expanded, see the
.Sx FORMATS .Sx FORMATS
section. section.
.It Xo Ic side\-status
.Op Cm off | left | right
.Xc
Show a vertical status line at the left or right of the terminal.
The side status line uses the
.Ic side\-status\-style
colours, draws a vertical line on the edge next to the window area and is
updated every
.Ic status\-interval
seconds like the status line.
.It Ic side\-status\-format Ar format
Specify the format used for the side status line.
The format may produce multiple rows using the
.Ic nl
style or a newline character.
.Pp
Rows drawn with
.Ic list=on
are trimmed to the available height and scrolled to keep the row drawn with
.Ic list=focus
visible; rows drawn with
.Ic list=left\-marker
and
.Ic list=right\-marker
replace the first and last visible list row when rows are hidden above or
below.
.It Ic side\-status\-style Ar style
Set the style of the side status line.
For how to specify
.Ar style ,
see the
.Sx STYLES
section.
.It Ic side\-status\-width Ar width
Set the width of the side status line.
.It Xo Ic silence\-action .It Xo Ic silence\-action
.Op Cm any | none | current | other .Op Cm any | none | current | other
.Xc .Xc
@@ -7498,6 +7533,8 @@ The following variables are available, where appropriate:
.It Li "line" Ta "" Ta "Line number in the list" .It Li "line" Ta "" Ta "Line number in the list"
.It Li "loop_index" Ta "" Ta "Index of item in the W:, P:, S:, or L: loop" .It Li "loop_index" Ta "" Ta "Index of item in the W:, P:, S:, or L: loop"
.It Li "loop_last_flag" Ta "" Ta "1 if last window, pane, session, client in the W:, P:, S:, or L: loop" .It Li "loop_last_flag" Ta "" Ta "1 if last window, pane, session, client in the W:, P:, S:, or L: loop"
.It Li "mouse_absolute_x" Ta "" Ta "Mouse X position in terminal, if any"
.It Li "mouse_absolute_y" Ta "" Ta "Mouse Y position in terminal, if any"
.It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag" .It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag"
.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag"
.It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" .It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag"
@@ -7814,6 +7851,8 @@ and
.Ic list=right\-marker .Ic list=right\-marker
mark the text to be used to mark that text has been trimmed from the left or mark the text to be used to mark that text has been trimmed from the left or
right of the list if there is not enough space. right of the list if there is not enough space.
.It Ic nl
End the current row when drawn over multiple rows
.It Ic noattr .It Ic noattr
Do not copy attributes from the default style. Do not copy attributes from the default style.
.It Xo Ic push\-default , .It Xo Ic push\-default ,

42
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1464 2026/10/15 08:07:17 nicm Exp $ */ /* $OpenBSD: tmux.h,v 1.1465 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -179,6 +179,7 @@ enum key_code_mouse_location {
KEYC_MOUSE_LOCATION_STATUS_LEFT, KEYC_MOUSE_LOCATION_STATUS_LEFT,
KEYC_MOUSE_LOCATION_STATUS_RIGHT, KEYC_MOUSE_LOCATION_STATUS_RIGHT,
KEYC_MOUSE_LOCATION_STATUS_DEFAULT, KEYC_MOUSE_LOCATION_STATUS_DEFAULT,
KEYC_MOUSE_LOCATION_STATUS_BORDER,
KEYC_MOUSE_LOCATION_BORDER, KEYC_MOUSE_LOCATION_BORDER,
KEYC_MOUSE_LOCATION_SCROLLBAR_UP, KEYC_MOUSE_LOCATION_SCROLLBAR_UP,
KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER, KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER,
@@ -236,6 +237,7 @@ enum key_code_mouse_location {
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS_LEFT), \ KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS_LEFT), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS_RIGHT), \ KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS_RIGHT), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS_DEFAULT), \ KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS_DEFAULT), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, STATUS_BORDER), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, BORDER), \ KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, BORDER), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_UP), \ KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_UP), \
KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_SLIDER), \ KEYC_MOUSE_KEY(KEYC_ ## t, KEYC_TYPE_ ## t, SCROLLBAR_SLIDER), \
@@ -272,6 +274,7 @@ enum key_code_mouse_location {
{ #s "StatusLeft", KEYC_ ## name ## _STATUS_LEFT }, \ { #s "StatusLeft", KEYC_ ## name ## _STATUS_LEFT }, \
{ #s "StatusRight", KEYC_ ## name ## _STATUS_RIGHT }, \ { #s "StatusRight", KEYC_ ## name ## _STATUS_RIGHT }, \
{ #s "StatusDefault", KEYC_ ## name ## _STATUS_DEFAULT }, \ { #s "StatusDefault", KEYC_ ## name ## _STATUS_DEFAULT }, \
{ #s "StatusBorder", KEYC_ ## name ## _STATUS_BORDER }, \
{ #s "ScrollbarUp", KEYC_ ## name ## _SCROLLBAR_UP }, \ { #s "ScrollbarUp", KEYC_ ## name ## _SCROLLBAR_UP }, \
{ #s "ScrollbarSlider", KEYC_ ## name ## _SCROLLBAR_SLIDER }, \ { #s "ScrollbarSlider", KEYC_ ## name ## _SCROLLBAR_SLIDER }, \
{ #s "ScrollbarDown", KEYC_ ## name ## _SCROLLBAR_DOWN }, \ { #s "ScrollbarDown", KEYC_ ## name ## _SCROLLBAR_DOWN }, \
@@ -983,6 +986,7 @@ struct style_range {
u_int start; u_int start;
u_int end; /* not included */ u_int end; /* not included */
u_int y; /* row for multi-row areas */
TAILQ_ENTRY(style_range) entry; TAILQ_ENTRY(style_range) entry;
}; };
@@ -1023,10 +1027,10 @@ struct style {
int width; int width;
int width_percentage; int width_percentage;
int pad; int pad;
int nl;
u_int link;
enum style_default_type default_type; enum style_default_type default_type;
u_int link;
}; };
/* Cursor style. */ /* Cursor style. */
@@ -1626,6 +1630,8 @@ struct session {
int statusat; int statusat;
u_int statuslines; u_int statuslines;
int sidestatusat; /* -1 off, 0 left, 1 right */
u_int sidestatuswidth;
struct options *options; struct options *options;
@@ -1685,6 +1691,8 @@ struct mouse_event {
int statusat; int statusat;
u_int statuslines; u_int statuslines;
int sideat;
u_int sidewidth;
u_int x; u_int x;
u_int y; u_int y;
@@ -1825,6 +1833,7 @@ struct tty {
u_int mouse_last_b; u_int mouse_last_b;
int mouse_drag_flag; int mouse_drag_flag;
int mouse_drag_status; int mouse_drag_status;
enum key_code_mouse_location mouse_drag_location;
u_int mouse_drag_x; u_int mouse_drag_x;
u_int mouse_drag_y; u_int mouse_drag_y;
int mouse_scrolling_flag; int mouse_scrolling_flag;
@@ -2074,9 +2083,18 @@ struct status_line {
int references; int references;
u_int prompt_cx; u_int prompt_cx;
struct grid_cell style;
struct style_line_entry entries[STATUS_LINES_LIMIT];
};
/* Side status line. */
struct side_status_line {
struct screen screen;
struct grid_cell style; struct grid_cell style;
struct style_line_entry entries[STATUS_LINES_LIMIT]; char *expanded;
int linex;
struct style_ranges ranges;
}; };
/* File in client. */ /* File in client. */
@@ -2245,6 +2263,7 @@ struct client {
struct mouse_event click_event; struct mouse_event click_event;
struct status_line status; struct status_line status;
struct side_status_line side_status;
struct event cycle_timer; struct event cycle_timer;
enum client_theme theme; enum client_theme theme;
@@ -2291,6 +2310,7 @@ struct client {
#define CLIENT_WRITE_ACK 0x4000000000ULL #define CLIENT_WRITE_ACK 0x4000000000ULL
#define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL #define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL
#define CLIENT_CONTROL_DISCARD 0x10000000000ULL #define CLIENT_CONTROL_DISCARD 0x10000000000ULL
#define CLIENT_SIDESTATUSOFF 0x20000000000ULL
#define CLIENT_ALLREDRAWFLAGS \ #define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \ (CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUS| \
@@ -2759,8 +2779,13 @@ void events_fire_pane(const char *, struct window_pane *);
void events_fire_winlink(const char *, struct winlink *); void events_fire_winlink(const char *, struct winlink *);
/* format-draw.c */ /* format-draw.c */
#define FORMAT_DRAW_DEFAULT_COLOURS 0x1
#define FORMAT_DRAW_NO_LIST 0x2
void format_draw(struct screen_write_ctx *, const struct grid_cell *, void format_draw(struct screen_write_ctx *, const struct grid_cell *,
u_int, const char *, struct style_ranges *, int); u_int, const char *, struct style_ranges *, int);
u_int format_draw_lines(struct screen_write_ctx *,
const struct grid_cell *, u_int, u_int, u_int, const char *,
struct style_ranges *, int);
u_int format_width(const char *); u_int format_width(const char *);
char *format_trim_left(const char *, u_int); char *format_trim_left(const char *, u_int);
char *format_trim_right(const char *, u_int); char *format_trim_right(const char *, u_int);
@@ -3347,6 +3372,13 @@ void status_update_cache(struct session *);
u_int status_prompt_line_at(struct client *); u_int status_prompt_line_at(struct client *);
int status_at_line(struct client *); int status_at_line(struct client *);
u_int status_line_size(struct client *); u_int status_line_size(struct client *);
u_int status_side_size(struct client *);
int status_side_at_column(struct client *);
u_int status_side_rows(struct client *);
void status_side_init(struct client *);
void status_side_free(struct client *);
int status_side_redraw(struct client *);
struct style_range *status_side_get_range(struct client *, u_int, u_int);
struct style_range *status_get_range(struct client *, u_int, u_int); struct style_range *status_get_range(struct client *, u_int, u_int);
void status_init(struct client *); void status_init(struct client *);
void status_free(struct client *); void status_free(struct client *);
@@ -4182,6 +4214,8 @@ void style_set_scrollbar_style_from_option(struct style *,
void style_ranges_init(struct style_ranges *); void style_ranges_init(struct style_ranges *);
void style_ranges_free(struct style_ranges *); void style_ranges_free(struct style_ranges *);
struct style_range *style_ranges_get_range(struct style_ranges *, u_int); struct style_range *style_ranges_get_range(struct style_ranges *, u_int);
struct style_range *style_ranges_get_range_at(struct style_ranges *, u_int,
u_int);
/* spawn.c */ /* spawn.c */
struct winlink *spawn_window(struct spawn_context *, char **); struct winlink *spawn_window(struct spawn_context *, char **);

42
tty.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tty.c,v 1.490 2026/10/15 22:29:15 nicm Exp $ */ /* $OpenBSD: tty.c,v 1.491 2026/10/16 07:59:06 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -954,7 +954,11 @@ tty_window_bigger(struct tty *tty)
struct client *c = tty->client; struct client *c = tty->client;
struct window *w = c->session->curw->window; struct window *w = c->session->curw->window;
return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy); if (tty->sx - status_side_size(c) < w->sx)
return (1);
if (tty->sy - status_line_size(c) < w->sy)
return (1);
return (0);
} }
/* What offset should this window be drawn at? */ /* What offset should this window be drawn at? */
@@ -976,11 +980,12 @@ tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
struct client *c = tty->client; struct client *c = tty->client;
struct window *w = c->session->curw->window; struct window *w = c->session->curw->window;
struct window_pane *wp = w->active; struct window_pane *wp = w->active;
u_int cx, cy, lines; u_int cx, cy, lines, side;
lines = status_line_size(c); lines = status_line_size(c);
side = status_side_size(c);
if (tty->sx >= w->sx && tty->sy - lines >= w->sy) { if (tty->sx - side >= w->sx && tty->sy - lines >= w->sy) {
*ox = 0; *ox = 0;
*oy = 0; *oy = 0;
*sx = w->sx; *sx = w->sx;
@@ -990,7 +995,7 @@ tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
return (0); return (0);
} }
*sx = tty->sx; *sx = tty->sx - side;
*sy = tty->sy - lines; *sy = tty->sy - lines;
if (c->pan_window == w) { if (c->pan_window == w) {
@@ -1173,18 +1178,18 @@ tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
} else if (xoff < (int)ctx->wox && xoff + nx > ctx->wox + ctx->wsx) { } else if (xoff < (int)ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
/* Both left and right not visible. */ /* Both left and right not visible. */
*i = ctx->wox - xoff; *i = ctx->wox - xoff;
*x = 0; *x = ctx->xoff - ctx->rxoff;
*rx = ctx->wsx; *rx = ctx->wsx;
} else if (xoff < (int)ctx->wox) { } else if (xoff < (int)ctx->wox) {
/* Left not visible. */ /* Left not visible. */
*i = ctx->wox - (ctx->xoff + px); *i = ctx->wox - xoff;
*x = 0; *x = ctx->xoff - ctx->rxoff;
*rx = nx - *i; *rx = nx - *i;
} else { } else {
/* Right not visible. */ /* Right not visible. */
*i = 0; *i = 0;
*x = (ctx->xoff + px) - ctx->wox; *x = (ctx->xoff + px) - ctx->wox;
*rx = ctx->wsx - *x; *rx = ctx->wox + ctx->wsx - xoff;
} }
if (*rx > nx) if (*rx > nx)
fatalx("%s: x too big, %u > %u", __func__, *rx, nx); fatalx("%s: x too big, %u > %u", __func__, *rx, nx);
@@ -1267,18 +1272,18 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
} else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) { } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
/* Both left and right not visible. */ /* Both left and right not visible. */
*i = ctx->wox - xoff; *i = ctx->wox - xoff;
*x = 0; *x = ctx->xoff - ctx->rxoff;
*rx = ctx->wsx; *rx = ctx->wsx;
} else if (xoff < ctx->wox) { } else if (xoff < ctx->wox) {
/* Left not visible. */ /* Left not visible. */
*i = ctx->wox - (ctx->xoff + px); *i = ctx->wox - xoff;
*x = 0; *x = ctx->xoff - ctx->rxoff;
*rx = nx - *i; *rx = nx - *i;
} else { } else {
/* Right not visible. */ /* Right not visible. */
*i = 0; *i = 0;
*x = (ctx->xoff + px) - ctx->wox; *x = (ctx->xoff + px) - ctx->wox;
*rx = ctx->wsx - *x; *rx = ctx->wox + ctx->wsx - xoff;
} }
if (*rx > nx) if (*rx > nx)
fatalx("%s: x too big, %u > %u", __func__, *rx, nx); fatalx("%s: x too big, %u > %u", __func__, *rx, nx);
@@ -1919,8 +1924,8 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
return; return;
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) && if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) &&
(ctx->xoff + ctx->ocx < ctx->wox || (ctx->rxoff + ctx->ocx < ctx->wox ||
ctx->xoff + ctx->ocx + n > ctx->wox + ctx->wsx)) { ctx->rxoff + ctx->ocx + n > ctx->wox + ctx->wsx)) {
if ((~ctx->flags & TTY_CTX_WRAPPED) || if ((~ctx->flags & TTY_CTX_WRAPPED) ||
!tty_full_width(tty, ctx) || !tty_full_width(tty, ctx) ||
(tty->term->flags & TERM_NOAM) || (tty->term->flags & TERM_NOAM) ||
@@ -2112,10 +2117,11 @@ tty_margin_off(struct tty *tty)
static void static void
tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx)
{ {
u_int xoff = ctx->xoff - ctx->rxoff;
int l, r; int l, r;
l = ctx->xoff - ctx->wox; l = ctx->rxoff - ctx->wox;
r = ctx->xoff + ctx->sx - 1 - ctx->wox; r = ctx->rxoff + ctx->sx - 1 - ctx->wox;
if (l < 0) if (l < 0)
l = 0; l = 0;
@@ -2126,7 +2132,7 @@ tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx)
if (r > (int)ctx->wsx) if (r > (int)ctx->wsx)
r = ctx->wsx; r = ctx->wsx;
tty_margin(tty, l, r); tty_margin(tty, xoff + l, xoff + r);
} }
/* Set margin at absolute position. */ /* Set margin at absolute position. */