diff options
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | cmd-command-prompt.c | 2 | ||||
-rw-r--r-- | cmd-detach-client.c | 4 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | format.c | 4 | ||||
-rw-r--r-- | grid.c | 2 | ||||
-rw-r--r-- | server-client.c | 77 | ||||
-rw-r--r-- | tmux.1 | 1 | ||||
-rw-r--r-- | tmux.c | 2 | ||||
-rw-r--r-- | tmux.h | 12 | ||||
-rw-r--r-- | tty.c | 68 |
11 files changed, 156 insertions, 21 deletions
@@ -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), @@ -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) @@ -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); @@ -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" @@ -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); @@ -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); @@ -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); |