diff --git a/format.c b/format.c index 52eb4642..2e4bceb5 100644 --- a/format.c +++ b/format.c @@ -1756,6 +1756,23 @@ format_cb_pane_dead(struct format_tree *ft) return (NULL); } +/* Callback for pane_dead_signal. */ +static void * +format_cb_pane_dead_signal(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + const char *name; + + if (wp != NULL) { + if ((wp->flags & PANE_STATUSREADY) && WIFSIGNALED(wp->status)) { + name = sig2name(WTERMSIG(wp->status)); + return (format_printf("%s", name)); + } + return (NULL); + } + return (NULL); +} + /* Callback for pane_dead_status. */ static void * format_cb_pane_dead_status(struct format_tree *ft) @@ -1770,6 +1787,20 @@ format_cb_pane_dead_status(struct format_tree *ft) return (NULL); } +/* Callback for pane_dead_time. */ +static void * +format_cb_pane_dead_time(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + + if (wp != NULL) { + if (wp->flags & PANE_STATUSDRAWN) + return (&wp->dead_time); + return (NULL); + } + return (NULL); +} + /* Callback for pane_format. */ static void * format_cb_pane_format(struct format_tree *ft) @@ -2804,9 +2835,15 @@ static const struct format_table_entry format_table[] = { { "pane_dead", FORMAT_TABLE_STRING, format_cb_pane_dead }, + { "pane_dead_signal", FORMAT_TABLE_STRING, + format_cb_pane_dead_signal + }, { "pane_dead_status", FORMAT_TABLE_STRING, format_cb_pane_dead_status }, + { "pane_dead_time", FORMAT_TABLE_TIME, + format_cb_pane_dead_time + }, { "pane_fg", FORMAT_TABLE_STRING, format_cb_pane_fg }, diff --git a/options-table.c b/options-table.c index b8a5ccbb..31b0e372 100644 --- a/options-table.c +++ b/options-table.c @@ -1060,6 +1060,19 @@ const struct options_table_entry options_table[] = { "killed ('off' or 'failed') when the program inside exits." }, + { .name = "remain-on-exit-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_str = "Pane is dead (" + "#{?#{!=:#{pane_dead_status},}," + "status #{pane_dead_status},}" + "#{?#{!=:#{pane_dead_signal},}," + "signal #{pane_dead_signal},}, " + "#{t:pane_dead_time})", + .text = "Message shown after the program in a pane has exited, if " + "remain-on-exit is enabled." + }, + { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/server-fn.c b/server-fn.c index 92793093..2a79f3e3 100644 --- a/server-fn.c +++ b/server-fn.c @@ -310,9 +310,11 @@ server_destroy_pane(struct window_pane *wp, int notify) struct window *w = wp->window; struct screen_write_ctx ctx; struct grid_cell gc; - time_t t; - char tim[26]; int remain_on_exit; + const char *s; + char *expanded; + u_int sx = screen_size_x(&wp->base); + u_int sy = screen_size_y(&wp->base); if (wp->fd != -1) { #ifdef HAVE_UTEMPTER @@ -339,32 +341,26 @@ server_destroy_pane(struct window_pane *wp, int notify) return; wp->flags |= PANE_STATUSDRAWN; + gettimeofday(&wp->dead_time, NULL); if (notify) notify_pane("pane-died", wp); - screen_write_start_pane(&ctx, wp, &wp->base); - screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1); - screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1, 0); - screen_write_linefeed(&ctx, 1, 8); - memcpy(&gc, &grid_default_cell, sizeof gc); + s = options_get_string(wp->options, "remain-on-exit-format"); + if (*s != '\0') { + screen_write_start_pane(&ctx, wp, &wp->base); + screen_write_scrollregion(&ctx, 0, sy - 1); + screen_write_cursormove(&ctx, 0, sy - 1, 0); + screen_write_linefeed(&ctx, 1, 8); + memcpy(&gc, &grid_default_cell, sizeof gc); - time(&t); - ctime_r(&t, tim); - tim[strcspn(tim, "\n")] = '\0'; + expanded = format_single(NULL, s, NULL, NULL, NULL, wp); + format_draw(&ctx, &gc, sx, expanded, NULL, 0); + free(expanded); - if (WIFEXITED(wp->status)) { - screen_write_nputs(&ctx, -1, &gc, - "Pane is dead (status %d, %s)", - WEXITSTATUS(wp->status), - tim); - } else if (WIFSIGNALED(wp->status)) { - screen_write_nputs(&ctx, -1, &gc, - "Pane is dead (signal %s, %s)", - sig2name(WTERMSIG(wp->status)), - tim); + screen_write_stop(&ctx); } + wp->base.mode &= ~MODE_CURSOR; - screen_write_stop(&ctx); wp->flags |= PANE_REDRAW; return; } diff --git a/tmux.1 b/tmux.1 index 475ddfbb..a7a6846f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4471,6 +4471,11 @@ The pane may be reactivated with the .Ic respawn-pane command. .Pp +.It Ic remain-on-exit-format Ar string +Set the text shown at the bottom of exited panes when +.Ic remain-on-exit +is enabled. +.Pp .It Xo Ic synchronize-panes .Op Ic on | off .Xc @@ -5120,7 +5125,9 @@ The following variables are available, where appropriate: .It Li "pane_current_command" Ta "" Ta "Current command if available" .It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" +.It Li "pane_dead_signal" Ta "" Ta "Exit signal of process in dead pane" .It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" +.It Li "pane_dead_time" Ta "" Ta "Exit time of process in dead pane" .It Li "pane_fg" Ta "" Ta "Pane foreground colour" .It Li "pane_format" Ta "" Ta "1 if format is for a pane" .It Li "pane_height" Ta "" Ta "Height of pane" diff --git a/tmux.h b/tmux.h index 78283aa2..8a8535dd 100644 --- a/tmux.h +++ b/tmux.h @@ -1027,6 +1027,7 @@ struct window_pane { pid_t pid; char tty[TTY_NAME_MAX]; int status; + struct timeval dead_time; int fd; struct bufferevent *event;