From 44364d7112037bebb0bb1d20f72b12b1f736fa95 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 May 2015 10:10:16 +0000 Subject: [PATCH 1/6] Remove the c0-* options which never really worked satisfactorily. Going to try something else... --- input.c | 16 +++------------- tmux.1 | 18 ------------------ tmux.h | 5 ----- window.c | 40 ---------------------------------------- 4 files changed, 3 insertions(+), 76 deletions(-) diff --git a/input.c b/input.c index 384ab45d..886d561d 100644 --- a/input.c +++ b/input.c @@ -1069,7 +1069,6 @@ input_c0_dispatch(struct input_ctx *ictx) struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; - u_int trigger; log_debug("%s: '%c", __func__, ictx->ch); @@ -1081,7 +1080,7 @@ input_c0_dispatch(struct input_ctx *ictx) break; case '\010': /* BS */ screen_write_backspace(sctx); - goto count_c0; + break; case '\011': /* HT */ /* Don't tab beyond the end of the line. */ if (s->cx >= screen_size_x(s) - 1) @@ -1098,10 +1097,10 @@ input_c0_dispatch(struct input_ctx *ictx) case '\013': /* VT */ case '\014': /* FF */ screen_write_linefeed(sctx, 0); - goto count_c0; + break; case '\015': /* CR */ screen_write_carriagereturn(sctx); - goto count_c0; + break; case '\016': /* SO */ ictx->cell.set = 1; break; @@ -1113,15 +1112,6 @@ input_c0_dispatch(struct input_ctx *ictx) break; } - return (0); - -count_c0: - trigger = options_get_number(&wp->window->options, "c0-change-trigger"); - if (trigger != 0 && ++wp->changes >= trigger) { - wp->flags |= PANE_DROP; - window_pane_timer_start(wp); - } - return (0); } diff --git a/tmux.1 b/tmux.1 index 9debeaf4..79dfa4b4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2886,24 +2886,6 @@ used when the .Ic automatic-rename option is enabled. .Pp -.It Ic c0-change-interval Ar interval -.It Ic c0-change-trigger Ar trigger -These two options configure a simple form of rate limiting for a pane. -If -.Nm -sees more than -.Ar trigger -C0 sequences that modify the screen (for example, carriage returns, linefeeds -or backspaces) in one millisecond, it will stop updating the pane immediately and -instead redraw it entirely every -.Ar interval -milliseconds. -This helps to prevent fast output (such as -.Xr yes 1 ) -overwhelming the terminal. -The default is a trigger of 250 and an interval of 100. -A trigger of zero disables the rate limiting. -.Pp .It Ic clock-mode-colour Ar colour Set clock colour. .Pp diff --git a/tmux.h b/tmux.h index 6d4e29fd..ccbdb763 100644 --- a/tmux.h +++ b/tmux.h @@ -844,10 +844,6 @@ struct window_pane { char tty[TTY_NAME_MAX]; int status; - u_int changes; - struct event changes_timer; - u_int changes_redraw; - int fd; struct bufferevent *event; @@ -2107,7 +2103,6 @@ struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); -void window_pane_timer_start(struct window_pane *); int window_pane_spawn(struct window_pane *, int, char **, const char *, const char *, int, struct environ *, struct termios *, char **); diff --git a/window.c b/window.c index 8366833a..7edfed42 100644 --- a/window.c +++ b/window.c @@ -58,7 +58,6 @@ u_int next_window_pane_id; u_int next_window_id; u_int next_active_point; -void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); @@ -741,9 +740,6 @@ window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode(wp); - if (event_initialized(&wp->changes_timer)) - evtimer_del(&wp->changes_timer); - if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); @@ -877,42 +873,6 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, return (0); } -void -window_pane_timer_start(struct window_pane *wp) -{ - struct timeval tv; - - tv.tv_sec = 0; - tv.tv_usec = 1000; - - evtimer_del(&wp->changes_timer); - evtimer_set(&wp->changes_timer, window_pane_timer_callback, wp); - evtimer_add(&wp->changes_timer, &tv); -} - -void -window_pane_timer_callback(unused int fd, unused short events, void *data) -{ - struct window_pane *wp = data; - struct window *w = wp->window; - u_int interval, trigger; - - interval = options_get_number(&w->options, "c0-change-interval"); - trigger = options_get_number(&w->options, "c0-change-trigger"); - - if (wp->changes_redraw++ == interval) { - wp->flags |= PANE_REDRAW; - wp->changes_redraw = 0; - } - - if (trigger == 0 || wp->changes < trigger) { - wp->flags |= PANE_REDRAW; - wp->flags &= ~PANE_DROP; - } else - window_pane_timer_start(wp); - wp->changes = 0; -} - void window_pane_read_callback(unused struct bufferevent *bufev, void *data) { From b833fabeb2334c44406bd54ff7cc804d20c546f6 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 May 2015 10:58:22 +0000 Subject: [PATCH 2/6] Left the c0-* options behind in the table. --- options-table.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/options-table.c b/options-table.c index 21758543..9869e14f 100644 --- a/options-table.c +++ b/options-table.c @@ -485,20 +485,6 @@ const struct options_table_entry window_options_table[] = { "#{?pane_dead,[dead],}" }, - { .name = "c0-change-trigger", - .type = OPTIONS_TABLE_NUMBER, - .default_num = 250, - .minimum = 0, - .maximum = USHRT_MAX - }, - - { .name = "c0-change-interval", - .type = OPTIONS_TABLE_NUMBER, - .default_num = 100, - .minimum = 1, - .maximum = USHRT_MAX - }, - { .name = "clock-mode-colour", .type = OPTIONS_TABLE_COLOUR, .default_num = 4 From e958db09a72c2308d3edc8384be02714eb068ae7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 15:27:46 +0000 Subject: [PATCH 3/6] Add bell-action "other" to pass through bells in all windows except the current, suggested by Jan ONDREJ. --- options-table.c | 2 +- server-window.c | 11 ++++++++--- tmux.1 | 8 +++++--- tmux.h | 1 + 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/options-table.c b/options-table.c index 9869e14f..44d36dca 100644 --- a/options-table.c +++ b/options-table.c @@ -49,7 +49,7 @@ const char *options_table_status_position_list[] = { "top", "bottom", NULL }; const char *options_table_bell_action_list[] = { - "none", "any", "current", NULL + "none", "any", "current", "other", NULL }; /* Server options. */ diff --git a/server-window.c b/server-window.c index 4385dd90..1b3938d3 100644 --- a/server-window.c +++ b/server-window.c @@ -77,13 +77,18 @@ server_window_check_bell(struct session *s, struct winlink *wl) if (c->session != s || c->flags & CLIENT_CONTROL) continue; if (!visual) { - if (c->session->curw->window == w || action == BELL_ANY) + if ((action == BELL_CURRENT && + c->session->curw->window == w) || + (action == BELL_OTHER && + c->session->curw->window != w) || + action == BELL_ANY) tty_bell(&c->tty); continue; } - if (c->session->curw->window == w) + if (action == BELL_CURRENT && c->session->curw->window == w) status_message_set(c, "Bell in current window"); - else if (action == BELL_ANY) + else if (action == BELL_ANY || (action == BELL_OTHER && + c->session->curw->window != w)) status_message_set(c, "Bell in window %d", wl->idx); } diff --git a/tmux.1 b/tmux.1 index 79dfa4b4..7d9c0dc8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2375,16 +2375,18 @@ Set the base index from which an unused index should be searched when a new window is created. The default is zero. .It Xo Ic bell-action -.Op Ic any | none | current +.Op Ic any | none | current | other .Xc Set action on window bell. .Ic any means a bell in any window linked to a session causes a bell in the current window of that session, .Ic none -means all bells are ignored and +means all bells are ignored, .Ic current -means only bells in windows other than the current window are ignored. +means only bells in windows other than the current window are ignored and +.Ic other +means bells in the current window are ignored but not those in other windows. .It Xo Ic bell-on-alert .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index ccbdb763..f7c44b20 100644 --- a/tmux.h +++ b/tmux.h @@ -75,6 +75,7 @@ extern char **environ; #define BELL_NONE 0 #define BELL_ANY 1 #define BELL_CURRENT 2 +#define BELL_OTHER 3 /* Special key codes. */ #define KEYC_NONE 0xfff From ec34439f9cddcbde78d5fa2c628e454d2df5dd53 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 15:29:29 +0000 Subject: [PATCH 4/6] Add a session_alerts format which is a list of all the alerts in the current session in symbolic form (something like "0!,4~,5!"). Use this in the default set-titles-string. Prompted by a request from Jan ONDREJ. --- format.c | 20 ++++++++++++++++++++ options-table.c | 2 +- tmux.1 | 1 + 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 39351fc3..db6f9231 100644 --- a/format.c +++ b/format.c @@ -494,6 +494,8 @@ format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; time_t t; + struct winlink *wl; + char alerts[256], tmp[16]; ft->s = s; @@ -518,6 +520,24 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); + + *alerts = '\0'; + RB_FOREACH (wl, winlinks, &s->windows) { + if ((wl->flags & WINLINK_ALERTFLAGS) == 0) + continue; + snprintf(tmp, sizeof tmp, "%u", wl->idx); + + if (*alerts != '\0') + strlcat(alerts, ",", sizeof alerts); + strlcat(alerts, tmp, sizeof alerts); + if (wl->flags & WINLINK_ACTIVITY) + strlcat(alerts, "#", sizeof alerts); + if (wl->flags & WINLINK_BELL) + strlcat(alerts, "!", sizeof alerts); + if (wl->flags & WINLINK_SILENCE) + strlcat(alerts, "~", sizeof alerts); + } + format_add(ft, "session_alerts", "%s", alerts); } /* Set default format keys for a client. */ diff --git a/options-table.c b/options-table.c index 44d36dca..4ad45d37 100644 --- a/options-table.c +++ b/options-table.c @@ -296,7 +296,7 @@ const struct options_table_entry session_options_table[] = { { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, - .default_str = "#S:#I:#W - \"#T\"" + .default_str = "#S:#I:#W - \"#T\" #{session_alerts}" }, { .name = "status", diff --git a/tmux.1 b/tmux.1 index 7d9c0dc8..f5a0aaa3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3304,6 +3304,7 @@ The following variables are available, where appropriate: .It Li "pane_width" Ta "" Ta "Width of pane" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" +.It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" .It Li "session_activity" Ta "" Ta "Integer time of session last activity" .It Li "session_activity_string" Ta "" Ta "String time of session last activity" From 37ae8a9e0fc2cd7ab2387a9a762a39dc0ff6723c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 19:36:08 +0000 Subject: [PATCH 5/6] Tidy blank lines when outputting server info. --- cmd-show-messages.c | 48 +++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 028b4bb0..2a04bd93 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -47,11 +47,11 @@ const struct cmd_entry cmd_server_info_entry = { cmd_show_messages_exec }; -void cmd_show_messages_server(struct cmd_q *); -void cmd_show_messages_terminals(struct cmd_q *); -void cmd_show_messages_jobs(struct cmd_q *); +int cmd_show_messages_server(struct cmd_q *); +int cmd_show_messages_terminals(struct cmd_q *, int); +int cmd_show_messages_jobs(struct cmd_q *, int); -void +int cmd_show_messages_server(struct cmd_q *cmdq) { char *tim; @@ -63,10 +63,12 @@ cmd_show_messages_server(struct cmd_q *cmdq) cmdq_print(cmdq, "socket path %s", socket_path); cmdq_print(cmdq, "debug level %d", debug_level); cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); + + return (1); } -void -cmd_show_messages_terminals(struct cmd_q *cmdq) +int +cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) { struct tty_term *term; const struct tty_term_code_entry *ent; @@ -76,8 +78,11 @@ cmd_show_messages_terminals(struct cmd_q *cmdq) n = 0; LIST_FOREACH(term, &tty_terms, entry) { - cmdq_print(cmdq, - "Terminal %u: %s [references=%u, flags=0x%x]:", + if (blank) { + cmdq_print(cmdq, "%s", ""); + blank = 0; + } + cmdq_print(cmdq, "Terminal %u: %s [references=%u, flags=0x%x]:", n, term->name, term->references, term->flags); n++; for (i = 0; i < NTTYCODE; i++) { @@ -106,21 +111,26 @@ cmd_show_messages_terminals(struct cmd_q *cmdq) } } } + return (n != 0); } -void -cmd_show_messages_jobs(struct cmd_q *cmdq) +int +cmd_show_messages_jobs(struct cmd_q *cmdq, int blank) { struct job *job; u_int n; n = 0; LIST_FOREACH(job, &all_jobs, lentry) { - cmdq_print(cmdq, - "Job %u: %s [fd=%d, pid=%d, status=%d]", + if (blank) { + cmdq_print(cmdq, "%s", ""); + blank = 0; + } + cmdq_print(cmdq, "Job %u: %s [fd=%d, pid=%d, status=%d]", n, job->cmd, job->fd, job->pid, job->status); n++; } + return (n != 0); } enum cmd_retval @@ -130,23 +140,19 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c; struct message_entry *msg; char *tim; - int done; + int done, blank; - done = 0; + done = blank = 0; if (args_has(args, 'I') || self->entry == &cmd_server_info_entry) { - cmd_show_messages_server(cmdq); + blank = cmd_show_messages_server(cmdq); done = 1; } if (args_has(args, 'T') || self->entry == &cmd_server_info_entry) { - if (done) - cmdq_print(cmdq, "%s", ""); - cmd_show_messages_terminals(cmdq); + blank = cmd_show_messages_terminals(cmdq, blank); done = 1; } if (args_has(args, 'J') || self->entry == &cmd_server_info_entry) { - if (done) - cmdq_print(cmdq, "%s", ""); - cmd_show_messages_jobs(cmdq); + cmd_show_messages_jobs(cmdq, blank); done = 1; } if (done) From 3f4ee98162cd5bb7000f93fec0e631e123b1281d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 22:40:38 +0000 Subject: [PATCH 6/6] To replace c0-*, add a high watermark to the pty event, and also backoff when the any of the ttys the pane is going to write to has buffered enough data. --- tmux.h | 16 ++++++++++++++-- tty.c | 26 +++++++++++++++++--------- window.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 79 insertions(+), 19 deletions(-) diff --git a/tmux.h b/tmux.h index f7c44b20..bc47a268 100644 --- a/tmux.h +++ b/tmux.h @@ -56,6 +56,16 @@ extern char **environ; */ #define UTF8_SIZE 9 +/* + * READ_SIZE is the maximum size of data to hold from a pty (the event high + * watermark). READ_BACKOFF is the amount of data waiting to be output to a tty + * before pty reads will be backed off. READ_TIME is how long to back off + * before the next read (in microseconds) if a tty is above READ_BACKOFF. + */ +#define READ_SIZE 1024 +#define READ_BACKOFF 512 +#define READ_TIME 100 + /* Fatal errors. */ #define fatal(msg) log_fatal("%s: %s", __func__, msg); #define fatalx(msg) log_fatalx("%s: %s", __func__, msg); @@ -847,6 +857,7 @@ struct window_pane { int fd; struct bufferevent *event; + struct event timer; struct input_ctx *ictx; @@ -1595,8 +1606,9 @@ void tty_draw_line(struct tty *, const struct window_pane *, struct screen *, int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); -void tty_write( - void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); +void tty_write(void (*)(struct tty *, const struct tty_ctx *), + struct tty_ctx *); +int tty_client_ready(struct client *, struct window_pane *wp); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); void tty_cmd_cell(struct tty *, const struct tty_ctx *); void tty_cmd_clearendofline(struct tty *, const struct tty_ctx *); diff --git a/tty.c b/tty.c index a9f49c9b..a58ca937 100644 --- a/tty.c +++ b/tty.c @@ -723,9 +723,23 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, tty_update_mode(tty, tty->mode, s); } +int +tty_client_ready(struct client *c, struct window_pane *wp) +{ + if (c->session == NULL || c->tty.term == NULL) + return (0); + if (c->flags & CLIENT_SUSPENDED) + return (0); + if (c->tty.flags & TTY_FREEZE) + return (0); + if (c->session->curw->window != wp->window) + return (0); + return (1); +} + void -tty_write( - void (*cmdfn)(struct tty *, const struct tty_ctx *), struct tty_ctx *ctx) +tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), + struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct client *c; @@ -740,13 +754,7 @@ tty_write( return; TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL || c->tty.term == NULL) - continue; - if (c->flags & CLIENT_SUSPENDED) - continue; - if (c->tty.flags & TTY_FREEZE) - continue; - if (c->session->curw->window != wp->window) + if (!tty_client_ready(c, wp)) continue; ctx->xoff = wp->xoff; diff --git a/window.c b/window.c index 7edfed42..bc17656d 100644 --- a/window.c +++ b/window.c @@ -58,6 +58,7 @@ u_int next_window_pane_id; u_int next_window_id; u_int next_active_point; +void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); @@ -740,6 +741,9 @@ window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode(wp); + if (event_initialized(&wp->timer)) + evtimer_del(&wp->timer); + if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); @@ -867,28 +871,53 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, window_pane_error_callback, wp); + + bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); bufferevent_enable(wp->event, EV_READ|EV_WRITE); free(cmd); return (0); } +void +window_pane_timer_callback(unused int fd, unused short events, void *data) +{ + window_pane_read_callback(NULL, data); +} + void window_pane_read_callback(unused struct bufferevent *bufev, void *data) { - struct window_pane *wp = data; - char *new_data; - size_t new_size; + struct window_pane *wp = data; + struct evbuffer *evb = wp->event->input; + char *new_data; + size_t new_size, available; + struct client *c; + struct timeval tv; - new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off; + if (event_initialized(&wp->timer)) + evtimer_del(&wp->timer); + + log_debug("%%%u has %zu bytes", wp->id, EVBUFFER_LENGTH(evb)); + + TAILQ_FOREACH(c, &clients, entry) { + if (!tty_client_ready(c, wp)) + continue; + + available = EVBUFFER_LENGTH(c->tty.event->output); + if (available > READ_BACKOFF) + goto start_timer; + } + + new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { - new_data = EVBUFFER_DATA(wp->event->input); + new_data = EVBUFFER_DATA(evb); bufferevent_write(wp->pipe_event, new_data, new_size); } input_parse(wp); - wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); + wp->pipe_off = EVBUFFER_LENGTH(evb); /* * If we get here, we're not outputting anymore, so set the silence @@ -897,11 +926,22 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) wp->window->flags |= WINDOW_SILENCE; if (gettimeofday(&wp->window->silence_timer, NULL) != 0) fatal("gettimeofday failed."); + return; + +start_timer: + log_debug("%%%u backing off (%s %zu > %d)", wp->id, c->ttyname, + available, READ_BACKOFF); + + tv.tv_sec = 0; + tv.tv_usec = READ_TIME; + + evtimer_set(&wp->timer, window_pane_timer_callback, wp); + evtimer_add(&wp->timer, &tv); } void -window_pane_error_callback( - unused struct bufferevent *bufev, unused short what, void *data) +window_pane_error_callback(unused struct bufferevent *bufev, unused short what, + void *data) { struct window_pane *wp = data;