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

Reply via email to