summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornicm <nicm>2020-06-05 07:33:57 +0000
committernicm <nicm>2020-06-05 07:33:57 +0000
commitc586208991e4291450757e3a19739f368aecbe5d (patch)
tree1fd2a4d7c912879f02e1b43367825670e0538800
parentd9cd493d093f14b934343a2e57998c86fbca2ef7 (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--cmd-refresh-client.c4
-rw-r--r--control.c63
-rw-r--r--resize.c16
-rw-r--r--server-client.c40
-rw-r--r--tmux.145
-rw-r--r--tmux.h9
6 files changed, 138 insertions, 39 deletions
diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c
index 4eb8417b..bbe0c736 100644
--- a/cmd-refresh-client.c
+++ b/cmd-refresh-client.c
@@ -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/control.c b/control.c
index 1801bd75..b9b22567 100644
--- a/control.c
+++ b/control.c
@@ -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/resize.c b/resize.c
index 68717e35..d6e6dce2 100644
--- a/resize.c
+++ b/resize.c
@@ -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/server-client.c b/server-client.c
index e3383aab..1a02a240 100644
--- a/server-client.c
+++ b/server-client.c
@@ -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/tmux.1 b/tmux.1
index 68d8b3cc..4489c9ae 100644
--- a/tmux.1
+++ b/tmux.1
@@ -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/tmux.h b/tmux.h
index 4523fce4..fa11708c 100644
--- a/tmux.h
+++ b/tmux.h
@@ -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 *);