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.
pull/2270/head
nicm 2020-06-05 07:33:57 +00:00
parent d9cd493d09
commit c586208991
6 changed files with 138 additions and 39 deletions

View File

@ -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);
}

View File

@ -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. */

View File

@ -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;
@ -375,6 +375,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;
@ -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);
}

View File

@ -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)

45
tmux.1
View File

@ -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 ,

9
tmux.h
View File

@ -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 *);