summaryrefslogtreecommitdiffstats
path: root/status.c
diff options
context:
space:
mode:
Diffstat (limited to 'status.c')
-rw-r--r--status.c425
1 files changed, 102 insertions, 323 deletions
diff --git a/status.c b/status.c
index de6d2716..467db6ad 100644
--- a/status.c
+++ b/status.c
@@ -29,14 +29,6 @@
#include "tmux.h"
-static char *status_redraw_get_left(struct client *, struct grid_cell *,
- size_t *);
-static char *status_redraw_get_right(struct client *, struct grid_cell *,
- size_t *);
-static char *status_print(struct client *, struct winlink *,
- struct grid_cell *);
-static char *status_replace(struct client *, struct winlink *,
- const char *);
static void status_message_callback(int, short, void *);
static void status_timer_callback(int, short, void *);
@@ -196,7 +188,8 @@ status_timer_start_all(void)
void
status_update_cache(struct session *s)
{
- if (!options_get_number(s->options, "status"))
+ s->statuslines = options_get_number(s->options, "status");
+ if (s->statuslines == 0)
s->statusat = -1;
else if (options_get_number(s->options, "status-position") == 0)
s->statusat = 0;
@@ -225,75 +218,35 @@ status_line_size(struct client *c)
if (c->flags & CLIENT_STATUSOFF)
return (0);
- if (s->statusat == -1)
- return (0);
- return (1);
-}
-
-/* Retrieve options for left string. */
-static char *
-status_redraw_get_left(struct client *c, struct grid_cell *gc, size_t *size)
-{
- struct session *s = c->session;
- const char *template;
- char *left;
- size_t leftlen;
-
- style_apply_update(gc, s->options, "status-left-style");
-
- template = options_get_string(s->options, "status-left");
- left = status_replace(c, NULL, template);
-
- *size = options_get_number(s->options, "status-left-length");
- leftlen = screen_write_cstrlen("%s", left);
- if (leftlen < *size)
- *size = leftlen;
- return (left);
+ return (s->statuslines);
}
-/* Retrieve options for right string. */
-static char *
-status_redraw_get_right(struct client *c, struct grid_cell *gc, size_t *size)
+/* Get window at window list position. */
+struct style_range *
+status_get_range(struct client *c, u_int x, u_int y)
{
- struct session *s = c->session;
- const char *template;
- char *right;
- size_t rightlen;
-
- style_apply_update(gc, s->options, "status-right-style");
-
- template = options_get_string(s->options, "status-right");
- right = status_replace(c, NULL, template);
+ struct status_line *sl = &c->status;
+ struct style_range *sr;
- *size = options_get_number(s->options, "status-right-length");
- rightlen = screen_write_cstrlen("%s", right);
- if (rightlen < *size)
- *size = rightlen;
- return (right);
+ if (y >= nitems(sl->entries))
+ return (NULL);
+ TAILQ_FOREACH(sr, &sl->entries[y].ranges, entry) {
+ if (x >= sr->start && x < sr->end)
+ return (sr);
+ }
+ return (NULL);
}
-/* Get window at window list position. */
-struct window *
-status_get_window_at(struct client *c, u_int x)
+/* Free all ranges. */
+static void
+status_free_ranges(struct style_ranges *srs)
{
- struct session *s = c->session;
- struct winlink *wl;
- struct options *oo;
- const char *sep;
- size_t seplen;
-
- x += c->status.window_list_offset;
- RB_FOREACH(wl, winlinks, &s->windows) {
- oo = wl->window->options;
-
- sep = options_get_string(oo, "window-status-separator");
- seplen = screen_write_cstrlen("%s", sep);
+ struct style_range *sr, *sr1;
- if (x < wl->status_width)
- return (wl->window);
- x -= wl->status_width + seplen;
+ TAILQ_FOREACH_SAFE(sr, srs, entry, sr1) {
+ TAILQ_REMOVE(srs, sr, entry);
+ free(sr);
}
- return (NULL);
}
/* Save old status line. */
@@ -327,6 +280,10 @@ void
status_init(struct client *c)
{
struct status_line *sl = &c->status;
+ u_int i;
+
+ for (i = 0; i < nitems(sl->entries); i++)
+ TAILQ_INIT(&sl->entries[i].ranges);
screen_init(&sl->screen, c->tty.sx, 1, 0);
sl->active = &sl->screen;
@@ -337,6 +294,12 @@ void
status_free(struct client *c)
{
struct status_line *sl = &c->status;
+ u_int i;
+
+ for (i = 0; i < nitems(sl->entries); i++) {
+ status_free_ranges(&sl->entries[i].ranges);
+ free((void *)sl->entries[i].expanded);
+ }
if (event_initialized(&sl->timer))
evtimer_del(&sl->timer);
@@ -352,19 +315,19 @@ status_free(struct client *c)
int
status_redraw(struct client *c)
{
- struct status_line *sl = &c->status;
- struct screen_write_ctx ctx;
- struct session *s = c->session;
- struct winlink *wl;
- struct screen old_screen, window_list;
- struct grid_cell stdgc, lgc, rgc, gc;
- struct options *oo;
- char *left, *right;
- const char *sep;
- u_int offset, needed, lines;
- u_int wlstart, wlwidth, wlavailable, wloffset, wlsize;
- size_t llen, rlen, seplen;
- int larrow, rarrow;
+ struct status_line *sl = &c->status;
+ struct status_line_entry *sle;
+ struct session *s = c->session;
+ struct screen_write_ctx ctx;
+ struct grid_cell gc;
+ u_int lines, i, width = c->tty.sx;
+ int flags, force = 0, changed = 0;
+ struct options_entry *o;
+ struct format_tree *ft;
+ const char *fmt;
+ char *expanded;
+
+ log_debug("%s enter", __func__);
/* Shouldn't get here if not the active screen. */
if (sl->active != &sl->screen)
@@ -374,257 +337,71 @@ status_redraw(struct client *c)
lines = status_line_size(c);
if (c->tty.sy == 0 || lines == 0)
return (1);
- left = right = NULL;
- larrow = rarrow = 0;
/* Set up default colour. */
- style_apply(&stdgc, s->options, "status-style");
-
- /* Create the target screen. */
- memcpy(&old_screen, sl->active, sizeof old_screen);
- screen_init(sl->active, c->tty.sx, lines, 0);
- screen_write_start(&ctx, NULL, sl->active);
- for (offset = 0; offset < lines * c->tty.sx; offset++)
- screen_write_putc(&ctx, &stdgc, ' ');
- screen_write_stop(&ctx);
-
- /* If the height is too small, blank status line. */
- if (c->tty.sy < lines)
- goto out;
-
- /* Work out left and right strings. */
- memcpy(&lgc, &stdgc, sizeof lgc);
- left = status_redraw_get_left(c, &lgc, &llen);
- memcpy(&rgc, &stdgc, sizeof rgc);
- right = status_redraw_get_right(c, &rgc, &rlen);
-
- /*
- * Figure out how much space we have for the window list. If there
- * isn't enough space, just show a blank status line.
- */
- needed = 0;
- if (llen != 0)
- needed += llen;
- if (rlen != 0)
- needed += rlen;
- if (c->tty.sx == 0 || c->tty.sx <= needed)
- goto out;
- wlavailable = c->tty.sx - needed;
-
- /* Calculate the total size needed for the window list. */
- wlstart = wloffset = wlwidth = 0;
- RB_FOREACH(wl, winlinks, &s->windows) {
- free(wl->status_text);
- memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
- wl->status_text = status_print(c, wl, &wl->status_cell);
- wl->status_width = screen_write_cstrlen("%s", wl->status_text);
-
- if (wl == s->curw)
- wloffset = wlwidth;
-
- oo = wl->window->options;
- sep = options_get_string(oo, "window-status-separator");
- seplen = screen_write_cstrlen("%s", sep);
- wlwidth += wl->status_width + seplen;
- }
-
- /* Create a new screen for the window list. */
- screen_init(&window_list, wlwidth, 1, 0);
-
- /* And draw the window list into it. */
- screen_write_start(&ctx, NULL, &window_list);
- RB_FOREACH(wl, winlinks, &s->windows) {
- screen_write_cnputs(&ctx, -1, &wl->status_cell, "%s",
- wl->status_text);
-
- oo = wl->window->options;
- sep = options_get_string(oo, "window-status-separator");
- screen_write_cnputs(&ctx, -1, &stdgc, "%s", sep);
- }
- screen_write_stop(&ctx);
-
- /* If there is enough space for the total width, skip to draw now. */
- if (wlwidth <= wlavailable)
- goto draw;
-
- /* Find size of current window text. */
- wlsize = s->curw->status_width;
-
- /*
- * If the current window is already on screen, good to draw from the
- * start and just leave off the end.
- */
- if (wloffset + wlsize < wlavailable) {
- if (wlavailable > 0) {
- rarrow = 1;
- wlavailable--;
- }
- wlwidth = wlavailable;
- } else {
- /*
- * Work out how many characters we need to omit from the
- * start. There are wlavailable characters to fill, and
- * wloffset + wlsize must be the last. So, the start character
- * is wloffset + wlsize - wlavailable.
- */
- if (wlavailable > 0) {
- larrow = 1;
- wlavailable--;
- }
-
- wlstart = wloffset + wlsize - wlavailable;
- if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
- rarrow = 1;
- wlstart++;
- wlavailable--;
- }
- wlwidth = wlavailable;
+ style_apply(&gc, s->options, "status-style");
+ if (!grid_cells_equal(&gc, &sl->style)) {
+ force = 1;
+ memcpy(&sl->style, &gc, sizeof sl->style);
}
- /* Bail if anything is now too small too. */
- if (wlwidth == 0 || wlavailable == 0) {
- screen_free(&window_list);
- goto out;
+ /* Resize the target screen. */
+ if (screen_size_x(&sl->screen) != width ||
+ screen_size_y(&sl->screen) != lines) {
+ if (screen_size_x(&sl->screen) != width)
+ force = 1;
+ screen_resize(&sl->screen, width, lines, 0);
+ changed = 1;
}
+ screen_write_start(&ctx, NULL, &sl->screen);
- /*
- * Now the start position is known, work out the state of the left and
- * right arrows.
- */
- offset = 0;
- RB_FOREACH(wl, winlinks, &s->windows) {
- if (wl->flags & WINLINK_ALERTFLAGS &&
- larrow == 1 && offset < wlstart)
- larrow = -1;
-
- offset += wl->status_width;
-
- if (wl->flags & WINLINK_ALERTFLAGS &&
- rarrow == 1 && offset > wlstart + wlwidth)
- rarrow = -1;
- }
+ /* 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);
-draw:
- /* Begin drawing. */
- screen_write_start(&ctx, NULL, sl->active);
+ /* Write the status lines. */
+ o = options_get(s->options, "status-format");
+ if (o == NULL)
+ screen_write_clearscreen(&ctx, gc.bg);
+ else {
+ for (i = 0; i < lines; i++) {
+ screen_write_cursormove(&ctx, 0, i, 0);
+
+ fmt = options_array_get(o, i);
+ if (fmt == NULL) {
+ screen_write_clearline(&ctx, gc.bg);
+ continue;
+ }
+ sle = &sl->entries[i];
- /* Draw the left string and arrow. */
- screen_write_cursormove(&ctx, 0, 0, 0);
- if (llen != 0)
- screen_write_cnputs(&ctx, llen, &lgc, "%s", left);
- if (larrow != 0) {
- memcpy(&gc, &stdgc, sizeof gc);
- if (larrow == -1)
- gc.attr ^= GRID_ATTR_REVERSE;
- screen_write_putc(&ctx, &gc, '<');
- }
+ expanded = format_expand_time(ft, fmt);
+ if (!force &&
+ sle->expanded != NULL &&
+ strcmp(expanded, sle->expanded) == 0) {
+ free(expanded);
+ continue;
+ }
+ changed = 1;
- /* Draw the right string and arrow. */
- if (rarrow != 0) {
- screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0, 0);
- memcpy(&gc, &stdgc, sizeof gc);
- if (rarrow == -1)
- gc.attr ^= GRID_ATTR_REVERSE;
- screen_write_putc(&ctx, &gc, '>');
- } else
- screen_write_cursormove(&ctx, c->tty.sx - rlen, 0, 0);
- if (rlen != 0)
- screen_write_cnputs(&ctx, rlen, &rgc, "%s", right);
+ screen_write_clearline(&ctx, gc.bg);
+ status_free_ranges(&sle->ranges);
+ format_draw(&ctx, &gc, width, expanded, &sle->ranges);
- /* Figure out the offset for the window list. */
- if (llen != 0)
- wloffset = llen;
- else
- wloffset = 0;
- if (wlwidth < wlavailable) {
- switch (options_get_number(s->options, "status-justify")) {
- case 1: /* centred */
- wloffset += (wlavailable - wlwidth) / 2;
- break;
- case 2: /* right */
- wloffset += (wlavailable - wlwidth);
- break;
+ free(sle->expanded);
+ sle->expanded = expanded;
}
}
- if (larrow != 0)
- wloffset++;
-
- /* Copy the window list. */
- sl->window_list_offset = -wloffset + wlstart;
- screen_write_cursormove(&ctx, wloffset, 0, 0);
- screen_write_fast_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
- screen_free(&window_list);
-
- /* Save left and right size. */
- sl->left_size = llen;
- sl->right_size = rlen;
-
screen_write_stop(&ctx);
-out:
- free(left);
- free(right);
-
- if (grid_compare(sl->active->grid, old_screen.grid) == 0) {
- screen_free(&old_screen);
- return (0);
- }
- screen_free(&old_screen);
- return (1);
-}
-
-/* Replace special sequences in fmt. */
-static char *
-status_replace(struct client *c, struct winlink *wl, const char *fmt)
-{
- struct format_tree *ft;
- char *expanded;
- u_int tag;
-
- if (fmt == NULL)
- return (xstrdup(""));
-
- if (wl != NULL)
- tag = FORMAT_WINDOW|wl->window->id;
- else
- tag = FORMAT_NONE;
- if (c->flags & CLIENT_STATUSFORCE)
- ft = format_create(c, NULL, tag, FORMAT_STATUS|FORMAT_FORCE);
- else
- ft = format_create(c, NULL, tag, FORMAT_STATUS);
- format_defaults(ft, c, NULL, wl, NULL);
-
- expanded = format_expand_time(ft, fmt);
-
+ /* Free the format tree. */
format_free(ft);
- return (expanded);
-}
-/* Return winlink status line entry and adjust gc as necessary. */
-static char *
-status_print(struct client *c, struct winlink *wl, struct grid_cell *gc)
-{
- struct options *oo = wl->window->options;
- struct session *s = c->session;
- const char *fmt;
- char *text;
-
- style_apply_update(gc, oo, "window-status-style");
- fmt = options_get_string(oo, "window-status-format");
- if (wl == s->curw) {
- style_apply_update(gc, oo, "window-status-current-style");
- fmt = options_get_string(oo, "window-status-current-format");
- }
- if (wl == TAILQ_FIRST(&s->lastw))
- style_apply_update(gc, oo, "window-status-last-style");
-
- if (wl->flags & WINLINK_BELL)
- style_apply_update(gc, oo, "window-status-bell-style");
- else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE))
- style_apply_update(gc, oo, "window-status-activity-style");
-
- text = status_replace(c, wl, fmt);
- return (text);
+ /* Return if the status line has changed. */
+ log_debug("%s exit: force=%d, changed=%d", __func__, force, changed);
+ return (force || changed);
}
/* Set a status line message. */
@@ -713,8 +490,9 @@ status_message_redraw(struct client *c)
style_apply(&gc, s->options, "message-style");
screen_write_start(&ctx, NULL, sl->active);
- screen_write_cursormove(&ctx, 0, 0, 0);
- for (offset = 0; offset < lines * c->tty.sx; offset++)
+ screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
+ screen_write_cursormove(&ctx, 0, lines - 1, 0);
+ for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, 0, lines - 1, 0);
screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
@@ -864,12 +642,13 @@ status_prompt_redraw(struct client *c)
start = c->tty.sx;
screen_write_start(&ctx, NULL, sl->active);
- screen_write_cursormove(&ctx, 0, 0, 0);
- for (offset = 0; offset < lines * c->tty.sx; offset++)
+ screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
+ screen_write_cursormove(&ctx, 0, lines - 1, 0);
+ for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' ');
- screen_write_cursormove(&ctx, 0, 0, 0);
+ screen_write_cursormove(&ctx, 0, lines - 1, 0);
screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string);
- screen_write_cursormove(&ctx, start, 0, 0);
+ screen_write_cursormove(&ctx, start, lines - 1, 0);
left = c->tty.sx - start;
if (left == 0)