When enabling 'status-text', support the ability to specify new status
lines with a delimiter -- the semi-colon ';'.  Standard formatting is
supported within the string.
---
 cmd-new-session.c |  14 ++++-
 options-table.c   |  24 +++++++++
 resize.c          |   5 +-
 screen-redraw.c   |   4 +-
 server-client.c   |   3 +-
 status.c          | 151 ++++++++++++++++++++++++++++++++++++++++++++++++------
 tmux.1            |   6 +++
 tmux.h            |  17 ++++--
 tty.c             |   2 +-
 9 files changed, 198 insertions(+), 28 deletions(-)

diff --git a/cmd-new-session.c b/cmd-new-session.c
index 2d671275..a0b04180 100644
--- a/cmd-new-session.c
+++ b/cmd-new-session.c
@@ -78,7 +78,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
        char                   **argv, *cause, *cp, *to_free = NULL;
        int                      detached, already_attached, idx, argc;
        int                      is_control = 0;
-       u_int                    sx, sy;
+       u_int                    sx, sy, lines;
        struct environ_entry    *envent;
        struct cmd_find_state    fs;
 
@@ -219,6 +219,18 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item 
*item)
        if (sy == 0)
                sy = 1;
 
+       /* Adjust for status line. */
+       if (options_get_number(global_s_options, "status")) {
+               lines = 1;
+               if (*options_get_string(global_s_options, "status-text") != 
'\0')
+                       lines = status_line_size(c);
+       } else
+               lines = 0;
+       if (sy > lines)
+               sy -= lines;
+       else
+               sy = 1;
+
        /* Figure out the command for the new window. */
        argc = -1;
        argv = NULL;
diff --git a/options-table.c b/options-table.c
index c9a69271..6c00d89f 100644
--- a/options-table.c
+++ b/options-table.c
@@ -380,6 +380,18 @@ const struct options_table_entry options_table[] = {
          .style = "status-style"
        },
 
+       { .name = "status-caption",
+         .type = OPTIONS_TABLE_FLAG,
+         .scope = OPTIONS_TABLE_SESSION,
+         .default_num = 1
+       },
+
+       { .name = "status-caption-text",
+         .type = OPTIONS_TABLE_STRING,
+         .scope = OPTIONS_TABLE_SESSION,
+         .default_str = ""
+       },
+
        { .name = "status-fg",
          .type = OPTIONS_TABLE_COLOUR,
          .scope = OPTIONS_TABLE_SESSION,
@@ -504,6 +516,18 @@ const struct options_table_entry options_table[] = {
          .default_str = "bg=green,fg=black"
        },
 
+       { .name = "status-text",
+         .type = OPTIONS_TABLE_STRING,
+         .scope = OPTIONS_TABLE_SESSION,
+         .default_str = ""
+       },
+
+       { .name = "status-text-style",
+         .type = OPTIONS_TABLE_STYLE,
+         .scope = OPTIONS_TABLE_SESSION,
+         .default_str = "bg=green,fg=black"
+       },
+
        { .name = "update-environment",
          .type = OPTIONS_TABLE_ARRAY,
          .scope = OPTIONS_TABLE_SESSION,
diff --git a/resize.c b/resize.c
index 8ed359b4..bd25599d 100644
--- a/resize.c
+++ b/resize.c
@@ -53,11 +53,11 @@ recalculate_sizes(void)
        int                      flag, is_zoomed, forced;
 
        RB_FOREACH(s, sessions, &sessions) {
-               lines = status_line_size(s);
 
                s->attached = 0;
                ssx = ssy = UINT_MAX;
                TAILQ_FOREACH(c, &clients, entry) {
+                       lines = status_line_size(c);
                        if (c->flags & CLIENT_SUSPENDED)
                                continue;
                        if ((c->flags & (CLIENT_CONTROL|CLIENT_SIZECHANGED)) ==
@@ -71,8 +71,7 @@ recalculate_sizes(void)
                                        c->flags |= CLIENT_STATUSOFF;
                                if ((~c->flags & CLIENT_STATUSOFF) &&
                                    !(c->flags & CLIENT_CONTROL) &&
-                                   c->tty.sy > lines &&
-                                   c->tty.sy - lines < ssy)
+                                   c->tty.sy > 1 && c->tty.sy - 1 < ssy)
                                        ssy = c->tty.sy - lines;
                                else if (c->tty.sy < ssy)
                                        ssy = c->tty.sy;
diff --git a/screen-redraw.c b/screen-redraw.c
index df966aff..9fc3c3b0 100644
--- a/screen-redraw.c
+++ b/screen-redraw.c
@@ -388,7 +388,7 @@ screen_redraw_screen(struct client *c, int draw_panes, int 
draw_status,
        if (c->flags & CLIENT_STATUSOFF)
                lines = 0;
        else
-               lines = status_line_size(c->session);
+               lines = status_line_size(c);
        if (c->message_string != NULL || c->prompt_string != NULL)
                lines = (lines == 0) ? 1 : lines;
 
@@ -425,7 +425,7 @@ screen_redraw_pane(struct client *c, struct window_pane *wp)
 
        yoff = wp->yoff;
        if (status_at_line(c) == 0)
-               yoff += status_line_size(c->session);
+               yoff += status_line_size(c);
 
        log_debug("%s: redraw pane %%%u (at %u,%u)", c->name, wp->id,
            wp->xoff, yoff);
diff --git a/server-client.c b/server-client.c
index 4bee7616..09e7d902 100644
--- a/server-client.c
+++ b/server-client.c
@@ -194,6 +194,7 @@ server_client_create(int fd)
        c->tty.sy = 24;
 
        screen_init(&c->status.status, c->tty.sx, 1, 0);
+       TAILQ_INIT(&c->status.status_lines);
 
        c->message_string = NULL;
        TAILQ_INIT(&c->message_log);
@@ -1223,7 +1224,7 @@ server_client_reset_state(struct client *c)
        if (status_at_line(c) != 0)
                lines = 0;
        else
-               lines = status_line_size(c->session);
+               lines = status_line_size(c);
        if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - lines)
                tty_cursor(&c->tty, 0, 0);
        else
diff --git a/status.c b/status.c
index 275220c5..60033941 100644
--- a/status.c
+++ b/status.c
@@ -40,6 +40,8 @@ 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 *);
 
+static u_int    status_lines_parse(struct client *);
+
 static char    *status_prompt_find_history_file(void);
 static const char *status_prompt_up_history(u_int *);
 static const char *status_prompt_down_history(u_int *);
@@ -167,6 +169,74 @@ status_timer_callback(__unused int fd, __unused short 
events, void *arg)
        log_debug("client %p, status interval %d", c, (int)tv.tv_sec);
 }
 
+static u_int
+status_lines_parse(struct client *c)
+{
+       struct status_text      *st = NULL, *st1;
+       const char              *st_text;
+       char                    *st_text1, *text;
+       u_int                    size = 0, line = 1;
+
+       /* Blank string to indicate tearing down the status items. */
+       st_text = options_get_string(c->session->options, "status-text");
+       if (*st_text == '\0') {
+               /* Zeroed out.  No more extra lines. */
+               if (TAILQ_EMPTY(&c->status.status_lines))
+                       goto out;
+
+               TAILQ_FOREACH_SAFE(st, &c->status.status_lines, entry, st1) {
+                       TAILQ_REMOVE(&c->status.status_lines, st, entry);
+                       free((char *)st->text);
+                       free(st);
+               }
+               goto out;
+       }
+
+       st_text1 = xstrdup(st_text);
+       /* If the status_line cache is empty then add everything we know
+        * about wholesale as a special case.
+        */
+       if (TAILQ_EMPTY(&c->status.status_lines)) {
+               while ((text = strsep(&st_text1, ";")) != NULL) {
+                       if (*text == '\0')
+                               continue;
+
+                       st = xmalloc(sizeof *st);
+                       st->text = xstrdup(text);
+                       st->line = line;
+                       TAILQ_INSERT_HEAD(&c->status.status_lines, st, entry);
+
+                       line++;
+               }
+               goto out;
+       }
+
+       /* Tear down previous entries, and add new ones. */
+       TAILQ_FOREACH_SAFE(st, &c->status.status_lines, entry, st1) {
+               TAILQ_REMOVE(&c->status.status_lines, st, entry);
+               free((char *)st->text);
+               free(st);
+       }
+
+       while ((text = strsep(&st_text1, ";")) != NULL) {
+               st = xmalloc(sizeof *st);
+               st->text = xstrdup(text);
+               st->line = line;
+               TAILQ_INSERT_TAIL(&c->status.status_lines, st, entry);
+
+               line++;
+       }
+
+out:
+       size = 0;
+       TAILQ_FOREACH(st, &c->status.status_lines, entry) {
+               size++;
+       }
+
+       /* +1 to include the status line itself. */
+       return (size + 1);
+}
+
 /* Start status timer for client. */
 void
 status_timer_start(struct client *c)
@@ -215,7 +285,7 @@ status_at_line(struct client *c)
                return (-1);
        if (s->statusat != 1)
                return (s->statusat);
-       return (c->tty.sy - status_line_size(s));
+       return (c->tty.sy - status_line_size(c));
 }
 
 /*
@@ -224,11 +294,19 @@ status_at_line(struct client *c)
  * CLIENT_STATUSOFF flag is set for this).
  */
 u_int
-status_line_size(struct session *s)
+status_line_size(struct client *c)
 {
-       if (s->statusat == -1)
+       struct session  *s = c->session;
+       u_int            lines = 1;
+
+       if (s == NULL || !options_get_number(s->options, "status"))
                return (0);
-       return (1);
+
+       lines = status_lines_parse(c);
+
+       if (s->sy < lines)
+               return (s->sy);
+       return (lines);
 }
 
 /* Retrieve options for left string. */
@@ -307,14 +385,15 @@ status_redraw(struct client *c)
        struct session          *s = c->session;
        struct winlink          *wl;
        struct screen            old_status, window_list;
-       struct grid_cell         stdgc, lgc, rgc, gc;
+       struct grid_cell         stdgc, stextgc, lgc, rgc, gc;
        struct options          *oo;
+       struct status_text      *st;
        time_t                   t;
-       char                    *left, *right;
-       const char              *sep;
-       u_int                    offset, needed, lines;
+       char                    *left, *right, *expanded;
+       const char              *sl, *sep;
+       u_int                    offset, needed, lines, i, x;
        u_int                    wlstart, wlwidth, wlavailable, wloffset, 
wlsize;
-       size_t                   llen, rlen, seplen;
+       size_t                   llen, rlen, seplen, expandedlen;
        int                      larrow, rarrow;
 
        /* Delete the saved status line, if any. */
@@ -325,7 +404,7 @@ status_redraw(struct client *c)
        }
 
        /* No status line? */
-       lines = status_line_size(s);
+       lines = status_line_size(c);
        if (c->tty.sy == 0 || lines == 0)
                return (1);
        left = right = NULL;
@@ -336,13 +415,16 @@ status_redraw(struct client *c)
 
        /* Set up default colour. */
        style_apply(&stdgc, s->options, "status-style");
+       style_apply(&stextgc, s->options, "status-text-style");
 
        /* Create the target screen. */
        memcpy(&old_status, &c->status.status, sizeof old_status);
        screen_init(&c->status.status, c->tty.sx, lines, 0);
        screen_write_start(&ctx, NULL, &c->status.status);
-       for (offset = 0; offset < lines * c->tty.sx; offset++)
-               screen_write_putc(&ctx, &stdgc, ' ');
+       for (i = 0; i < lines; i++) {
+               for (offset = 0; offset < c->tty.sx; offset++)
+                       screen_write_putc(&ctx, &stdgc, ' ');
+       }
        screen_write_stop(&ctx);
 
        /* If the height is too small, blank status line. */
@@ -512,6 +594,34 @@ draw:
        screen_write_fast_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
        screen_free(&window_list);
 
+       /* Draw the status text line if any. */
+       i = 1;
+       TAILQ_FOREACH(st, &c->status.status_lines, entry) {
+               sl = st->text;
+               expanded = status_replace(c, NULL, sl, t);
+               expandedlen = screen_write_cstrlen("%s", expanded);
+               switch (options_get_number(s->options, "status-justify")) {
+               case 1: /* centred */
+                       if (expandedlen > c->tty.sx)
+                               x = 0;
+                       else
+                               x = c->tty.sx / 2 - expandedlen / 2;
+                       break;
+               case 2: /* right */
+                       if (expandedlen > c->tty.sx)
+                               x = 0;
+                       else
+                               x = c->tty.sx - expandedlen;
+                       break;
+               default:
+                       x = 0;
+                       break;
+               }
+               screen_write_cursormove(&ctx, x, st->line);
+               screen_write_cnputs(&ctx, c->tty.sx, &stextgc, "%s", expanded);
+               free(expanded);
+       }
+
        screen_write_stop(&ctx);
 
 out:
@@ -592,8 +702,7 @@ status_message_set(struct client *c, const char *fmt, ...)
        status_message_clear(c);
 
        if (c->status.old_status == NULL) {
-               c->status.old_status = xmalloc(sizeof
-                   *c->status.old_status);
+               c->status.old_status = xmalloc(sizeof *c->status.old_status);
                memcpy(c->status.old_status, &c->status.status,
                    sizeof *c->status.old_status);
                screen_init(&c->status.status, c->tty.sx, 1, 0);
@@ -661,7 +770,7 @@ status_message_redraw(struct client *c)
                return (0);
        memcpy(&old_status, &c->status.status, sizeof old_status);
 
-       lines = status_line_size(c->session);
+       lines = status_line_size(c);
        if (lines <= 1) {
                lines = 1;
                screen_init(&c->status.status, c->tty.sx, 1, 0);
@@ -678,7 +787,12 @@ status_message_redraw(struct client *c)
        screen_write_cursormove(&ctx, 0, 0);
        for (offset = 0; offset < lines * c->tty.sx; offset++)
                screen_write_putc(&ctx, &gc, ' ');
+       if (lines > 1) {
+               screen_write_copy(&ctx, &old_status, 0, 0, c->tty.sx, lines - 1,
+                   NULL, NULL);
+       }
        screen_write_cursormove(&ctx, 0, lines - 1);
+
        screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
        screen_write_stop(&ctx);
 
@@ -816,7 +930,7 @@ status_prompt_redraw(struct client *c)
                return (0);
        memcpy(&old_status, &c->status.status, sizeof old_status);
 
-       lines = status_line_size(c->session);
+       lines = status_line_size(c);
        if (lines <= 1) {
                lines = 1;
                screen_init(&c->status.status, c->tty.sx, 1, 0);
@@ -840,6 +954,11 @@ status_prompt_redraw(struct client *c)
                start = c->tty.sx;
 
        screen_write_start(&ctx, NULL, &c->status.status);
+
+       if (lines > 1) {
+               screen_write_copy(&ctx, &old_status, 0, 0, c->tty.sx, lines - 1,
+                   NULL, NULL);
+       }
        screen_write_cursormove(&ctx, 0, 0);
        for (offset = 0; offset < lines * c->tty.sx; offset++)
                screen_write_putc(&ctx, &gc, ' ');
diff --git a/tmux.1 b/tmux.1
index 5387e882..91cf7250 100644
--- a/tmux.1
+++ b/tmux.1
@@ -2960,6 +2960,12 @@ For how to specify
 see the
 .Ic message-command-style
 option.
+.It Ic status-text Ar text
+If
+.Ar text
+is not empty, the status line is shown as two lines in height with
+.Ar text
+on the second line.
 .It Ic update-environment[] Ar variable
 Set list of environment variables to be copied into the session environment
 when a new session is created or an existing session is attached.
diff --git a/tmux.h b/tmux.h
index 8c9ba561..0800d44f 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1315,10 +1315,19 @@ struct cmd_entry {
        enum cmd_retval  (*exec)(struct cmd *, struct cmdq_item *);
 };
 
+struct status_text {
+       const char      *text;
+       u_int            line;
+
+       TAILQ_ENTRY(status_text) entry;
+};
+
 struct status_line {
-       struct event     timer;
-       struct screen    status;
-       struct screen   *old_status;
+       struct event                    timer;
+       struct screen                   status;
+       struct screen                   *old_status;
+
+       TAILQ_HEAD(, status_text)        status_lines;
 };
 
 /* Client connection. */
@@ -1934,7 +1943,7 @@ void       status_timer_start(struct client *);
 void    status_timer_start_all(void);
 void    status_update_saved(struct session *s);
 int     status_at_line(struct client *);
-u_int   status_line_size(struct session *);
+u_int   status_line_size(struct client *);
 struct window *status_get_window_at(struct client *, u_int);
 int     status_redraw(struct client *);
 void printflike(2, 3) status_message_set(struct client *, const char *, ...);
diff --git a/tty.c b/tty.c
index 01208a25..ce8847fc 100644
--- a/tty.c
+++ b/tty.c
@@ -1061,7 +1061,7 @@ tty_write(void (*cmdfn)(struct tty *, const struct 
tty_ctx *),
                ctx->xoff = wp->xoff;
                ctx->yoff = wp->yoff;
                if (status_at_line(c) == 0)
-                       ctx->yoff += status_line_size(c->session);
+                       ctx->yoff += status_line_size(c);
 
                cmdfn(&c->tty, ctx);
        }
-- 
2.15.1

-- 
You received this message because you are subscribed to the Google Groups 
"tmux-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to tmux-users+unsubscr...@googlegroups.com.
To post to this group, send an email to tmux-users@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to