aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--cmd-command-prompt.c2
-rw-r--r--cmd-detach-client.c4
-rw-r--r--configure.ac3
-rw-r--r--format.c4
-rw-r--r--grid.c2
-rw-r--r--server-client.c77
-rw-r--r--tmux.11
-rw-r--r--tmux.c2
-rw-r--r--tmux.h12
-rw-r--r--tty.c68
11 files changed, 156 insertions, 21 deletions
diff --git a/TODO b/TODO
index 3d024dcd..fba405f1 100644
--- a/TODO
+++ b/TODO
@@ -100,7 +100,6 @@
client w/o a session else cmd_current_client
- miscellaneous
- * way to keep a job running just read its last line of output for #()
* link panes into multiple windows
* live update: server started with -U connects to server, requests
sessions and windows, receives file descriptors
@@ -113,6 +112,7 @@
* marks in history, automatically add (move?) one when pane is changed
* this doesn't work, need pane reference count probably:
bind -n DoubleClick3Status confirm-before -p "kill-window #I? (y/n)" kill-window
+ * respawn -c flag same as neww etc
- hooks
* more hooks for various things
diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c
index 3cc5b2fb..41bbe01d 100644
--- a/cmd-command-prompt.c
+++ b/cmd-command-prompt.c
@@ -192,7 +192,7 @@ cmd_command_prompt_callback(void *data, const char *s, int done)
if (!done)
free(new_template);
- if (c->prompt_callbackfn != (void *)&cmd_command_prompt_callback)
+ if (c->prompt_callbackfn != cmd_command_prompt_callback)
return (1);
return (0);
}
diff --git a/cmd-detach-client.c b/cmd-detach-client.c
index a10fd42a..2e21f795 100644
--- a/cmd-detach-client.c
+++ b/cmd-detach-client.c
@@ -67,9 +67,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
const char *cmd = args_get(args, 'E');
if (self->entry == &cmd_suspend_client_entry) {
- tty_stop_tty(&c->tty);
- c->flags |= CLIENT_SUSPENDED;
- proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0);
+ server_client_suspend(c);
return (CMD_RETURN_NORMAL);
}
diff --git a/configure.ac b/configure.ac
index 80ea3872..b078c4f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
# configure.ac
-AC_INIT(tmux, 2.4)
+AC_INIT(tmux, master)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -35,6 +35,7 @@ AC_USE_SYSTEM_EXTENSIONS
test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc
# Is this --enable-debug?
+enable_debug=yes
AC_ARG_ENABLE(
debug,
AC_HELP_STRING(--enable-debug, enable debug build flags),
diff --git a/format.c b/format.c
index c1a91a85..839cf48a 100644
--- a/format.c
+++ b/format.c
@@ -1174,7 +1174,9 @@ format_defaults_client(struct format_tree *ft, struct client *c)
format_add_tv(ft, "client_created", &c->creation_time);
format_add_tv(ft, "client_activity", &c->activity_time);
- format_add(ft, "client_written", "%zu", tty->written);
+
+ format_add(ft, "client_written", "%zu", c->written);
+ format_add(ft, "client_discarded", "%zu", c->discarded);
name = server_client_get_key_table(c);
if (strcmp(c->keytable->name, name) == 0)
diff --git a/grid.c b/grid.c
index 43145eae..af555e6d 100644
--- a/grid.c
+++ b/grid.c
@@ -89,7 +89,7 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1);
if (gc->data.size != 1 || gc->data.width != 1)
return (1);
- if ((gc->fg & COLOUR_FLAG_RGB) ||(gc->bg & COLOUR_FLAG_RGB))
+ if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
return (1);
return (0);
}
diff --git a/server-client.c b/server-client.c
index 3c63a9d5..e42ad8e0 100644
--- a/server-client.c
+++ b/server-client.c
@@ -47,7 +47,7 @@ static void server_client_dispatch_command(struct client *, struct imsg *);
static void server_client_dispatch_identify(struct client *, struct imsg *);
static void server_client_dispatch_shell(struct client *);
-/* Idenfity mode callback. */
+/* Identify mode callback. */
static void
server_client_callback_identify(__unused int fd, __unused short events, void *data)
{
@@ -323,15 +323,30 @@ server_client_free(__unused int fd, __unused short events, void *arg)
}
}
+/* Suspend a client. */
+void
+server_client_suspend(struct client *c)
+{
+ struct session *s = c->session;
+
+ if (s == NULL || (c->flags & CLIENT_DETACHING))
+ return;
+
+ tty_stop_tty(&c->tty);
+ c->flags |= CLIENT_SUSPENDED;
+ proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0);
+}
+
/* Detach a client. */
void
server_client_detach(struct client *c, enum msgtype msgtype)
{
- struct session *s = c->session;
+ struct session *s = c->session;
- if (s == NULL)
+ if (s == NULL || (c->flags & CLIENT_DETACHING))
return;
+ c->flags |= CLIENT_DETACHING;
notify_client("client-detached", c);
proc_send_s(c->peer, msgtype, s->name);
}
@@ -1209,6 +1224,14 @@ server_client_check_exit(struct client *c)
c->flags &= ~CLIENT_EXIT;
}
+/* Redraw timer callback. */
+static void
+server_client_redraw_timer(__unused int fd, __unused short events,
+ __unused void* data)
+{
+ log_debug("redraw timer fired");
+}
+
/* Check for client redraws. */
static void
server_client_check_redraw(struct client *c)
@@ -1216,19 +1239,61 @@ server_client_check_redraw(struct client *c)
struct session *s = c->session;
struct tty *tty = &c->tty;
struct window_pane *wp;
- int flags, masked;
+ int needed, flags, masked;
+ struct timeval tv = { .tv_usec = 1000 };
+ static struct event ev;
+ size_t left;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
+ /*
+ * If there is outstanding data, defer the redraw until it has been
+ * consumed. We can just add a timer to get out of the event loop and
+ * end up back here.
+ */
+ needed = 0;
+ if (c->flags & CLIENT_REDRAW)
+ needed = 1;
+ else {
+ TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
+ if (wp->flags & PANE_REDRAW) {
+ needed = 1;
+ break;
+ }
+ }
+ }
+ if (needed) {
+ left = EVBUFFER_LENGTH(tty->out);
+ if (left != 0) {
+ log_debug("%s: redraw deferred (%zu left)", c->name, left);
+ if (evtimer_initialized(&ev) && evtimer_pending(&ev, NULL))
+ return;
+ log_debug("redraw timer started");
+ evtimer_set(&ev, server_client_redraw_timer, NULL);
+ evtimer_add(&ev, &tv);
+
+ /*
+ * We may have got here for a single pane redraw, but
+ * force a full redraw next time in case other panes
+ * have been updated.
+ */
+ c->flags |= CLIENT_REDRAW;
+ return;
+ }
+ if (evtimer_initialized(&ev))
+ evtimer_del(&ev);
+ log_debug("%s: redraw needed", c->name);
+ }
+
if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
if (options_get_number(s->options, "set-titles"))
server_client_set_title(c);
screen_redraw_update(c); /* will adjust flags */
}
- flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR);
- tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR;
+ flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR);
+ tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE)) | TTY_NOCURSOR;
if (c->flags & CLIENT_REDRAW) {
tty_update_mode(tty, tty->mode, NULL);
diff --git a/tmux.1 b/tmux.1
index 873a63b2..63ae6436 100644
--- a/tmux.1
+++ b/tmux.1
@@ -3512,6 +3512,7 @@ The following variables are available, where appropriate:
.It Li "client_activity" Ta "" Ta "Integer time client last had activity"
.It Li "client_created" Ta "" Ta "Integer time client created"
.It Li "client_control_mode" Ta "" Ta "1 if client is in control mode"
+.It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind"
.It Li "client_height" Ta "" Ta "Height of client"
.It Li "client_key_table" Ta "" Ta "Current key table"
.It Li "client_last_session" Ta "" Ta "Name of the client's last session"
diff --git a/tmux.c b/tmux.c
index c106d88d..78c8c70b 100644
--- a/tmux.c
+++ b/tmux.c
@@ -118,7 +118,7 @@ make_label(const char *label)
uid = getuid();
if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0')
- xasprintf(&base, "%s/tmux-%u", s, uid);
+ xasprintf(&base, "%s/tmux-%ld", s, (long)uid);
else
xasprintf(&base, "%s/tmux-%ld", _PATH_TMP, (long)uid);
diff --git a/tmux.h b/tmux.h
index 79601e1f..b84ba3cf 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1047,7 +1047,8 @@ struct tty {
struct evbuffer *in;
struct event event_out;
struct evbuffer *out;
- size_t written;
+ struct event timer;
+ size_t discarded;
struct termios tio;
@@ -1063,6 +1064,7 @@ struct tty {
#define TTY_STARTED 0x10
#define TTY_OPENED 0x20
#define TTY_FOCUS 0x40
+#define TTY_BLOCK 0x80
int flags;
struct tty_term *term;
@@ -1089,7 +1091,7 @@ struct tty {
struct tty_key *key_tree;
};
#define TTY_TYPES \
- { "VT100", "VT101", "VT102", "VT220", "VT320", "VT420", "UNKNOWN" }
+ { "VT100", "VT101", "VT102", "VT220", "VT320", "VT420", "Unknown" }
/* TTY command context. */
struct tty_ctx {
@@ -1310,6 +1312,9 @@ struct client {
char *ttyname;
struct tty tty;
+ size_t written;
+ size_t discarded;
+
void (*stdin_callback)(struct client *, int, void *);
void *stdin_callback_data;
struct evbuffer *stdin_data;
@@ -1337,7 +1342,7 @@ struct client {
#define CLIENT_DEAD 0x200
#define CLIENT_BORDERS 0x400
#define CLIENT_READONLY 0x800
-/* 0x1000 unused */
+#define CLIENT_DETACHING 0x1000
#define CLIENT_CONTROL 0x2000
#define CLIENT_CONTROLCONTROL 0x4000
#define CLIENT_FOCUSED 0x8000
@@ -1837,6 +1842,7 @@ void server_client_create(int);
int server_client_open(struct client *, char **);
void server_client_unref(struct client *);
void server_client_lost(struct client *);
+void server_client_suspend(struct client *);
void server_client_detach(struct client *, enum msgtype);
void server_client_exec(struct client *, const char *);
void server_client_loop(void);
diff --git a/tty.c b/tty.c
index 14906cc3..362cd36c 100644
--- a/tty.c
+++ b/tty.c
@@ -76,9 +76,13 @@ static void tty_default_attributes(struct tty *, const struct window_pane *,
#define tty_use_margin(tty) \
((tty)->term_type == TTY_VT420)
-#define tty_pane_full_width(tty, ctx) \
+#define tty_pane_full_width(tty, ctx) \
((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx)
+#define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */)
+#define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8)
+#define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8)
+
void
tty_create_log(void)
{
@@ -172,6 +176,51 @@ tty_read_callback(__unused int fd, __unused short events, void *data)
}
static void
+tty_timer_callback(__unused int fd, __unused short events, void *data)
+{
+ struct tty *tty = data;
+ struct client *c = tty->client;
+ struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL };
+
+ log_debug("%s: %zu discarded", c->name, tty->discarded);
+
+ c->flags |= CLIENT_REDRAW;
+ c->discarded += tty->discarded;
+
+ if (tty->discarded < TTY_BLOCK_STOP(tty)) {
+ tty->flags &= ~TTY_BLOCK;
+ tty_invalidate(tty);
+ return;
+ }
+ tty->discarded = 0;
+ evtimer_add(&tty->timer, &tv);
+}
+
+static int
+tty_block_maybe(struct tty *tty)
+{
+ struct client *c = tty->client;
+ size_t size = EVBUFFER_LENGTH(tty->out);
+ struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL };
+
+ if (size < TTY_BLOCK_START(tty))
+ return (0);
+
+ if (tty->flags & TTY_BLOCK)
+ return (1);
+ tty->flags |= TTY_BLOCK;
+
+ log_debug("%s: can't keep up, %zu discarded", c->name, size);
+
+ evbuffer_drain(tty->out, size);
+ c->discarded += size;
+
+ tty->discarded = 0;
+ evtimer_add(&tty->timer, &tv);
+ return (1);
+}
+
+static void
tty_write_callback(__unused int fd, __unused short events, void *data)
{
struct tty *tty = data;
@@ -184,6 +233,9 @@ tty_write_callback(__unused int fd, __unused short events, void *data)
return;
log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size);
+ if (tty_block_maybe(tty))
+ return;
+
if (EVBUFFER_LENGTH(tty->out) != 0)
event_add(&tty->event_out, NULL);
}
@@ -198,7 +250,7 @@ tty_open(struct tty *tty, char **cause)
}
tty->flags |= TTY_OPENED;
- tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
+ tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER);
event_set(&tty->event_in, tty->fd, EV_PERSIST|EV_READ,
tty_read_callback, tty);
@@ -207,6 +259,8 @@ tty_open(struct tty *tty, char **cause)
event_set(&tty->event_out, tty->fd, EV_WRITE, tty_write_callback, tty);
tty->out = evbuffer_new();
+ evtimer_set(&tty->timer, tty_timer_callback, tty);
+
tty_start_tty(tty);
tty_keys_build(tty);
@@ -273,6 +327,9 @@ tty_stop_tty(struct tty *tty)
return;
tty->flags &= ~TTY_STARTED;
+ event_del(&tty->timer);
+ tty->flags &= ~TTY_BLOCK;
+
event_del(&tty->event_in);
event_del(&tty->event_out);
@@ -425,9 +482,14 @@ tty_add(struct tty *tty, const char *buf, size_t len)
{
struct client *c = tty->client;
+ if (tty->flags & TTY_BLOCK) {
+ tty->discarded += len;
+ return;
+ }
+
evbuffer_add(tty->out, buf, len);
log_debug("%s: %.*s", c->name, (int)len, (const char *)buf);
- tty->written += len;
+ c->written += len;
if (tty_log_fd != -1)
write(tty_log_fd, buf, len);