diff options
author | Nicholas Marriott <nicm@cvs.openbsd.org> | 2020-06-05 07:33:58 +0000 |
---|---|---|
committer | Nicholas Marriott <nicm@cvs.openbsd.org> | 2020-06-05 07:33:58 +0000 |
commit | e3cc2ec00df34a3693cf153f1ddd8b26ca2f9a05 (patch) | |
tree | 2d3aa8f07be44135bc03fff8de69d69c5f5f749b | |
parent | faf5215f887b8e0980a8106f934ca771445dd730 (diff) |
Add support for pausing a pane when the output buffered for a control
mode client gets too far behind. The pause-after flag with a time is set
on the pane with refresh-client -f and a paused pane may be resumed with
refresh-client -A. GitHub issue 2217.
-rw-r--r-- | usr.bin/tmux/cmd-refresh-client.c | 6 | ||||
-rw-r--r-- | usr.bin/tmux/control.c | 65 | ||||
-rw-r--r-- | usr.bin/tmux/resize.c | 18 | ||||
-rw-r--r-- | usr.bin/tmux/server-client.c | 42 | ||||
-rw-r--r-- | usr.bin/tmux/tmux.1 | 49 | ||||
-rw-r--r-- | usr.bin/tmux/tmux.h | 11 |
6 files changed, 145 insertions, 46 deletions
diff --git a/usr.bin/tmux/cmd-refresh-client.c b/usr.bin/tmux/cmd-refresh-client.c index d6eadbe9e43..35163d64cf4 100644 --- a/usr.bin/tmux/cmd-refresh-client.c +++ b/usr.bin/tmux/cmd-refresh-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-refresh-client.c,v 1.36 2020/05/22 11:07:04 nicm Exp $ */ +/* $OpenBSD: cmd-refresh-client.c,v 1.37 2020/06/05 07:33:57 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> @@ -66,6 +66,8 @@ cmd_refresh_client_update_offset(struct client *tc, const char *value) control_set_pane_on(tc, wp); else if (strcmp(colon, "off") == 0) control_set_pane_off(tc, wp); + else if (strcmp(colon, "continue") == 0) + control_continue_pane(tc, wp); out: free(copy); @@ -168,7 +170,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) } tty_set_size(&tc->tty, x, y, 0, 0); tc->flags |= CLIENT_SIZECHANGED; - recalculate_sizes(); + recalculate_sizes_now(1); return (CMD_RETURN_NORMAL); } diff --git a/usr.bin/tmux/control.c b/usr.bin/tmux/control.c index 9d28b398bf3..56d0f0e7641 100644 --- a/usr.bin/tmux/control.c +++ b/usr.bin/tmux/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD: control.c,v 1.37 2020/06/02 08:17:27 nicm Exp $ */ +/* $OpenBSD: control.c,v 1.38 2020/06/05 07:33:57 nicm Exp $ */ /* * Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com> @@ -65,6 +65,7 @@ struct control_pane { int flags; #define CONTROL_PANE_OFF 0x1 +#define CONTROL_PANE_PAUSED 0x2 int pending_flag; TAILQ_ENTRY(control_pane) pending_entry; @@ -153,6 +154,19 @@ control_add_pane(struct client *c, struct window_pane *wp) return (cp); } +/* Discard output for a pane. */ +static void +control_discard_pane(struct client *c, struct control_pane *cp) +{ + struct control_state *cs = c->control_state; + struct control_block *cb, *cb1; + + TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { + TAILQ_REMOVE(&cp->blocks, cb, entry); + control_free_block(cs, cb); + } +} + /* Get actual pane for this client. */ static struct window_pane * control_window_pane(struct client *c, u_int pane) @@ -197,7 +211,7 @@ control_pane_offset(struct client *c, struct window_pane *wp, int *off) } cp = control_get_pane(c, wp); - if (cp == NULL) { + if (cp == NULL || (cp->flags & CONTROL_PANE_PAUSED)) { *off = 0; return (NULL); } @@ -216,7 +230,7 @@ control_set_pane_on(struct client *c, struct window_pane *wp) struct control_pane *cp; cp = control_get_pane(c, wp); - if (cp != NULL) { + if (cp != NULL && (cp->flags & CONTROL_PANE_OFF)) { cp->flags &= ~CONTROL_PANE_OFF; memcpy(&cp->offset, &wp->offset, sizeof cp->offset); memcpy(&cp->queued, &wp->offset, sizeof cp->queued); @@ -233,6 +247,21 @@ control_set_pane_off(struct client *c, struct window_pane *wp) cp->flags |= CONTROL_PANE_OFF; } +/* Continue a paused pane. */ +void +control_continue_pane(struct client *c, struct window_pane *wp) +{ + struct control_pane *cp; + + cp = control_get_pane(c, wp); + if (cp != NULL && (cp->flags & CONTROL_PANE_PAUSED)) { + cp->flags &= ~CONTROL_PANE_PAUSED; + memcpy(&cp->offset, &wp->offset, sizeof cp->offset); + memcpy(&cp->queued, &wp->offset, sizeof cp->queued); + control_write(c, "%%continue %%%u", wp->id); + } +} + /* Write a line. */ static void control_vwrite(struct client *c, const char *fmt, va_list ap) @@ -285,6 +314,7 @@ control_write_output(struct client *c, struct window_pane *wp) struct control_pane *cp; struct control_block *cb; size_t new_size; + uint64_t t; if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) return; @@ -296,8 +326,22 @@ control_write_output(struct client *c, struct window_pane *wp) return; } cp = control_add_pane(c, wp); - if (cp->flags & CONTROL_PANE_OFF) + if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED)) goto ignore; + if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { + cb = TAILQ_FIRST(&cp->blocks); + if (cb != NULL) { + t = get_timer(); + log_debug("%s: %s: %%%u is %lld behind", __func__, + c->name, wp->id, (long long)t - cb->t); + if (cb->t < t - c->pause_age) { + cp->flags |= CONTROL_PANE_PAUSED; + control_discard_pane(c, cp); + control_write(c, "%%pause %%%u", wp->id); + return; + } + } + } window_pane_get_new_data(wp, &cp->queued, &new_size); if (new_size == 0) @@ -585,20 +629,15 @@ control_start(struct client *c) } } -/* Flush all output for a client that is detaching. */ +/* Discard all output for a client. */ void -control_flush(struct client *c) +control_discard(struct client *c) { struct control_state *cs = c->control_state; struct control_pane *cp; - struct control_block *cb, *cb1; - RB_FOREACH(cp, control_panes, &cs->panes) { - TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { - TAILQ_REMOVE(&cp->blocks, cb, entry); - control_free_block(cs, cb); - } - } + RB_FOREACH(cp, control_panes, &cs->panes) + control_discard_pane(c, cp); } /* Stop control mode. */ diff --git a/usr.bin/tmux/resize.c b/usr.bin/tmux/resize.c index b37ddfbcb20..40448f671d3 100644 --- a/usr.bin/tmux/resize.c +++ b/usr.bin/tmux/resize.c @@ -1,4 +1,4 @@ -/* $OpenBSD: resize.c,v 1.40 2020/05/16 16:50:55 nicm Exp $ */ +/* $OpenBSD: resize.c,v 1.41 2020/06/05 07:33:57 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> @@ -227,7 +227,7 @@ done: } void -recalculate_size(struct window *w) +recalculate_size(struct window *w, int now) { struct session *s; struct client *c; @@ -348,10 +348,10 @@ recalculate_size(struct window *w) break; } if (w->flags & WINDOW_RESIZE) { - if (changed && w->new_sx == sx && w->new_sy == sy) + if (!now && changed && w->new_sx == sx && w->new_sy == sy) changed = 0; } else { - if (changed && w->sx == sx && w->sy == sy) + if (!now && changed && w->sx == sx && w->sy == sy) changed = 0; } @@ -360,7 +360,7 @@ recalculate_size(struct window *w) return; } log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy); - if (type == WINDOW_SIZE_MANUAL) + if (now || type == WINDOW_SIZE_MANUAL) resize_window(w, sx, sy, xpixel, ypixel); else { w->new_sx = sx; @@ -376,6 +376,12 @@ recalculate_size(struct window *w) void recalculate_sizes(void) { + recalculate_sizes_now(0); +} + +void +recalculate_sizes_now(int now) +{ struct session *s; struct client *c; struct window *w; @@ -407,5 +413,5 @@ recalculate_sizes(void) /* Walk each window and adjust the size. */ RB_FOREACH(w, windows, &windows) - recalculate_size(w); + recalculate_size(w, now); } diff --git a/usr.bin/tmux/server-client.c b/usr.bin/tmux/server-client.c index e5f9b3d9d92..6a677431c6d 100644 --- a/usr.bin/tmux/server-client.c +++ b/usr.bin/tmux/server-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server-client.c,v 1.354 2020/06/02 08:17:27 nicm Exp $ */ +/* $OpenBSD: server-client.c,v 1.355 2020/06/05 07:33:57 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> @@ -1093,7 +1093,7 @@ server_client_update_latest(struct client *c) w->latest = c; if (options_get_number(w->options, "window-size") == WINDOW_SIZE_LATEST) - recalculate_size(w); + recalculate_size(w, 0); } /* @@ -1541,7 +1541,7 @@ server_client_check_pane_buffer(struct window_pane *wp) __func__, c->name, wpo->used - wp->base_offset, new_size, wp->id); if (new_size > SERVER_CLIENT_PANE_LIMIT) { - control_flush(c); + control_discard(c); c->flags |= CLIENT_EXIT; } if (wpo->used < minimum) @@ -1785,7 +1785,7 @@ server_client_check_exit(struct client *c) return; if (c->flags & CLIENT_CONTROL) { - control_flush(c); + control_discard(c); if (!control_all_done(c)) return; } @@ -2362,6 +2362,23 @@ server_client_get_cwd(struct client *c, struct session *s) return ("/"); } +/* Get control client flags. */ +static uint64_t +server_client_control_flags(struct client *c, const char *next) +{ + if (strcmp(next, "pause-after") == 0) { + c->pause_age = 0; + return (CLIENT_CONTROL_PAUSEAFTER); + } + if (sscanf(next, "pause-after=%u", &c->pause_age) == 1) { + c->pause_age *= 1000; + return (CLIENT_CONTROL_PAUSEAFTER); + } + if (strcmp(next, "no-output") == 0) + return (CLIENT_CONTROL_NOOUTPUT); + return (0); +} + /* Set client flags. */ void server_client_set_flags(struct client *c, const char *flags) @@ -2376,11 +2393,10 @@ server_client_set_flags(struct client *c, const char *flags) if (not) next++; - flag = 0; - if (c->flags & CLIENT_CONTROL) { - if (strcmp(next, "no-output") == 0) - flag = CLIENT_CONTROL_NOOUTPUT; - } + if (c->flags & CLIENT_CONTROL) + flag = server_client_control_flags(c, next); + else + flag = 0; if (strcmp(next, "read-only") == 0) flag = CLIENT_READONLY; else if (strcmp(next, "ignore-size") == 0) @@ -2405,7 +2421,8 @@ server_client_set_flags(struct client *c, const char *flags) const char * server_client_get_flags(struct client *c) { - static char s[256]; + static char s[256]; + char tmp[32]; *s = '\0'; if (c->flags & CLIENT_ATTACHED) @@ -2416,6 +2433,11 @@ server_client_get_flags(struct client *c) strlcat(s, "ignore-size,", sizeof s); if (c->flags & CLIENT_CONTROL_NOOUTPUT) strlcat(s, "no-output,", sizeof s); + if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { + xsnprintf(tmp, sizeof tmp, "pause-after=%u,", + c->pause_age / 1000); + strlcat(s, tmp, sizeof s); + } if (c->flags & CLIENT_READONLY) strlcat(s, "read-only,", sizeof s); if (c->flags & CLIENT_ACTIVEPANE) diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1 index cda3aa11bbb..226f4498f70 100644 --- a/usr.bin/tmux/tmux.1 +++ b/usr.bin/tmux/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.777 2020/06/04 10:34:40 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.778 2020/06/05 07:33:57 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: June 4 2020 $ +.Dd $Mdocdate: June 5 2020 $ .Dt TMUX 1 .Os .Sh NAME @@ -977,14 +977,18 @@ detaching the client, typically causing it to exit. sets a comma-separated list of client flags. The flags are: .Bl -tag -width Ds -.It read-only -the client is read-only +.It active-pane +the client has an independent active pane .It ignore-size the client does not affect the size of other clients .It no-output the client does not receive pane output in control mode -.It active-pane -the client has an independent active pane +.It pause-after=seconds +output is paused once the pane is +.Ar seconds +behind in control mode +.It read-only +the client is read-only .El .Pp A leading @@ -1295,22 +1299,27 @@ it. .Fl C sets the width and height of a control mode client. .Fl A -informs -.Nm -of a control mode client's interest in a pane. +allows a control mode client to trigger actions on a pane. The argument is a pane ID (with leading .Ql % ) , a colon, then one of -.Ql on +.Ql on , +.Ql off or -.Ql off . +.Ql continue . If .Ql off , .Nm will not send output from the pane to the client and if all clients have turned the pane off, will stop reading from the pane. +If +.Ql continue , +.Nm +will return to sending output to a paused pane (see the +.Ar pause-after +flag). .Fl A -may be given multiple times. +may be given multiple times for different panes. .Pp .Fl f sets a comma-separated list of client flags, see @@ -3345,6 +3354,10 @@ Allows setting the system clipboard. Allows setting the cursor colour. .It cstyle Allows setting the cursor style. +.It extkeys +Supports extended keys. +.It focus +Supports focus reporting. .It margins Supports DECSLRM margins. .It overline @@ -3353,6 +3366,8 @@ Supports the overline SGR attribute. Supports the DECFRA rectangle fill escape sequence. .It RGB Supports RGB colour with the SGR escape sequences. +.It strikethrough +Supports the strikethrough SGR escape sequence. .It sync Supports synchronized updates. .It title @@ -5881,6 +5896,12 @@ The client is now attached to the session with ID .Ar session-id , which is named .Ar name . +.It Ic %continue Ar pane-id +The pane has been continued after being paused (if the +.Ar pause-after +flag is set, see +.Ic refresh-client +.Fl A ) . .It Ic %exit Op Ar reason The .Nm @@ -5907,6 +5928,10 @@ escapes non-printable characters and backslash as octal \\xxx. The pane with ID .Ar pane-id has changed mode. +.It Ic %pause Ar pane-id +The pane has been paused (if the +.Ar pause-after +flag is set). .It Ic %session-changed Ar session-id Ar name The client is now attached to the session with ID .Ar session-id , diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h index 223df889053..dbed18d3564 100644 --- a/usr.bin/tmux/tmux.h +++ b/usr.bin/tmux/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1062 2020/06/02 20:51:46 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1063 2020/06/05 07:33:57 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> @@ -1575,7 +1575,9 @@ struct client { struct cmdq_list *queue; struct client_windows windows; + struct control_state *control_state; + u_int pause_age; pid_t pid; int fd; @@ -1643,6 +1645,7 @@ struct client { #define CLIENT_REDRAWPANES 0x20000000 #define CLIENT_NOFORK 0x40000000 #define CLIENT_ACTIVEPANE 0x80000000ULL +#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -2449,8 +2452,9 @@ void status_prompt_save_history(void); void resize_window(struct window *, u_int, u_int, int, int); void default_window_size(struct client *, struct session *, struct window *, u_int *, u_int *, u_int *, u_int *, int); -void recalculate_size(struct window *); +void recalculate_size(struct window *, int); void recalculate_sizes(void); +void recalculate_sizes_now(int); /* input.c */ struct input_ctx *input_init(struct window_pane *, struct bufferevent *); @@ -2837,11 +2841,12 @@ char *default_window_name(struct window *); char *parse_window_name(const char *); /* control.c */ -void control_flush(struct client *); +void control_discard(struct client *); void control_start(struct client *); void control_stop(struct client *); void control_set_pane_on(struct client *, struct window_pane *); void control_set_pane_off(struct client *, struct window_pane *); +void control_continue_pane(struct client *, struct window_pane *); struct window_pane_offset *control_pane_offset(struct client *, struct window_pane *, int *); void control_reset_offsets(struct client *); |