diff --git a/cmd-show-messages.c b/cmd-show-messages.c index ccc11c92..681ff261 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -46,11 +46,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; @@ -62,10 +62,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; @@ -75,8 +77,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++) { @@ -105,21 +110,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 @@ -129,23 +139,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) diff --git a/format.c b/format.c index d045a253..e5f9d28c 100644 --- a/format.c +++ b/format.c @@ -495,6 +495,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; @@ -519,6 +521,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/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/options-table.c b/options-table.c index 756931db..f4a4879a 100644 --- a/options-table.c +++ b/options-table.c @@ -48,7 +48,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. */ @@ -295,7 +295,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", @@ -484,20 +484,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 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 6114b7d2..a987693e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2379,16 +2379,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 @@ -2890,24 +2892,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 @@ -3325,6 +3309,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" diff --git a/tmux.h b/tmux.h index 725b915f..054a859a 100644 --- a/tmux.h +++ b/tmux.h @@ -60,6 +60,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); @@ -79,6 +89,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 @@ -848,12 +859,9 @@ 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; + struct event timer; struct input_ctx *ictx; @@ -1602,8 +1610,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 *); @@ -2111,7 +2120,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/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 e3925f4f..d07a813d 100644 --- a/window.c +++ b/window.c @@ -740,8 +740,8 @@ window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode(wp); - if (event_initialized(&wp->changes_timer)) - evtimer_del(&wp->changes_timer); + if (event_initialized(&wp->timer)) + evtimer_del(&wp->timer); if (wp->fd != -1) { #ifdef HAVE_UTEMPTER @@ -885,64 +885,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_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; + 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 @@ -951,11 +940,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;