Hi list,
I had made a patch for myself to add a per-pane status bar a few weeks ago,
and saw yesterday that there was a ticket open for a similar feature.
Nicholas pointed out that he had already done work on this, but suggested I
post my patch anyway.
Firstly, sorry for duplicating effort. I hope this patch is helpful but I
understand if it isn't.
Secondly, the ticket does request top-of-pane title, where my patch
currently only does bottom-of-pane, but it uses the expansion system that
the regular status line uses, so the pane title and whatever else might be
wanted can go there (I use pane-tty and pane-title, for example). If it is
desired for this patch to move forward, I will work on adding a
pane-status-position option to allow it to be placed top-of-pane (as well
as other suggestions I receive)
The patch works by decreasing the pane height by 1 when pane-status is
enabled, and drawing the pane status line in the opened up space.
Adjustments are made for border drawing, window selection, and mouse
movements to work properly with the adjusted pane size. Redrawing is
handled similar to the regular status line, in that the statuses are
pre-rendered in memory and only sent to the screen if they differ from the
last render. Rendering is per-client to allow things like client-tty
expansions to work correctly.
Usage is documented in the man page, and the default options move the
pane-title from status-right to pane-status-format (leaving only the time
in status-right), allowing for a quick example when run with default
settings.
Thanks,
-- John O'Meara
diff --git a/layout.c b/layout.c
index b91b86c..c66ca00 100644
--- a/layout.c
+++ b/layout.c
@@ -170,6 +170,9 @@ layout_fix_panes(struct window *w, u_int wsx, u_int wsy)
struct window_pane *wp;
struct layout_cell *lc;
u_int sx, sy;
+ int has_pane_status;
+
+ has_pane_status = options_get_number(&w->options, "pane-status");
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL)
@@ -207,9 +210,9 @@ layout_fix_panes(struct window *w, u_int wsx, u_int wsy)
* is two because scroll regions cannot be one line.
*/
if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy)
- sy = lc->sy;
+ sy = lc->sy - has_pane_status;
else {
- sy = wsy - lc->yoff;
+ sy = wsy - lc->yoff - has_pane_status;
if (sy < 2)
sy = lc->sy;
}
@@ -527,9 +530,12 @@ layout_resize_pane_mouse(struct client *c)
struct window_pane *wp;
struct mouse_event *m = &c->tty.mouse;
int pane_border;
+ int has_pane_status;
w = c->session->curw->window;
+ has_pane_status = options_get_number(&w->options, "pane-status");
+
pane_border = 0;
if (m->event & MOUSE_EVENT_DRAG && m->flags & MOUSE_RESIZE_PANE) {
TAILQ_FOREACH(wp, &w->panes, entry) {
@@ -538,12 +544,12 @@ layout_resize_pane_mouse(struct client *c)
if (wp->xoff + wp->sx == m->lx &&
wp->yoff <= 1 + m->ly &&
- wp->yoff + wp->sy >= m->ly) {
+ wp->yoff + wp->sy + has_pane_status >= m->ly) {
layout_resize_pane(wp, LAYOUT_LEFTRIGHT,
m->x - m->lx);
pane_border = 1;
}
- if (wp->yoff + wp->sy == m->ly &&
+ if (wp->yoff + wp->sy + has_pane_status == m->ly &&
wp->xoff <= 1 + m->lx &&
wp->xoff + wp->sx >= m->lx) {
layout_resize_pane(wp, LAYOUT_TOPBOTTOM,
@@ -557,8 +563,8 @@ layout_resize_pane_mouse(struct client *c)
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((wp->xoff + wp->sx == m->x &&
wp->yoff <= 1 + m->y &&
- wp->yoff + wp->sy >= m->y) ||
- (wp->yoff + wp->sy == m->y &&
+ wp->yoff + wp->sy + has_pane_status >= m->y) ||
+ (wp->yoff + wp->sy + has_pane_status == m->y &&
wp->xoff <= 1 + m->x &&
wp->xoff + wp->sx >= m->x)) {
pane_border = 1;
diff --git a/options-table.c b/options-table.c
index 2bcf29b..be9af3f 100644
--- a/options-table.c
+++ b/options-table.c
@@ -396,7 +396,7 @@ const struct options_table_entry session_options_table[] = {
{ .name = "status-right",
.type = OPTIONS_TABLE_STRING,
- .default_str = " \"#{=21:pane_title}\" %H:%M %d-%b-%y"
+ .default_str = "%H:%M %d-%b-%y"
},
{ .name = "status-right-attr",
@@ -652,6 +652,39 @@ const struct options_table_entry window_options_table[] = {
.default_str = "default"
},
+ { .name = "pane-status",
+ .type = OPTIONS_TABLE_FLAG,
+ .default_num = 1
+ },
+
+ { .name = "pane-status-attr",
+ .type = OPTIONS_TABLE_ATTRIBUTES,
+ .default_num = 0,
+ .style = "pane-status-style"
+ },
+
+ { .name = "pane-status-bg",
+ .type = OPTIONS_TABLE_COLOUR,
+ .default_num = 2,
+ .style = "pane-status-style"
+ },
+
+ { .name = "pane-status-fg",
+ .type = OPTIONS_TABLE_COLOUR,
+ .default_num = 0,
+ .style = "pane-status-style"
+ },
+
+ { .name = "pane-status-format",
+ .type = OPTIONS_TABLE_STRING,
+ .default_str = "[#T]"
+ },
+
+ { .name = "pane-status-style",
+ .type = OPTIONS_TABLE_STYLE,
+ .default_str = "default"
+ },
+
{ .name = "remain-on-exit",
.type = OPTIONS_TABLE_FLAG,
.default_num = 0
diff --git a/resize.c b/resize.c
index 9ad73c8..edf44d5 100644
--- a/resize.c
+++ b/resize.c
@@ -128,8 +128,16 @@ recalculate_sizes(void)
forced |= WINDOW_FORCEHEIGHT;
}
+#if 0 /* This optimization to not resize panes if the window size doesn't
+ change fails to handle the case where pane sizes may have changed
+ due to options like 'pane-status' changing. Unfortunately, we
+ can't at this point know if this function was called because of an
+ option change, let alone specific option changes. For now, we'll
+ just skip the optimization and take the computational hit rather
+ than have a stale display. */
if (w->sx == ssx && w->sy == ssy)
continue;
+#endif
log_debug("window size %u,%u (was %u,%u)", ssx, ssy, w->sx,
w->sy);
diff --git a/screen-redraw.c b/screen-redraw.c
index c2b2ece..a36a2fe 100644
--- a/screen-redraw.c
+++ b/screen-redraw.c
@@ -54,13 +54,17 @@ void screen_redraw_draw_number(struct client *, struct window_pane *);
int
screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py)
{
+ int has_pane_status;
+
+ has_pane_status = options_get_number(&wp->window->options, "pane-status");
+
/* Inside pane. */
if (px >= wp->xoff && px < wp->xoff + wp->sx &&
- py >= wp->yoff && py < wp->yoff + wp->sy)
+ py >= wp->yoff && py < wp->yoff + wp->sy + has_pane_status)
return (0);
/* Left/right borders. */
- if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= wp->yoff + wp->sy) {
+ if ((wp->yoff == 0 || py >= wp->yoff - 1 + has_pane_status) && py <= wp->yoff + wp->sy + has_pane_status) {
if (wp->xoff != 0 && px == wp->xoff - 1)
return (1);
if (px == wp->xoff + wp->sx)
@@ -71,7 +75,7 @@ screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py)
if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= wp->xoff + wp->sx) {
if (wp->yoff != 0 && py == wp->yoff - 1)
return (1);
- if (py == wp->yoff + wp->sy)
+ if (py == wp->yoff + wp->sy + has_pane_status)
return (1);
}
@@ -106,6 +110,9 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py,
struct window *w = c->session->curw->window;
struct window_pane *wp;
int borders;
+ int has_pane_status;
+
+ has_pane_status = options_get_number(&w->options, "pane-status");
if (px > w->sx || py > w->sy)
return (CELL_OUTSIDE);
@@ -119,7 +126,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py,
if ((wp->xoff != 0 && px < wp->xoff - 1) ||
px > wp->xoff + wp->sx ||
(wp->yoff != 0 && py < wp->yoff - 1) ||
- py > wp->yoff + wp->sy)
+ py > wp->yoff + wp->sy + has_pane_status)
continue;
/* If definitely inside, return so. */
@@ -180,6 +187,10 @@ int
screen_redraw_check_active(u_int px, u_int py, int type, struct window *w,
struct window_pane *wp)
{
+ int has_pane_status;
+
+ has_pane_status = options_get_number(&wp->window->options, "pane-status");
+
/* Is this off the active pane border? */
if (screen_redraw_cell_border1(w->active, px, py) != 1)
return (0);
@@ -204,7 +215,7 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w,
}
/* Check if the pane covers the whole height. */
- if (wp->yoff == 0 && wp->sy == w->sy) {
+ if (wp->yoff == 0 && wp->sy + has_pane_status == w->sy) {
/* This can either be the left pane or the right pane. */
if (wp->xoff == 0) { /* left pane */
if (wp == w->active)
@@ -217,6 +228,24 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w,
return (type);
}
+void
+screen_redraw_draw_pane_status(struct client *c, u_int top)
+{
+ struct window *w = c->session->curw->window;
+ struct window_pane *wp;
+ struct pane_status *ps = TAILQ_FIRST(&c->pane_statuses);
+
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (!window_pane_visible(wp))
+ continue;
+ if (!ps) {
+ fatalx("pane status not found");
+ }
+ tty_draw_line(&c->tty, &ps->status, 0, wp->xoff, top + wp->yoff + wp->sy);
+ ps = TAILQ_NEXT(ps, entry);
+ }
+}
+
/* Redraw entire screen. */
void
screen_redraw_screen(struct client *c, int draw_panes, int draw_status,
@@ -226,6 +255,7 @@ screen_redraw_screen(struct client *c, int draw_panes, int draw_status,
struct tty *tty = &c->tty;
u_int top;
int status, spos;
+ int draw_pane_status;
/* Suspended clients should not be updated. */
if (c->flags & CLIENT_SUSPENDED)
@@ -238,6 +268,10 @@ screen_redraw_screen(struct client *c, int draw_panes, int draw_status,
else
status = options_get_number(oo, "status");
top = 0;
+ if (draw_status && options_get_number(&c->session->curw->window->options, "pane-status"))
+ draw_pane_status = 1;
+ else
+ draw_pane_status = 0;
if (status && spos == 0)
top = 1;
if (!status)
@@ -249,6 +283,9 @@ screen_redraw_screen(struct client *c, int draw_panes, int draw_status,
screen_redraw_draw_panes(c, top);
if (draw_status)
screen_redraw_draw_status(c, top);
+ if (draw_pane_status){
+ screen_redraw_draw_pane_status(c, top);
+ }
tty_reset(tty);
}
diff --git a/server-client.c b/server-client.c
index f7ce35c..fa278af 100644
--- a/server-client.c
+++ b/server-client.c
@@ -749,6 +749,7 @@ server_client_check_redraw(struct client *c)
redraw = status_prompt_redraw(c);
else
redraw = status_redraw(c);
+ redraw |= pane_status_redraw(c);
if (!redraw)
c->flags &= ~CLIENT_STATUS;
}
diff --git a/status.c b/status.c
index 5f8895f..b7546bb 100644
--- a/status.c
+++ b/status.c
@@ -38,7 +38,7 @@ void status_job_free(void *);
void status_job_callback(struct job *);
char *status_print(struct client *, struct winlink *, time_t,
struct grid_cell *);
-char *status_replace(struct client *, struct winlink *, const char *, time_t);
+char *status_replace(struct client *, struct winlink *, struct window_pane *wp, const char *, time_t);
void status_replace1(struct client *, char **, char **, char *, size_t);
void status_message_callback(int, short, void *);
@@ -87,7 +87,7 @@ status_redraw_get_left(struct client *c, time_t t, int utf8flag,
style_apply_update(gc, &s->options, "status-left-style");
template = options_get_string(&s->options, "status-left");
- left = status_replace(c, NULL, template, t);
+ left = status_replace(c, NULL, NULL, template, t);
*size = options_get_number(&s->options, "status-left-length");
leftlen = screen_write_cstrlen(utf8flag, "%s", left);
@@ -109,7 +109,7 @@ status_redraw_get_right(struct client *c, time_t t, int utf8flag,
style_apply_update(gc, &s->options, "status-right-style");
template = options_get_string(&s->options, "status-right");
- right = status_replace(c, NULL, template, t);
+ right = status_replace(c, NULL, NULL, template, t);
*size = options_get_number(&s->options, "status-right-length");
rightlen = screen_write_cstrlen(utf8flag, "%s", right);
@@ -362,6 +362,86 @@ out:
return (1);
}
+/* Draw status for panes */
+int
+pane_status_redraw(struct client *c)
+{
+ struct screen_write_ctx ctx;
+ int utf8flag;
+ struct grid_cell stdgc;
+ char *msg;
+ struct window *w = c->session->curw->window;
+ struct window_pane *wp;
+ struct pane_statuses old_pane_statuses;
+ struct pane_status *pane_status;
+ struct pane_status *t;
+ int diff = 0;
+ unsigned int len;
+
+ if (options_get_number(&w->options, "pane-status") == 0) {
+ return 0;
+ }
+
+ memcpy(&old_pane_statuses, &c->pane_statuses, sizeof old_pane_statuses);
+ TAILQ_INIT(&c->pane_statuses);
+
+ utf8flag = options_get_number(&c->session->options, "status-utf8");
+
+ /* Create set of statuses for each pane */
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (!window_pane_visible(wp))
+ continue;
+ pane_status = malloc(sizeof *pane_status);
+ if (!pane_status) {
+ continue;
+ }
+ memcpy(&stdgc, &grid_default_cell, sizeof stdgc);
+ colour_set_fg(&stdgc, options_get_number(&wp->window->options, "pane-status-fg"));
+ colour_set_bg(&stdgc, options_get_number(&wp->window->options, "pane-status-bg"));
+ stdgc.attr |= options_get_number(&wp->window->options, "pane-status-attr");
+ screen_init(&pane_status->status, wp->sx, 1, 0);
+ msg = status_replace(c, NULL, wp, options_get_string(&wp->window->options, "pane-status-format"), time(NULL));
+ len = screen_write_strlen(utf8flag, "%s", msg);
+ if (len > wp->sx) {
+ len = wp->sx;
+ }
+ screen_write_start(&ctx, NULL, &pane_status->status);
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_cnputs(&ctx, len, &stdgc, utf8flag, "%s", msg);
+ screen_write_stop(&ctx);
+ free(msg);
+ TAILQ_INSERT_TAIL(&c->pane_statuses, pane_status, entry);
+ }
+
+ /* Check if any of the status lines have changed, or if the number
+ * of status lines have changed. */
+ {
+ struct pane_status *old;
+ struct pane_status *new;
+ old = TAILQ_FIRST(&old_pane_statuses);
+ new = TAILQ_FIRST(&c->pane_statuses);
+ while (old != TAILQ_END(&old_pane_statuses) && new != TAILQ_END(&c->pane_statuses)) {
+ if (grid_compare(old->status.grid, new->status.grid) != 0) {
+ //screen_free(&old_status);
+ diff = 1;
+ }
+ //screen_free(&old_status);
+ old = TAILQ_NEXT(old, entry);
+ new = TAILQ_NEXT(new, entry);
+ }
+ if (old != TAILQ_END(&old_pane_statuses) || new != TAILQ_END(&c->pane_statuses)) {
+ diff = 1;
+ }
+ }
+ /* discard old */
+ TAILQ_FOREACH_SAFE(pane_status, &old_pane_statuses, entry, t) {
+ screen_free(&pane_status->status);
+ free(pane_status);
+ }
+ return diff;
+}
+
+
/* Replace a single special sequence (prefixed by #). */
void
status_replace1(struct client *c, char **iptr, char **optr, char *out,
@@ -431,7 +511,7 @@ skip_to:
/* Replace special sequences in fmt. */
char *
-status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
+status_replace(struct client *c, struct winlink *wl, struct window_pane *wp, const char *fmt, time_t t)
{
static char out[BUFSIZ];
char in[BUFSIZ], ch, *iptr, *optr, *expanded;
@@ -461,7 +541,7 @@ status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
*optr = '\0';
ft = format_create();
- format_defaults(ft, c, NULL, wl, NULL);
+ format_defaults(ft, c, NULL, wl, wp);
expanded = format_expand(ft, out);
format_free(ft);
return (expanded);
@@ -626,7 +706,7 @@ status_print(struct client *c, struct winlink *wl, time_t t,
else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE))
style_apply_update(gc, oo, "window-status-activity-style");
- text = status_replace(c, wl, fmt, t);
+ text = status_replace(c, wl, NULL, fmt, t);
return (text);
}
diff --git a/tmux.1 b/tmux.1
index 8a0879b..b277376 100644
--- a/tmux.1
+++ b/tmux.1
@@ -2617,7 +2617,7 @@ Set the position of the status line.
Display
.Ar string
to the right of the status bar.
-By default, the current window title in double quotes, the date and the time
+By default, the date and the time
are shown.
As with
.Ic status-left ,
@@ -2918,6 +2918,33 @@ see the
option.
Attributes are ignored.
.Pp
+.It Xo Ic pane-status
+.Op Ic on | off
+.Xc
+Show or hide a status line at the bottom of each pane.
+.Pp
+.It Ic pane-status-format Ar string
+Display
+.Ar string
+in the status bar of each pane in the window.
+By default, the pane title is shown in brackets.
+As with
+.Ic status-left ,
+.Ar string
+will be passed to
+.Xr strftime 3 ,
+character pairs are replaced, and UTF-8 is dependent on the
+.Ic status-utf8
+option.
+.Pp
+.It Ic pane-status-style Ar style
+Set the pane status line style.
+For how to specify
+.Ar style ,
+see the
+.Ic message-command-style
+opption.
+.Pp
.It Xo Ic remain-on-exit
.Op Ic on | off
.Xc
diff --git a/tmux.h b/tmux.h
index e296ac7..bc1e659 100644
--- a/tmux.h
+++ b/tmux.h
@@ -923,6 +923,12 @@ TAILQ_HEAD(window_panes, window_pane);
RB_HEAD(window_pane_tree, window_pane);
ARRAY_DECL(window_pane_list, struct window_pane *);
+struct pane_status {
+ struct screen status;
+ TAILQ_ENTRY(pane_status) entry;
+};
+TAILQ_HEAD(pane_statuses, pane_status);
+
/* Window structure. */
struct window {
u_int id;
@@ -1283,6 +1289,7 @@ struct client {
struct status_out_tree status_new;
struct timeval status_timer;
struct screen status;
+ struct pane_statuses pane_statuses;
#define CLIENT_TERMINAL 0x1
#define CLIENT_PREFIX 0x2
@@ -1923,6 +1930,7 @@ void status_free_jobs(struct status_out_tree *);
void status_update_jobs(struct client *);
void status_set_window_at(struct client *, u_int);
int status_redraw(struct client *);
+int pane_status_redraw(struct client *c);
void printflike(2, 3) status_message_set(struct client *, const char *, ...);
void status_message_clear(struct client *);
int status_message_redraw(struct client *);
@@ -2058,6 +2066,7 @@ void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int);
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int);
/* screen-redraw.c */
+void screen_redraw_draw_pane_status(struct client *c, u_int top);
void screen_redraw_screen(struct client *, int, int, int);
void screen_redraw_pane(struct client *, struct window_pane *);
diff --git a/window.c b/window.c
index fff2cfc..b7e6f53 100644
--- a/window.c
+++ b/window.c
@@ -1180,6 +1180,9 @@ window_pane_find_up(struct window_pane *wp)
u_int edge, left, right, end;
struct window_pane_list list;
int found;
+ int has_pane_status;
+
+ has_pane_status = options_get_number(&wp->window->options, "pane-status");
if (wp == NULL || !window_pane_visible(wp))
return (NULL);
@@ -1195,7 +1198,7 @@ window_pane_find_up(struct window_pane *wp)
TAILQ_FOREACH(next, &wp->window->panes, entry) {
if (next == wp || !window_pane_visible(next))
continue;
- if (next->yoff + next->sy + 1 != edge)
+ if (next->yoff + next->sy + 1 + has_pane_status != edge)
continue;
end = next->xoff + next->sx - 1;
@@ -1223,12 +1226,15 @@ window_pane_find_down(struct window_pane *wp)
u_int edge, left, right, end;
struct window_pane_list list;
int found;
+ int has_pane_status;
+
+ has_pane_status = options_get_number(&wp->window->options, "pane-status");
if (wp == NULL || !window_pane_visible(wp))
return (NULL);
ARRAY_INIT(&list);
- edge = wp->yoff + wp->sy + 1;
+ edge = wp->yoff + wp->sy + 1 + has_pane_status;
if (edge >= wp->window->sy)
edge = 0;
------------------------------------------------------------------------------
BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT
Develop your own process in accordance with the BPMN 2 standard
Learn Process modeling best practices with Bonita BPM through live exercises
http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_
source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
_______________________________________________
tmux-users mailing list
tmux-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tmux-users