From 5f2bfd980737d2f13b744e288532e5d2daf97a0f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 Apr 2016 13:21:33 +0000 Subject: [PATCH 1/3] Make the grid_cell passed into screen_write_* const. --- screen-write.c | 100 ++++++++++++++++++++++++------------------------- tmux.h | 10 ++--- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/screen-write.c b/screen-write.c index e58d744c..63801dee 100644 --- a/screen-write.c +++ b/screen-write.c @@ -64,11 +64,15 @@ screen_write_reset(struct screen_write_ctx *ctx) /* Write character. */ void -screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc, +screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp, u_char ch) { - utf8_set(&gc->data, ch); - screen_write_cell(ctx, gc); + struct grid_cell gc; + + memcpy(&gc, gcp, sizeof gc); + + utf8_set(&gc.data, ch); + screen_write_cell(ctx, &gc); } /* Calculate string length, with embedded formatting. */ @@ -148,75 +152,74 @@ screen_write_strlen(const char *fmt, ...) /* Write simple string (no UTF-8 or maximum length). */ void -screen_write_puts(struct screen_write_ctx *ctx, struct grid_cell *gc, +screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - screen_write_vnputs(ctx, -1, gc, fmt, ap); + screen_write_vnputs(ctx, -1, gcp, fmt, ap); va_end(ap); } /* Write string with length limit (-1 for unlimited). */ void screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, - struct grid_cell *gc, const char *fmt, ...) + const struct grid_cell *gcp, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - screen_write_vnputs(ctx, maxlen, gc, fmt, ap); + screen_write_vnputs(ctx, maxlen, gcp, fmt, ap); va_end(ap); } void screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, - struct grid_cell *gc, const char *fmt, va_list ap) + const struct grid_cell *gcp, const char *fmt, va_list ap) { + struct grid_cell gc; + struct utf8_data *ud = &gc.data; char *msg; - struct utf8_data ud; u_char *ptr; size_t left, size = 0; enum utf8_state more; + memcpy(&gc, gcp, sizeof gc); xvasprintf(&msg, fmt, ap); ptr = msg; while (*ptr != '\0') { - if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { + if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); - if (left < (size_t)ud.size - 1) + if (left < (size_t)ud->size - 1) break; - while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) + while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - if (more == UTF8_DONE) { - if (maxlen > 0 && - size + ud.width > (size_t) maxlen) { - while (size < (size_t) maxlen) { - screen_write_putc(ctx, gc, ' '); - size++; - } - break; + if (more != UTF8_DONE) + continue; + if (maxlen > 0 && size + ud->width > (size_t)maxlen) { + while (size < (size_t)maxlen) { + screen_write_putc(ctx, &gc, ' '); + size++; } - size += ud.width; - - utf8_copy(&gc->data, &ud); - screen_write_cell(ctx, gc); + break; } + size += ud->width; + screen_write_cell(ctx, &gc); } else { - if (maxlen > 0 && size + 1 > (size_t) maxlen) + if (maxlen > 0 && size + 1 > (size_t)maxlen) break; if (*ptr == '\001') - gc->attr ^= GRID_ATTR_CHARSET; + gc.attr ^= GRID_ATTR_CHARSET; else if (*ptr > 0x1f && *ptr < 0x7f) { size++; - screen_write_putc(ctx, gc, *ptr); + screen_write_putc(ctx, &gc, *ptr); } ptr++; } @@ -228,22 +231,22 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, /* Write string, similar to nputs, but with embedded formatting (#[]). */ void screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, - struct grid_cell *gc, const char *fmt, ...) + const struct grid_cell *gcp, const char *fmt, ...) { - struct grid_cell lgc; - struct utf8_data ud; + struct grid_cell gc; + struct utf8_data *ud = &gc.data; va_list ap; char *msg; u_char *ptr, *last; size_t left, size = 0; enum utf8_state more; + memcpy(&gc, gcp, sizeof gc); + va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); - memcpy(&lgc, gc, sizeof lgc); - ptr = msg; while (*ptr != '\0') { if (ptr[0] == '#' && ptr[1] == '[') { @@ -255,42 +258,39 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, } *last = '\0'; - style_parse(gc, &lgc, ptr); + style_parse(gcp, &gc, ptr); ptr = last + 1; continue; } - if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { + if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); - if (left < (size_t)ud.size - 1) + if (left < (size_t)ud->size - 1) break; - while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) + while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - if (more == UTF8_DONE) { - if (maxlen > 0 && - size + ud.width > (size_t) maxlen) { - while (size < (size_t) maxlen) { - screen_write_putc(ctx, gc, ' '); - size++; - } - break; + if (more != UTF8_DONE) + continue; + if (maxlen > 0 && size + ud->width > (size_t)maxlen) { + while (size < (size_t)maxlen) { + screen_write_putc(ctx, &gc, ' '); + size++; } - size += ud.width; - - utf8_copy(&lgc.data, &ud); - screen_write_cell(ctx, &lgc); + break; } + size += ud->width; + screen_write_cell(ctx, &gc); } else { - if (maxlen > 0 && size + 1 > (size_t) maxlen) + if (maxlen > 0 && size + 1 > (size_t)maxlen) break; if (*ptr > 0x1f && *ptr < 0x7f) { size++; - screen_write_putc(ctx, &lgc, *ptr); + screen_write_putc(ctx, &gc, *ptr); } ptr++; } diff --git a/tmux.h b/tmux.h index 0b868537..0ceb14e9 100644 --- a/tmux.h +++ b/tmux.h @@ -2034,15 +2034,15 @@ void screen_write_stop(struct screen_write_ctx *); void screen_write_reset(struct screen_write_ctx *); size_t printflike(1, 2) screen_write_cstrlen(const char *, ...); void printflike(4, 5) screen_write_cnputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, const char *, ...); + ssize_t, const struct grid_cell *, const char *, ...); size_t printflike(1, 2) screen_write_strlen(const char *, ...); void printflike(3, 4) screen_write_puts(struct screen_write_ctx *, - struct grid_cell *, const char *, ...); + const struct grid_cell *, const char *, ...); void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, const char *, ...); + ssize_t, const struct grid_cell *, const char *, ...); void screen_write_vnputs(struct screen_write_ctx *, ssize_t, - struct grid_cell *, const char *, va_list); -void screen_write_putc(struct screen_write_ctx *, struct grid_cell *, + const struct grid_cell *, const char *, va_list); +void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *, u_char); void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int, u_int, u_int, u_int); From c5443da2d3bd5ef2464cc59941e55890aaff3fb0 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 Apr 2016 13:36:10 +0000 Subject: [PATCH 2/3] The backoff timer is causing no end of trouble with disconnected clients stopping data in attached ones. So get rid of it and see how we get on with just a high watermark on each pane. --- tmux.h | 12 ++---------- window.c | 39 +++------------------------------------ 2 files changed, 5 insertions(+), 46 deletions(-) diff --git a/tmux.h b/tmux.h index 0ceb14e9..d6cf114e 100644 --- a/tmux.h +++ b/tmux.h @@ -60,15 +60,8 @@ struct tmuxproc; /* Automatic name refresh interval, in microseconds. Must be < 1 second. */ #define NAME_INTERVAL 500000 -/* - * 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 +/* The maximum amount of data to hold from a pty (the event high watermark). */ +#define READ_SIZE 128 /* Attribute to make gcc check printf-like arguments. */ #define printflike(a, b) __attribute__ ((format (printf, a, b))) @@ -889,7 +882,6 @@ struct window_pane { int fd; struct bufferevent *event; - struct event timer; struct input_ctx *ictx; diff --git a/window.c b/window.c index 1769552f..ff90906f 100644 --- a/window.c +++ b/window.c @@ -777,9 +777,6 @@ 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); @@ -916,35 +913,16 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, 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; struct evbuffer *evb = wp->event->input; char *new_data; - size_t new_size, available; - struct client *c; - struct timeval tv; + size_t new_size; - 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; - } + log_debug("%%%u has %zu bytes (of %zu)", wp->id, EVBUFFER_LENGTH(evb), + (size_t)READ_SIZE); new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { @@ -955,17 +933,6 @@ window_pane_read_callback(__unused struct bufferevent *bufev, void *data) input_parse(wp); wp->pipe_off = EVBUFFER_LENGTH(evb); - 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 From 0d84fdd95303a6ed4dcd761425e35f3731b86725 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 Apr 2016 14:05:24 +0000 Subject: [PATCH 3/3] Final parts of command hooks, add before- and after- hooks to each command. --- cmd-if-shell.c | 1 + cmd-queue.c | 56 +++++++++++++++++++++++++++++++++++++++++------ cmd-source-file.c | 1 + format.c | 5 +++++ tmux.1 | 33 +++++++++++++++++++++++++--- 5 files changed, 86 insertions(+), 10 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 3e2a5251..2f47a0d7 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -157,6 +157,7 @@ cmd_if_shell_callback(struct job *job) } cmdq1 = cmdq_new(cmdq->client); + cmdq1->flags |= cmdq->flags & CMD_Q_NOHOOKS; cmdq1->emptyfn = cmd_if_shell_done; cmdq1->data = cdata; diff --git a/cmd-queue.c b/cmd-queue.c index 1c80c59d..c35d0c62 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -186,6 +186,9 @@ static enum cmd_retval cmdq_continue_one(struct cmd_q *cmdq) { struct cmd *cmd = cmdq->cmd; + const char *name = cmd->entry->name; + struct session *s; + struct hooks *hooks; enum cmd_retval retval; char *tmp; int flags = !!(cmd->flags & CMD_CONTROL); @@ -197,19 +200,51 @@ cmdq_continue_one(struct cmd_q *cmdq) cmdq->time = time(NULL); cmdq->number++; - cmdq_guard(cmdq, "begin", flags); + if (~cmdq->flags & CMD_Q_REENTRY) + cmdq_guard(cmdq, "begin", flags); - if (cmd_prepare_state(cmd, cmdq, NULL) != 0) + if (cmd_prepare_state(cmd, cmdq, cmdq->parent) != 0) goto error; + + if (~cmdq->flags & CMD_Q_NOHOOKS) { + s = NULL; + if (cmdq->state.tflag.s != NULL) + s = cmdq->state.tflag.s; + else if (cmdq->state.sflag.s != NULL) + s = cmdq->state.sflag.s; + else if (cmdq->state.c != NULL) + s = cmdq->state.c->session; + if (s != NULL) + hooks = s->hooks; + else + hooks = global_hooks; + + if (~cmdq->flags & CMD_Q_REENTRY) { + cmdq->flags |= CMD_Q_REENTRY; + if (hooks_wait(hooks, cmdq, NULL, + "before-%s", name) == 0) + return (CMD_RETURN_WAIT); + if (cmd_prepare_state(cmd, cmdq, cmdq->parent) != 0) + goto error; + } + } else + hooks = NULL; + cmdq->flags &= ~CMD_Q_REENTRY; + retval = cmd->entry->exec(cmd, cmdq); if (retval == CMD_RETURN_ERROR) goto error; + if (hooks != NULL && hooks_wait(hooks, cmdq, NULL, + "after-%s", name) == 0) + retval = CMD_RETURN_WAIT; cmdq_guard(cmdq, "end", flags); + return (retval); error: cmdq_guard(cmdq, "error", flags); + cmdq->flags &= ~CMD_Q_REENTRY; return (CMD_RETURN_ERROR); } @@ -232,11 +267,18 @@ cmdq_continue(struct cmd_q *cmdq) if (empty) goto empty; - if (cmdq->item == NULL) { - cmdq->item = TAILQ_FIRST(&cmdq->queue); - cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); - } else - cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); + /* + * If the command isn't in the middle of running hooks (due to + * CMD_RETURN_WAIT), move onto the next command; otherwise, leave the + * state of the queue as it is. + */ + if (~cmdq->flags & CMD_Q_REENTRY) { + if (cmdq->item == NULL) { + cmdq->item = TAILQ_FIRST(&cmdq->queue); + cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); + } else + cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); + } do { while (cmdq->cmd != NULL) { diff --git a/cmd-source-file.c b/cmd-source-file.c index 9d2d6d68..d5f5209a 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -49,6 +49,7 @@ cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) char *cause; cmdq1 = cmdq_new(cmdq->client); + cmdq1->flags |= cmdq->flags & CMD_Q_NOHOOKS; cmdq1->emptyfn = cmd_source_file_done; cmdq1->data = cmdq; diff --git a/format.c b/format.c index 78c177cd..d2b75b9b 100644 --- a/format.c +++ b/format.c @@ -468,6 +468,7 @@ struct format_tree * format_create(struct cmd_q *cmdq, int flags) { struct format_tree *ft; + struct cmd *cmd; if (!event_initialized(&format_job_event)) { evtimer_set(&format_job_event, format_job_timer, NULL); @@ -486,6 +487,10 @@ format_create(struct cmd_q *cmdq, int flags) if (cmdq != NULL && cmdq->cmd != NULL) format_add(ft, "command_name", "%s", cmdq->cmd->entry->name); + if (cmdq != NULL && cmdq->parent != NULL) { + cmd = cmdq->parent->cmd; + format_add(ft, "command_hooked", "%s", cmd->entry->name); + } return (ft); } diff --git a/tmux.1 b/tmux.1 index d2bfe32b..0aeefc32 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3221,9 +3221,35 @@ shows only the option value, not the name. .Nm allows commands to run on various triggers, called .Em hooks . -Each hook has a -.Em name . -The following hooks are available: +Each +.Nm +command has a +.Em before +hook and an +.Em after +hook and there are a number of hooks not associated with commands. +.Pp +A command's before hook is run before the command is executed and its after +hook is run afterwards, except when the command is run as part of a hook +itself. +Before hooks are named using the +.Ql before- +prefix and after hooks the +.Ql after- +prefix. +For example, the following command adds a hook to select the even-vertical +layout after every +.Ic split-window : +.Bd -literal -offset indent +set-hook after-split-window "selectl even-vertical" +.Ed +.Pp +Or to write when each new window is created to a file: +.Bd -literal -offset indent +set-hook before-new-window 'run "date >>/tmp/log"' +.Ed +.Pp +In addition, the following hooks are available: .Bl -tag -width "XXXXXXXXXXXXXXXX" .It alert-activity Run when a window has activity. @@ -3450,6 +3476,7 @@ The following variables are available, where appropriate: .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports utf8" .It Li "client_width" Ta "" Ta "Width of client" +.It Li "command_hooked" Ta "" Ta "Name of command hooked, if any" .It Li "command_name" Ta "" Ta "Name of command in use, if any" .It Li "cursor_flag" Ta "" Ta "Pane cursor flag" .It Li "cursor_x" Ta "" Ta "Cursor X position in pane"