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 Nicholas Marriott
parent 647e15b038
commit a7e8801089
20 changed files with 1258 additions and 264 deletions

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>
@@ -68,7 +68,9 @@ status_timer_start(struct client *c)
else
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);
}
@@ -86,13 +88,28 @@ status_timer_start_all(void)
void
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)
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;
else
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. */
@@ -121,6 +138,44 @@ status_line_size(struct client *c)
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. */
u_int
status_prompt_line_at(struct client *c)
@@ -313,6 +368,154 @@ status_redraw(struct client *c)
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. */
static char *
status_message_escape(const char *s)