From ddc4512d2e0eda6c705e002cb5dbf80719d709e1 Mon Sep 17 00:00:00 2001 From: nicm <nicm> Date: Sun, 16 Oct 2016 17:55:14 +0000 Subject: [PATCH] Rewrite command queue handling. Each client still has a command queue, but there is also now a global command queue. Instead of command queues being dispatched on demand from wherever the command happens to be added, they are now all dispatched from the top level server loop. Command queues may now also include callbacks as well as commands, and items may be inserted after the current command as well as at the end. This all makes command queues significantly more predictable and easier to use, and avoids the complex multiple nested command queues used by source-file, if-shell and friends. A mass rename of struct cmdq to a better name (cmdq_item probably) is coming. --- cfg.c | 70 +++---- cmd-attach-session.c | 2 +- cmd-command-prompt.c | 30 ++- cmd-confirm-before.c | 27 ++- cmd-copy-mode.c | 4 +- cmd-display-panes.c | 48 +++-- cmd-find.c | 2 +- cmd-if-shell.c | 110 +++++----- cmd-load-buffer.c | 45 ++-- cmd-new-session.c | 6 +- cmd-new-window.c | 4 +- cmd-queue.c | 489 ++++++++++++++++++++++++++----------------- cmd-resize-pane.c | 4 +- cmd-run-shell.c | 28 ++- cmd-send-keys.c | 2 +- cmd-source-file.c | 54 ++--- cmd-split-window.c | 4 +- cmd-wait-for.c | 69 +++--- cmd.c | 51 ++--- control.c | 29 ++- format.c | 9 +- hooks.c | 68 ++---- key-bindings.c | 22 +- notify.c | 12 +- server-client.c | 44 ++-- server.c | 10 +- tmux.1 | 4 +- tmux.h | 268 ++++++++++++------------ window-choose.c | 4 +- 29 files changed, 817 insertions(+), 702 deletions(-) diff --git a/cfg.c b/cfg.c index 801056db..62cca179 100644 --- a/cfg.c +++ b/cfg.c @@ -29,14 +29,25 @@ #include "tmux.h" char *cfg_file; -static struct cmd_q *cfg_cmd_q; int cfg_finished; -int cfg_references; static char **cfg_causes; static u_int cfg_ncauses; struct client *cfg_client; -static void cfg_default_done(struct cmd_q *); +static enum cmd_retval +cfg_done(__unused struct cmd_q *cmdq, __unused void *data) +{ + if (cfg_finished) + return (CMD_RETURN_NORMAL); + cfg_finished = 1; + + if (!RB_EMPTY(&sessions)) + cfg_show_causes(RB_MIN(sessions, &sessions)); + + if (cfg_client != NULL) + server_client_unref(cfg_client); + return (CMD_RETURN_NORMAL); +} void set_cfg_file(const char *path) @@ -51,30 +62,24 @@ start_cfg(void) const char *home; int quiet = 0; - cfg_cmd_q = cmdq_new(NULL); - cfg_cmd_q->emptyfn = cfg_default_done; - - cfg_finished = 0; - cfg_references = 1; - cfg_client = TAILQ_FIRST(&clients); if (cfg_client != NULL) cfg_client->references++; - load_cfg(TMUX_CONF, cfg_cmd_q, 1); + load_cfg(TMUX_CONF, cfg_client, NULL, 1); if (cfg_file == NULL && (home = find_home()) != NULL) { xasprintf(&cfg_file, "%s/.tmux.conf", home); quiet = 1; } if (cfg_file != NULL) - load_cfg(cfg_file, cfg_cmd_q, quiet); + load_cfg(cfg_file, cfg_client, NULL, quiet); - cmdq_continue(cfg_cmd_q); + cmdq_append(cfg_client, cmdq_get_callback(cfg_done, NULL)); } int -load_cfg(const char *path, struct cmd_q *cmdq, int quiet) +load_cfg(const char *path, struct client *c, struct cmd_q *cmdq, int quiet) { FILE *f; char delim[3] = { '\\', '\\', '\0' }; @@ -82,6 +87,7 @@ load_cfg(const char *path, struct cmd_q *cmdq, int quiet) size_t line = 0; char *buf, *cause1, *p; struct cmd_list *cmdlist; + struct cmd_q *new_cmdq; log_debug("loading %s", path); if ((f = fopen(path, "rb")) == NULL) { @@ -117,8 +123,13 @@ load_cfg(const char *path, struct cmd_q *cmdq, int quiet) if (cmdlist == NULL) continue; - cmdq_append(cmdq, cmdlist, NULL); + new_cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0); + if (cmdq != NULL) + cmdq_insert_after(cmdq, new_cmdq); + else + cmdq_append(c, new_cmdq); cmd_list_free(cmdlist); + found++; } fclose(f); @@ -126,37 +137,6 @@ load_cfg(const char *path, struct cmd_q *cmdq, int quiet) return (found); } -static void -cfg_default_done(__unused struct cmd_q *cmdq) -{ - log_debug("%s: %u references%s", __func__, cfg_references, - cfg_finished ? " (finished)" : ""); - - if (cfg_finished || --cfg_references != 0) - return; - cfg_finished = 1; - - if (!RB_EMPTY(&sessions)) - cfg_show_causes(RB_MIN(sessions, &sessions)); - - cmdq_free(cfg_cmd_q); - cfg_cmd_q = NULL; - - if (cfg_client != NULL) { - /* - * The client command queue starts with client_exit set to 1 so - * only continue if not empty (that is, we have been delayed - * during configuration parsing for long enough that the - * MSG_COMMAND has arrived), else the client will exit before - * the MSG_COMMAND which might tell it not to. - */ - if (!TAILQ_EMPTY(&cfg_client->cmdq->queue)) - cmdq_continue(cfg_client->cmdq); - server_client_unref(cfg_client); - cfg_client = NULL; - } -} - void cfg_add_cause(const char *fmt, ...) { diff --git a/cmd-attach-session.c b/cmd-attach-session.c index daab428f..f0e860f9 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -145,7 +145,7 @@ cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); hooks_run(c->session->hooks, c, NULL, "client-attached"); - cmdq->client_exit = 0; + c->flags |= CLIENT_ATTACHED; } recalculate_sizes(); alerts_check_session(s); diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 12ecb493..5e21b2bc 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -121,12 +121,24 @@ cmd_command_prompt_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } +static enum cmd_retval +cmd_command_prompt_error(struct cmd_q *cmdq, void *data) +{ + char *error = data; + + cmdq_error(cmdq, "%s", error); + free(error); + + return (CMD_RETURN_NORMAL); +} + static int cmd_command_prompt_callback(void *data, const char *s) { struct cmd_command_prompt_cdata *cdata = data; struct client *c = cdata->c; struct cmd_list *cmdlist; + struct cmd_q *new_cmdq; char *cause, *new_template, *prompt, *ptr; char *input = NULL; @@ -153,17 +165,19 @@ cmd_command_prompt_callback(void *data, const char *s) if (cmd_string_parse(new_template, &cmdlist, NULL, 0, &cause) != 0) { if (cause != NULL) { - *cause = toupper((u_char) *cause); - status_message_set(c, "%s", cause); - free(cause); - } - return (0); + new_cmdq = cmdq_get_callback(cmd_command_prompt_error, + cause); + } else + new_cmdq = NULL; + } else { + new_cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0); + cmd_list_free(cmdlist); } - cmdq_run(c->cmdq, cmdlist, NULL); - cmd_list_free(cmdlist); + if (new_cmdq != NULL) + cmdq_append(c, new_cmdq); - if (c->prompt_callbackfn != (void *) &cmd_command_prompt_callback) + if (c->prompt_callbackfn != (void *)&cmd_command_prompt_callback) return (1); return (0); } diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 4241aefb..2dd52c81 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -83,12 +83,24 @@ cmd_confirm_before_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } +static enum cmd_retval +cmd_confirm_before_error(struct cmd_q *cmdq, void *data) +{ + char *error = data; + + cmdq_error(cmdq, "%s", error); + free(error); + + return (CMD_RETURN_NORMAL); +} + static int cmd_confirm_before_callback(void *data, const char *s) { struct cmd_confirm_before_data *cdata = data; struct client *c = cdata->client; struct cmd_list *cmdlist; + struct cmd_q *new_cmdq; char *cause; if (c->flags & CLIENT_DEAD) @@ -101,14 +113,17 @@ cmd_confirm_before_callback(void *data, const char *s) if (cmd_string_parse(cdata->cmd, &cmdlist, NULL, 0, &cause) != 0) { if (cause != NULL) { - cmdq_error(c->cmdq, "%s", cause); - free(cause); - } - return (0); + new_cmdq = cmdq_get_callback(cmd_confirm_before_error, + cause); + } else + new_cmdq = NULL; + } else { + new_cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0); + cmd_list_free(cmdlist); } - cmdq_run(c->cmdq, cmdlist, NULL); - cmd_list_free(cmdlist); + if (new_cmdq != NULL) + cmdq_append(c, new_cmdq); return (0); } diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 4591a37d..dc880d56 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -61,7 +61,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp = cmdq->state.tflag.wp; if (args_has(args, 'M')) { - if ((wp = cmd_mouse_pane(&cmdq->item->mouse, &s, NULL)) == NULL) + if ((wp = cmd_mouse_pane(&cmdq->mouse, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); @@ -80,7 +80,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'M')) { if (wp->mode != NULL && wp->mode != &window_copy_mode) return (CMD_RETURN_NORMAL); - window_copy_start_drag(c, &cmdq->item->mouse); + window_copy_start_drag(c, &cmdq->mouse); } if (wp->mode == &window_copy_mode && args_has(self->args, 'u')) window_copy_pageup(wp, 0); diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 2edb2eb3..471bec02 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -65,32 +65,48 @@ cmd_display_panes_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } +static enum cmd_retval +cmd_display_panes_error(struct cmd_q *cmdq, void *data) +{ + char *error = data; + + cmdq_error(cmdq, "%s", error); + free(error); + + return (CMD_RETURN_NORMAL); +} + static void cmd_display_panes_callback(struct client *c, struct window_pane *wp) { struct cmd_list *cmdlist; + struct cmd_q *new_cmdq; char *template, *cmd, *expanded, *cause; template = c->identify_callback_data; - if (wp != NULL) { - xasprintf(&expanded, "%%%u", wp->id); - cmd = cmd_template_replace(template, expanded, 1); + if (wp == NULL) + goto out; + xasprintf(&expanded, "%%%u", wp->id); + cmd = cmd_template_replace(template, expanded, 1); - if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { - if (cause != NULL) { - *cause = toupper((u_char) *cause); - status_message_set(c, "%s", cause); - free(cause); - } - } else { - cmdq_run(c->cmdq, cmdlist, NULL); - cmd_list_free(cmdlist); - } - - free(cmd); - free(expanded); + if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { + if (cause != NULL) { + new_cmdq = cmdq_get_callback(cmd_display_panes_error, + cause); + } else + new_cmdq = NULL; + } else { + new_cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0); + cmd_list_free(cmdlist); } + if (new_cmdq != NULL) + cmdq_append(c, new_cmdq); + + free(cmd); + free(expanded); + +out: free(c->identify_callback_data); c->identify_callback_data = NULL; c->identify_callback = NULL; diff --git a/cmd-find.c b/cmd-find.c index 21691f67..f9cbe442 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -1006,7 +1006,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_find_state *current, /* Mouse target is a plain = or {mouse}. */ if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { - m = &cmdq->item->mouse; + m = &cmdq->mouse; switch (type) { case CMD_FIND_PANE: fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 2a8cbff2..7192b204 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -31,9 +31,9 @@ static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmd_q *); -static void cmd_if_shell_callback(struct job *); -static void cmd_if_shell_done(struct cmd_q *); -static void cmd_if_shell_free(void *); +static enum cmd_retval cmd_if_shell_error(struct cmd_q *, void *); +static void cmd_if_shell_callback(struct job *); +static void cmd_if_shell_free(void *); const struct cmd_entry cmd_if_shell_entry = { .name = "if-shell", @@ -56,11 +56,9 @@ struct cmd_if_shell_data { char *cmd_if; char *cmd_else; + struct client *client; struct cmd_q *cmdq; struct mouse_event mouse; - - int bflag; - int references; }; static enum cmd_retval @@ -70,6 +68,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct cmd_if_shell_data *cdata; char *shellcmd, *cmd, *cause; struct cmd_list *cmdlist; + struct cmd_q *new_cmdq; struct session *s = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; struct window_pane *wp = cmdq->state.tflag.wp; @@ -104,7 +103,8 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) } return (CMD_RETURN_ERROR); } - cmdq_run(cmdq, cmdlist, &cmdq->item->mouse); + new_cmdq = cmdq_get_command(cmdlist, NULL, &cmdq->mouse, 0); + cmdq_insert_after(cmdq, new_cmdq); cmd_list_free(cmdlist); return (CMD_RETURN_NORMAL); } @@ -121,92 +121,80 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) else cdata->cmd_else = NULL; - cdata->bflag = args_has(args, 'b'); + cdata->client = cmdq->client; + cdata->client->references++; - cdata->cmdq = cmdq; - memcpy(&cdata->mouse, &cmdq->item->mouse, sizeof cdata->mouse); - cmdq->references++; + if (!args_has(args, 'b')) + cdata->cmdq = cmdq; + else + cdata->cmdq = NULL; + memcpy(&cdata->mouse, &cmdq->mouse, sizeof cdata->mouse); - cdata->references = 1; job_run(shellcmd, s, cwd, cmd_if_shell_callback, cmd_if_shell_free, cdata); free(shellcmd); - if (cdata->bflag) + if (args_has(args, 'b')) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } +static enum cmd_retval +cmd_if_shell_error(struct cmd_q *cmdq, void *data) +{ + char *error = data; + + cmdq_error(cmdq, "%s", error); + free(error); + + return (CMD_RETURN_NORMAL); +} + static void cmd_if_shell_callback(struct job *job) { struct cmd_if_shell_data *cdata = job->data; - struct cmd_q *cmdq = cdata->cmdq, *cmdq1; + struct client *c = cdata->client; struct cmd_list *cmdlist; - char *cause, *cmd; - - if (cmdq->flags & CMD_Q_DEAD) - return; + struct cmd_q *new_cmdq; + char *cause, *cmd, *file = cdata->file; + u_int line = cdata->line; if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0) cmd = cdata->cmd_else; else cmd = cdata->cmd_if; if (cmd == NULL) - return; + goto out; - if (cmd_string_parse(cmd, &cmdlist, cdata->file, cdata->line, - &cause) != 0) { - if (cause != NULL) { - cmdq_error(cmdq, "%s", cause); - free(cause); - } - return; + if (cmd_string_parse(cmd, &cmdlist, file, line, &cause) != 0) { + if (cause != NULL) + new_cmdq = cmdq_get_callback(cmd_if_shell_error, cause); + else + new_cmdq = NULL; + } else { + new_cmdq = cmdq_get_command(cmdlist, NULL, &cdata->mouse, 0); + cmd_list_free(cmdlist); } - cmdq1 = cmdq_new(cmdq->client); - cmdq1->emptyfn = cmd_if_shell_done; - cmdq1->data = cdata; + if (new_cmdq != NULL) { + if (cdata->cmdq == NULL) + cmdq_append(c, new_cmdq); + else + cmdq_insert_after(cdata->cmdq, new_cmdq); + } - cdata->references++; - cmdq_run(cmdq1, cmdlist, &cdata->mouse); - cmd_list_free(cmdlist); -} - -static void -cmd_if_shell_done(struct cmd_q *cmdq1) -{ - struct cmd_if_shell_data *cdata = cmdq1->data; - struct cmd_q *cmdq = cdata->cmdq; - - if (cmdq1->client_exit >= 0) - cmdq->client_exit = cmdq1->client_exit; - cmdq_free(cmdq1); - - if (--cdata->references != 0) - return; - - if (!cmdq_free(cmdq) && !cdata->bflag) - cmdq_continue(cmdq); - - free(cdata->cmd_else); - free(cdata->cmd_if); - - free(cdata->file); - free(cdata); +out: + if (cdata->cmdq != NULL) + cdata->cmdq->flags &= ~CMD_Q_WAITING; } static void cmd_if_shell_free(void *data) { struct cmd_if_shell_data *cdata = data; - struct cmd_q *cmdq = cdata->cmdq; - if (--cdata->references != 0) - return; - - if (!cmdq_free(cmdq) && !cdata->bflag) - cmdq_continue(cmdq); + server_client_unref(cdata->client); free(cdata->cmd_else); free(cdata->cmd_if); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index ca886d69..ae071968 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -46,17 +46,24 @@ const struct cmd_entry cmd_load_buffer_entry = { .exec = cmd_load_buffer_exec }; +struct cmd_load_buffer_data { + struct cmd_q *cmdq; + char *bufname; +}; + static enum cmd_retval cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct client *c = cmdq->client; - struct session *s; - FILE *f; - const char *path, *bufname, *cwd; - char *pdata, *new_pdata, *cause, *file, resolved[PATH_MAX]; - size_t psize; - int ch, error; + struct args *args = self->args; + struct cmd_load_buffer_data *cdata; + struct client *c = cmdq->client; + struct session *s; + FILE *f; + const char *path, *bufname, *cwd; + char *pdata, *new_pdata, *cause, *file; + char resolved[PATH_MAX]; + size_t psize; + int ch, error; bufname = NULL; if (args_has(args, 'b')) @@ -64,8 +71,12 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) path = args->argv[0]; if (strcmp(path, "-") == 0) { + cdata = xcalloc(1, sizeof *cdata); + cdata->cmdq = cmdq; + cdata->bufname = xstrdup(bufname); + error = server_set_stdin_callback(c, cmd_load_buffer_callback, - (void *)bufname, &cause); + cdata, &cause); if (error != 0) { cmdq_error(cmdq, "%s: %s", path, cause); free(cause); @@ -136,9 +147,9 @@ error: static void cmd_load_buffer_callback(struct client *c, int closed, void *data) { - const char *bufname = data; - char *pdata, *cause, *saved; - size_t psize; + struct cmd_load_buffer_data *cdata = data; + char *pdata, *cause, *saved; + size_t psize; if (!closed) return; @@ -146,7 +157,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) server_client_unref(c); if (c->flags & CLIENT_DEAD) - return; + goto out; psize = EVBUFFER_LENGTH(c->stdin_data); if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) @@ -156,7 +167,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) pdata[psize] = '\0'; evbuffer_drain(c->stdin_data, psize); - if (paste_set(pdata, psize, bufname, &cause) != 0) { + if (paste_set(pdata, psize, cdata->bufname, &cause) != 0) { /* No context so can't use server_client_msg_error. */ if (~c->flags & CLIENT_UTF8) { saved = cause; @@ -168,7 +179,9 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) free(pdata); free(cause); } - out: - cmdq_continue(c->cmdq); + cdata->cmdq->flags &= ~CMD_Q_WAITING; + + free(cdata->bufname); + free(cdata); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 5924b793..e587e5ee 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -312,14 +312,14 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) } if (!detached) - cmdq->client_exit = 0; + c->flags |= CLIENT_ATTACHED; if (to_free != NULL) free((void *)to_free); cmd_find_from_session(&fs, s); - if (hooks_wait(s->hooks, cmdq, &fs, "after-new-session") == 0) - return (CMD_RETURN_WAIT); + hooks_insert(s->hooks, cmdq, &fs, "after-new-session"); + return (CMD_RETURN_NORMAL); error: diff --git a/cmd-new-window.c b/cmd-new-window.c index 4d0e0057..af476913 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -157,8 +157,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) free((void *)to_free); cmd_find_from_winlink(&fs, s, wl); - if (hooks_wait(s->hooks, cmdq, &fs, "after-new-window") == 0) - return (CMD_RETURN_WAIT); + hooks_insert(s->hooks, cmdq, &fs, "after-new-window"); + return (CMD_RETURN_NORMAL); error: diff --git a/cmd-queue.c b/cmd-queue.c index 2012e871..fca3bf04 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -25,47 +25,316 @@ #include "tmux.h" -static enum cmd_retval cmdq_continue_one(struct cmd_q *); -static void cmdq_flush(struct cmd_q *); +/* Global command queue. */ +static struct cmd_q_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue); -/* Create new command queue. */ +/* Get command queue name. */ +static const char * +cmdq_name(struct client *c) +{ + static char s[32]; + + if (c == NULL) + return ("<global>"); + xsnprintf(s, sizeof s, "<%p>", c); + return (s); +} + +/* Get command queue from client. */ +static struct cmd_q_list * +cmdq_get(struct client *c) +{ + if (c == NULL) + return (&global_queue); + return (&c->queue); +} + +/* Append an item. */ +void +cmdq_append(struct client *c, struct cmd_q *cmdq) +{ + struct cmd_q_list *queue = cmdq_get(c); + struct cmd_q *next; + + do { + next = cmdq->next; + cmdq->next = NULL; + + if (c != NULL) + c->references++; + cmdq->client = c; + + cmdq->queue = queue; + TAILQ_INSERT_TAIL(queue, cmdq, entry); + + cmdq = next; + } while (cmdq != NULL); +} + +/* Insert an item. */ +void +cmdq_insert_after(struct cmd_q *after, struct cmd_q *cmdq) +{ + struct client *c = after->client; + struct cmd_q_list *queue = after->queue; + struct cmd_q *next; + + do { + next = cmdq->next; + cmdq->next = NULL; + + if (c != NULL) + c->references++; + cmdq->client = c; + + cmdq->queue = queue; + if (after->next != NULL) + TAILQ_INSERT_AFTER(queue, after->next, cmdq, entry); + else + TAILQ_INSERT_AFTER(queue, after, cmdq, entry); + after->next = cmdq; + + cmdq = next; + } while (cmdq != NULL); +} + +/* Remove an item. */ +static void +cmdq_remove(struct cmd_q *cmdq) +{ + free((void *)cmdq->hook); + + if (cmdq->client != NULL) + server_client_unref(cmdq->client); + + if (cmdq->type == CMD_Q_COMMAND) + cmd_list_free(cmdq->cmdlist); + + TAILQ_REMOVE(cmdq->queue, cmdq, entry); + free(cmdq); +} + +/* Set command group. */ +static u_int +cmdq_next_group(void) +{ + static u_int group; + + return (++group); +} + +/* Remove all subsequent items that match this item's group. */ +static void +cmdq_remove_group(struct cmd_q *cmdq) +{ + struct cmd_q *this, *next; + + this = TAILQ_NEXT(cmdq, entry); + while (this != NULL) { + next = TAILQ_NEXT(this, entry); + if (this->group == cmdq->group) + cmdq_remove(this); + this = next; + } +} + +/* Get a command for the command queue. */ struct cmd_q * -cmdq_new(struct client *c) +cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, + struct mouse_event *m, int flags) +{ + struct cmd_q *cmdq, *first = NULL, *last = NULL; + struct cmd *cmd; + u_int group = cmdq_next_group(); + + TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { + cmdq = xcalloc(1, sizeof *cmdq); + cmdq->type = CMD_Q_COMMAND; + cmdq->group = group; + cmdq->flags = flags; + + cmdq->cmdlist = cmdlist; + cmdq->cmd = cmd; + + if (current != NULL) + cmd_find_copy_state(&cmdq->current, current); + if (m != NULL) + memcpy(&cmdq->mouse, m, sizeof cmdq->mouse); + cmdlist->references++; + + if (first == NULL) + first = cmdq; + if (last != NULL) + last->next = cmdq; + last = cmdq; + } + return (first); +} + +/* Fire command on command queue. */ +static enum cmd_retval +cmdq_fire_command(struct cmd_q *cmdq) +{ + struct client *c = cmdq->client; + struct cmd *cmd = cmdq->cmd; + enum cmd_retval retval; + const char *name; + struct cmd_find_state *fsp, fs; + int flags; + + flags = !!(cmd->flags & CMD_CONTROL); + cmdq_guard(cmdq, "begin", flags); + + if (cmd_prepare_state(cmd, cmdq) != 0) { + retval = CMD_RETURN_ERROR; + goto out; + } + if (cmdq->client == NULL) + cmdq->client = cmd_find_client(cmdq, NULL, CMD_FIND_QUIET); + + retval = cmd->entry->exec(cmd, cmdq); + if (retval == CMD_RETURN_ERROR) + goto out; + + if (cmd->entry->flags & CMD_AFTERHOOK) { + name = cmd->entry->name; + if (cmd_find_valid_state(&cmdq->state.tflag)) + fsp = &cmdq->state.tflag; + else { + if (cmd_find_current(&fs, cmdq, CMD_FIND_QUIET) != 0) + goto out; + fsp = &fs; + } + hooks_insert(fsp->s->hooks, cmdq, fsp, "after-%s", name); + } + +out: + cmdq->client = c; + if (retval == CMD_RETURN_ERROR) + cmdq_guard(cmdq, "error", flags); + else + cmdq_guard(cmdq, "end", flags); + return (retval); +} + +/* Get a callback for the command queue. */ +struct cmd_q * +cmdq_get_callback(cmd_q_cb cb, void *data) { struct cmd_q *cmdq; cmdq = xcalloc(1, sizeof *cmdq); - cmdq->references = 1; + cmdq->type = CMD_Q_CALLBACK; + cmdq->group = 0; cmdq->flags = 0; - cmdq->client = c; - cmdq->client_exit = -1; - - TAILQ_INIT(&cmdq->queue); - cmdq->item = NULL; - cmdq->cmd = NULL; - - cmd_find_clear_state(&cmdq->current, NULL, 0); - cmdq->parent = NULL; + cmdq->cb = cb; + cmdq->data = data; return (cmdq); } -/* Free command queue */ -int -cmdq_free(struct cmd_q *cmdq) +/* Fire callback on callback queue. */ +static enum cmd_retval +cmdq_fire_callback(struct cmd_q *cmdq) { - log_debug("cmdq %p free: %u references", cmdq, cmdq->references); + return (cmdq->cb(cmdq, cmdq->data)); +} - if (--cmdq->references != 0) { - if (cmdq->flags & CMD_Q_DEAD) - return (1); +/* Process next item on command queue. */ +u_int +cmdq_next(struct client *c) +{ + struct cmd_q_list *queue = cmdq_get(c); + const char *name = cmdq_name(c); + struct cmd_q *cmdq; + enum cmd_retval retval; + u_int items = 0; + static u_int number; + + if (TAILQ_EMPTY(queue)) { + log_debug("%s %s: empty", __func__, name); + return (0); + } + if (TAILQ_FIRST(queue)->flags & CMD_Q_WAITING) { + log_debug("%s %s: waiting", __func__, name); return (0); } - cmdq_flush(cmdq); - free(cmdq); - return (1); + log_debug("%s %s: enter", __func__, name); + for (;;) { + cmdq = TAILQ_FIRST(queue); + if (cmdq == NULL) + break; + log_debug("%s %s: type %d, flags %x", __func__, name, + cmdq->type, cmdq->flags); + + /* + * Any item with the waiting flag set waits until an external + * event clears the flag (for example, a job - look at + * run-shell). + */ + if (cmdq->flags & CMD_Q_WAITING) + goto waiting; + + /* + * Items are only fired once, once the fired flag is set, a + * waiting flag can only be cleared by an external event. + */ + if (~cmdq->flags & CMD_Q_FIRED) { + cmdq->time = time(NULL); + cmdq->number = ++number; + + switch (cmdq->type) + { + case CMD_Q_COMMAND: + retval = cmdq_fire_command(cmdq); + + /* + * If a command returns an error, remove any + * subsequent commands in the same group. + */ + if (retval == CMD_RETURN_ERROR) + cmdq_remove_group(cmdq); + break; + case CMD_Q_CALLBACK: + retval = cmdq_fire_callback(cmdq); + break; + default: + retval = CMD_RETURN_ERROR; + break; + } + cmdq->flags |= CMD_Q_FIRED; + + if (retval == CMD_RETURN_WAIT) { + cmdq->flags |= CMD_Q_WAITING; + goto waiting; + } + items++; + } + cmdq_remove(cmdq); + } + + log_debug("%s %s: exit (empty)", __func__, name); + return (items); + +waiting: + log_debug("%s %s: exit (wait)", __func__, name); + return (items); +} + +/* Print a guard line. */ +void +cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) +{ + struct client *c = cmdq->client; + + if (c == NULL || !(c->flags & CLIENT_CONTROL)) + return; + + evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, + (long)cmdq->time, cmdq->number, flags); + server_client_push_stdout(c); } /* Show message from command. */ @@ -140,175 +409,3 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) free(msg); } - -/* Print a guard line. */ -void -cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) -{ - struct client *c = cmdq->client; - - if (c == NULL || !(c->flags & CLIENT_CONTROL)) - return; - - evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, - (long) cmdq->time, cmdq->number, flags); - server_client_push_stdout(c); -} - -/* Add command list to queue and begin processing if needed. */ -void -cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) -{ - cmdq_append(cmdq, cmdlist, m); - - if (cmdq->item == NULL) { - cmdq->cmd = NULL; - cmdq_continue(cmdq); - } -} - -/* Add command list to queue. */ -void -cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) -{ - struct cmd_q_item *item; - - item = xcalloc(1, sizeof *item); - item->cmdlist = cmdlist; - TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry); - cmdlist->references++; - - if (m != NULL) - memcpy(&item->mouse, m, sizeof item->mouse); - else - item->mouse.valid = 0; -} - -/* Process one command. */ -static enum cmd_retval -cmdq_continue_one(struct cmd_q *cmdq) -{ - struct cmd_list *cmdlist = cmdq->item->cmdlist; - struct cmd *cmd = cmdq->cmd; - enum cmd_retval retval; - char *tmp; - int flags = !!(cmd->flags & CMD_CONTROL); - const char *name; - struct cmd_find_state *fsp, fs; - - cmdlist->references++; - - tmp = cmd_print(cmd); - log_debug("cmdq %p: %s", cmdq, tmp); - free(tmp); - - cmdq->time = time(NULL); - cmdq->number++; - - cmdq_guard(cmdq, "begin", flags); - - if (cmd_prepare_state(cmd, cmdq, cmdq->parent) != 0) - goto error; - - retval = cmd->entry->exec(cmd, cmdq); - if (retval == CMD_RETURN_ERROR) - goto error; - - if (~cmd->entry->flags & CMD_AFTERHOOK) - goto end; - - if (cmd_find_valid_state(&cmdq->state.tflag)) - fsp = &cmdq->state.tflag; - else { - if (cmd_find_current(&fs, cmdq, CMD_FIND_QUIET) != 0) - goto end; - fsp = &fs; - } - name = cmd->entry->name; - if (hooks_wait(fsp->s->hooks, cmdq, fsp, "after-%s", name) == 0) - retval = CMD_RETURN_WAIT; - -end: - cmdq_guard(cmdq, "end", flags); - cmd_list_free(cmdlist); - return (retval); - -error: - cmdq_guard(cmdq, "error", flags); - cmd_list_free(cmdlist); - return (CMD_RETURN_ERROR); -} - -/* Continue processing command queue. Returns 1 if finishes empty. */ -int -cmdq_continue(struct cmd_q *cmdq) -{ - struct client *c = cmdq->client; - struct cmd_q_item *next; - enum cmd_retval retval; - int empty; - - log_debug("continuing cmdq %p: flags %#x (%p)", cmdq, cmdq->flags, c); - cmdq->references++; - - empty = TAILQ_EMPTY(&cmdq->queue); - 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); - - do { - while (cmdq->cmd != NULL) { - retval = cmdq_continue_one(cmdq); - if (retval == CMD_RETURN_ERROR) - break; - if (retval == CMD_RETURN_WAIT) - goto out; - if (retval == CMD_RETURN_STOP) { - cmdq_flush(cmdq); - goto empty; - } - cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); - } - next = TAILQ_NEXT(cmdq->item, qentry); - - TAILQ_REMOVE(&cmdq->queue, cmdq->item, qentry); - cmd_list_free(cmdq->item->cmdlist); - free(cmdq->item); - - cmdq->item = next; - if (cmdq->item != NULL) - cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); - } while (cmdq->item != NULL); - -empty: - log_debug("cmdq %p empty", cmdq); - if (cmdq->client_exit > 0) - cmdq->client->flags |= CLIENT_EXIT; - if (cmdq->emptyfn != NULL) - cmdq->emptyfn(cmdq); - empty = 1; - -out: - cmdq_free(cmdq); - return (empty); -} - -/* Flush command queue. */ -static void -cmdq_flush(struct cmd_q *cmdq) -{ - struct cmd_q_item *item, *item1; - - TAILQ_FOREACH_SAFE(item, &cmdq->queue, qentry, item1) { - TAILQ_REMOVE(&cmdq->queue, item, qentry); - cmd_list_free(item->cmdlist); - free(item); - } - cmdq->item = NULL; -} - diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index be652b56..3360bd6b 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -60,12 +60,12 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) int x, y; if (args_has(args, 'M')) { - if (cmd_mouse_window(&cmdq->item->mouse, &s) == NULL) + if (cmd_mouse_window(&cmdq->mouse, &s) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); c->tty.mouse_drag_update = cmd_resize_pane_mouse_update; - cmd_resize_pane_mouse_update(c, &cmdq->item->mouse); + cmd_resize_pane_mouse_update(c, &cmdq->mouse); return (CMD_RETURN_NORMAL); } diff --git a/cmd-run-shell.c b/cmd-run-shell.c index c81c76f8..ad2d4c2f 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -51,7 +51,6 @@ const struct cmd_entry cmd_run_shell_entry = { struct cmd_run_shell_data { char *cmd; struct cmd_q *cmdq; - int bflag; int wp_id; }; @@ -92,6 +91,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) cwd = s->cwd; else cwd = NULL; + ft = format_create(cmdq, 0); format_defaults(ft, cmdq->state.c, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); @@ -99,20 +99,24 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) cdata = xcalloc(1, sizeof *cdata); cdata->cmd = shellcmd; - cdata->bflag = args_has(args, 'b'); if (args_has(args, 't') && wp != NULL) cdata->wp_id = wp->id; else cdata->wp_id = -1; - cdata->cmdq = cmdq; - cmdq->references++; + if (args_has(args, 't') && wp != NULL) + cdata->wp_id = wp->id; + else + cdata->wp_id = -1; + + if (!args_has(args, 'b')) + cdata->cmdq = cmdq; job_run(shellcmd, s, cwd, cmd_run_shell_callback, cmd_run_shell_free, cdata); - if (cdata->bflag) + if (args_has(args, 'b')) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } @@ -121,16 +125,11 @@ static void cmd_run_shell_callback(struct job *job) { struct cmd_run_shell_data *cdata = job->data; - struct cmd_q *cmdq = cdata->cmdq; - char *cmd, *msg, *line; + char *cmd = cdata->cmd, *msg, *line; size_t size; int retcode; u_int lines; - if (cmdq->flags & CMD_Q_DEAD) - return; - cmd = cdata->cmd; - lines = 0; do { if ((line = evbuffer_readline(job->event->input)) != NULL) { @@ -163,16 +162,15 @@ cmd_run_shell_callback(struct job *job) if (msg != NULL) cmd_run_shell_print(job, msg); free(msg); + + if (cdata->cmdq != NULL) + cdata->cmdq->flags &= ~CMD_Q_WAITING; } static void cmd_run_shell_free(void *data) { struct cmd_run_shell_data *cdata = data; - struct cmd_q *cmdq = cdata->cmdq; - - if (!cmdq_free(cmdq) && !cdata->bflag) - cmdq_continue(cmdq); free(cdata->cmd); free(cdata); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 062a13d7..94316834 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -62,7 +62,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->state.c; struct window_pane *wp = cmdq->state.tflag.wp; struct session *s = cmdq->state.tflag.s; - struct mouse_event *m = &cmdq->item->mouse; + struct mouse_event *m = &cmdq->mouse; const u_char *keystr; int i, literal; key_code key; diff --git a/cmd-source-file.c b/cmd-source-file.c index 336a79e6..6f9c4451 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -28,7 +28,7 @@ static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmd_q *); -static void cmd_source_file_done(struct cmd_q *); +static enum cmd_retval cmd_source_file_done(struct cmd_q *, void *); const struct cmd_entry cmd_source_file_entry = { .name = "source-file", @@ -45,53 +45,31 @@ static enum cmd_retval cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct cmd_q *cmdq1; + struct client *c = cmdq->client; int quiet; - - cmdq1 = cmdq_new(cmdq->client); - cmdq1->emptyfn = cmd_source_file_done; - cmdq1->data = cmdq; + struct cmd_q *new_cmdq; quiet = args_has(args, 'q'); - switch (load_cfg(args->argv[0], cmdq1, quiet)) { + switch (load_cfg(args->argv[0], c, cmdq, quiet)) { case -1: - cmdq_free(cmdq1); - if (cfg_references == 0) { + if (cfg_finished) cfg_print_causes(cmdq); - return (CMD_RETURN_ERROR); - } - return (CMD_RETURN_NORMAL); + return (CMD_RETURN_ERROR); case 0: - cmdq_free(cmdq1); - if (cfg_references == 0) + if (cfg_finished) cfg_print_causes(cmdq); return (CMD_RETURN_NORMAL); } - - log_debug("%s: cmdq %p, parent %p", __func__, cmdq1, cmdq); - - cmdq->references++; - cfg_references++; - - cmdq_continue(cmdq1); - return (CMD_RETURN_WAIT); + if (cfg_finished) { + new_cmdq = cmdq_get_callback(cmd_source_file_done, NULL); + cmdq_insert_after(cmdq, new_cmdq); + } + return (CMD_RETURN_NORMAL); } -static void -cmd_source_file_done(struct cmd_q *cmdq1) +static enum cmd_retval +cmd_source_file_done(struct cmd_q *cmdq, __unused void *data) { - struct cmd_q *cmdq = cmdq1->data; - - log_debug("%s: cmdq %p, parent %p", __func__, cmdq1, cmdq); - - if (cmdq1->client_exit >= 0) - cmdq->client_exit = cmdq1->client_exit; - cmdq_free(cmdq1); - - cfg_references--; - if (cmdq_free(cmdq)) - return; - if (cfg_references == 0) - cfg_print_causes(cmdq); - cmdq_continue(cmdq); + cfg_print_causes(cmdq); + return (CMD_RETURN_NORMAL); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 41a53c63..ec2ea8cc 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -188,8 +188,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) fs.w = w; fs.wp = new_wp; cmd_find_log_state(__func__, &fs); - if (hooks_wait(s->hooks, cmdq, &fs, "after-split-window") == 0) - return (CMD_RETURN_WAIT); + hooks_insert(s->hooks, cmdq, &fs, "after-split-window"); + return (CMD_RETURN_NORMAL); error: diff --git a/cmd-wait-for.c b/cmd-wait-for.c index fb2fb699..505e4d63 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -41,13 +41,18 @@ const struct cmd_entry cmd_wait_for_entry = { .exec = cmd_wait_for_exec }; +struct wait_item { + struct cmd_q *cmdq; + TAILQ_ENTRY(wait_item) entry; +}; + struct wait_channel { const char *name; int locked; int woken; - TAILQ_HEAD(, cmd_q) waiters; - TAILQ_HEAD(, cmd_q) lockers; + TAILQ_HEAD(, wait_item) waiters; + TAILQ_HEAD(, wait_item) lockers; RB_ENTRY(wait_channel) entry; }; @@ -135,7 +140,7 @@ static enum cmd_retval cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { - struct cmd_q *wq, *wq1; + struct wait_item *wi, *wi1; if (wc == NULL) wc = cmd_wait_for_add(name); @@ -147,10 +152,11 @@ cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name, } log_debug("signal wait channel %s, with waiters", wc->name); - TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { - TAILQ_REMOVE(&wc->waiters, wq, waitentry); - if (!cmdq_free(wq)) - cmdq_continue(wq); + TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { + wi->cmdq->flags &= ~CMD_Q_WAITING; + + TAILQ_REMOVE(&wc->waiters, wi, entry); + free(wi); } cmd_wait_for_remove(wc); @@ -158,10 +164,10 @@ cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name, } static enum cmd_retval -cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, - struct wait_channel *wc) +cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { - struct client *c = cmdq->client; + struct client *c = cmdq->client; + struct wait_item *wi; if (c == NULL || c->session != NULL) { cmdq_error(cmdq, "not able to wait"); @@ -178,16 +184,18 @@ cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, } log_debug("wait channel %s not woken (%p)", wc->name, c); - TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); - cmdq->references++; + wi = xcalloc(1, sizeof *wi); + wi->cmdq = cmdq; + TAILQ_INSERT_TAIL(&wc->waiters, wi, entry); return (CMD_RETURN_WAIT); } static enum cmd_retval -cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, - struct wait_channel *wc) +cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { + struct wait_item *wi; + if (cmdq->client == NULL || cmdq->client->session != NULL) { cmdq_error(cmdq, "not able to lock"); return (CMD_RETURN_ERROR); @@ -197,8 +205,9 @@ cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, wc = cmd_wait_for_add(name); if (wc->locked) { - TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); - cmdq->references++; + wi = xcalloc(1, sizeof *wi); + wi->cmdq = cmdq; + TAILQ_INSERT_TAIL(&wc->lockers, wi, entry); return (CMD_RETURN_WAIT); } wc->locked = 1; @@ -210,17 +219,17 @@ static enum cmd_retval cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { - struct cmd_q *wq; + struct wait_item *wi; if (wc == NULL || !wc->locked) { cmdq_error(cmdq, "channel %s not locked", name); return (CMD_RETURN_ERROR); } - if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { - TAILQ_REMOVE(&wc->lockers, wq, waitentry); - if (!cmdq_free(wq)) - cmdq_continue(wq); + if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) { + wi->cmdq->flags &= ~CMD_Q_WAITING; + TAILQ_REMOVE(&wc->lockers, wi, entry); + free(wi); } else { wc->locked = 0; cmd_wait_for_remove(wc); @@ -233,19 +242,19 @@ void cmd_wait_for_flush(void) { struct wait_channel *wc, *wc1; - struct cmd_q *wq, *wq1; + struct wait_item *wi, *wi1; RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { - TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { - TAILQ_REMOVE(&wc->waiters, wq, waitentry); - if (!cmdq_free(wq)) - cmdq_continue(wq); + TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { + wi->cmdq->flags &= ~CMD_Q_WAITING; + TAILQ_REMOVE(&wc->waiters, wi, entry); + free(wi); } wc->woken = 1; - TAILQ_FOREACH_SAFE(wq, &wc->lockers, waitentry, wq1) { - TAILQ_REMOVE(&wc->lockers, wq, waitentry); - if (!cmdq_free(wq)) - cmdq_continue(wq); + TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) { + wi->cmdq->flags &= ~CMD_Q_WAITING; + TAILQ_REMOVE(&wc->lockers, wi, entry); + free(wi); } wc->locked = 0; cmd_wait_for_remove(wc); diff --git a/cmd.c b/cmd.c index 7964f654..9c37b849 100644 --- a/cmd.c +++ b/cmd.c @@ -390,12 +390,11 @@ usage: static int cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, - struct cmd_q *cmdq, struct cmd_q *parent) + struct cmd_q *cmdq) { int targetflags, error; struct cmd_find_state *fs = NULL; - struct cmd_find_state *current = NULL; - struct cmd_find_state tmp; + struct cmd_find_state current; if (flag == CMD_NONE || flag == CMD_CLIENT || @@ -449,21 +448,12 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, default: fatalx("unknown %cflag %d", c, flag); } - log_debug("%s: flag %c %d %#x", __func__, c, flag, targetflags); - if (parent != NULL) { - if (c == 't') - current = &parent->state.tflag; - else if (c == 's') - current = &parent->state.sflag; - } - if (current == NULL || !cmd_find_valid_state(current)) { - error = cmd_find_current(&tmp, cmdq, targetflags); - if (error != 0 && ~targetflags & CMD_FIND_QUIET) - return (-1); - current = &tmp; - } - if (!cmd_find_empty_state(current) && !cmd_find_valid_state(current)) + + error = cmd_find_current(¤t, cmdq, targetflags); + if (error != 0 && ~targetflags & CMD_FIND_QUIET) + return (-1); + if (!cmd_find_empty_state(¤t) && !cmd_find_valid_state(¤t)) fatalx("invalid current state"); switch (flag) { @@ -475,13 +465,13 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, case CMD_SESSION_CANFAIL: case CMD_SESSION_PREFERUNATTACHED: case CMD_SESSION_WITHPANE: - error = cmd_find_target(fs, current, cmdq, target, + error = cmd_find_target(fs, ¤t, cmdq, target, CMD_FIND_SESSION, targetflags); if (error != 0 && ~targetflags & CMD_FIND_QUIET) return (-1); break; case CMD_MOVEW_R: - error = cmd_find_target(fs, current, cmdq, target, + error = cmd_find_target(fs, ¤t, cmdq, target, CMD_FIND_SESSION, CMD_FIND_QUIET); if (error == 0) break; @@ -490,7 +480,7 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, case CMD_WINDOW_CANFAIL: case CMD_WINDOW_MARKED: case CMD_WINDOW_INDEX: - error = cmd_find_target(fs, current, cmdq, target, + error = cmd_find_target(fs, ¤t, cmdq, target, CMD_FIND_WINDOW, targetflags); if (error != 0 && ~targetflags & CMD_FIND_QUIET) return (-1); @@ -498,7 +488,7 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, case CMD_PANE: case CMD_PANE_CANFAIL: case CMD_PANE_MARKED: - error = cmd_find_target(fs, current, cmdq, target, + error = cmd_find_target(fs, ¤t, cmdq, target, CMD_FIND_PANE, targetflags); if (error != 0 && ~targetflags & CMD_FIND_QUIET) return (-1); @@ -510,14 +500,14 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, } int -cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq, struct cmd_q *parent) +cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) { - const struct cmd_entry *entry = cmd->entry; - struct cmd_state *state = &cmdq->state; - char *tmp; - enum cmd_entry_flag flag; - const char *s; - int error; + const struct cmd_entry *entry = cmd->entry; + struct cmd_state *state = &cmdq->state; + char *tmp; + enum cmd_entry_flag flag; + const char *s; + int error; tmp = cmd_print(cmd); log_debug("preparing state for %s (client %p)", tmp, cmdq->client); @@ -546,18 +536,19 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq, struct cmd_q *parent) state->c = cmd_find_client(cmdq, s, 1); break; } + log_debug("using client %p", state->c); s = args_get(cmd->args, 't'); log_debug("preparing -t state: target %s", s == NULL ? "none" : s); - error = cmd_prepare_state_flag('t', s, entry->tflag, cmdq, parent); + error = cmd_prepare_state_flag('t', s, entry->tflag, cmdq); if (error != 0) return (error); s = args_get(cmd->args, 's'); log_debug("preparing -s state: target %s", s == NULL ? "none" : s); - error = cmd_prepare_state_flag('s', s, entry->sflag, cmdq, parent); + error = cmd_prepare_state_flag('s', s, entry->sflag, cmdq); if (error != 0) return (error); diff --git a/control.c b/control.c index f6dedca8..cd605f4f 100644 --- a/control.c +++ b/control.c @@ -49,6 +49,21 @@ control_write_buffer(struct client *c, struct evbuffer *buffer) server_client_push_stdout(c); } +/* Control error callback. */ +static enum cmd_retval +control_error(struct cmd_q *cmdq, void *data) +{ + struct client *c = cmdq->client; + char *error = data; + + cmdq_guard(cmdq, "begin", 1); + control_write(c, "parse error: %s", error); + cmdq_guard(cmdq, "error", 1); + + free(error); + return (CMD_RETURN_NORMAL); +} + /* Control input callback. Read lines and fire commands. */ void control_callback(struct client *c, int closed, __unused void *data) @@ -56,6 +71,7 @@ control_callback(struct client *c, int closed, __unused void *data) char *line, *cause; struct cmd_list *cmdlist; struct cmd *cmd; + struct cmd_q *cmdq; if (closed) c->flags |= CLIENT_EXIT; @@ -70,18 +86,13 @@ control_callback(struct client *c, int closed, __unused void *data) } if (cmd_string_parse(line, &cmdlist, NULL, 0, &cause) != 0) { - c->cmdq->time = time(NULL); - c->cmdq->number++; - - cmdq_guard(c->cmdq, "begin", 1); - control_write(c, "parse error: %s", cause); - cmdq_guard(c->cmdq, "error", 1); - - free(cause); + cmdq = cmdq_get_callback(control_error, cause); + cmdq_append(c, cmdq); } else { TAILQ_FOREACH(cmd, &cmdlist->list, qentry) cmd->flags |= CMD_CONTROL; - cmdq_run(c->cmdq, cmdlist, NULL); + cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0); + cmdq_append(c, cmdq); cmd_list_free(cmdlist); } diff --git a/format.c b/format.c index ed6f2cab..f9128f01 100644 --- a/format.c +++ b/format.c @@ -473,7 +473,6 @@ 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); @@ -491,11 +490,9 @@ format_create(struct cmd_q *cmdq, int flags) format_add_tv(ft, "start_time", &start_time); 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); - } + format_add(ft, "command", "%s", cmdq->cmd->entry->name); + if (cmdq != NULL && cmdq->hook != NULL) + format_add(ft, "hook", "%s", cmdq->hook); return (ft); } diff --git a/hooks.c b/hooks.c index 2e0126b7..cdd2cf45 100644 --- a/hooks.c +++ b/hooks.c @@ -33,7 +33,6 @@ RB_GENERATE_STATIC(hooks_tree, hook, entry, hooks_cmp); static struct hook *hooks_find1(struct hooks *, const char *); static void hooks_free1(struct hooks *, struct hook *); -static void hooks_emptyfn(struct cmd_q *); static int hooks_cmp(struct hook *hook1, struct hook *hook2) @@ -140,28 +139,14 @@ hooks_find(struct hooks *hooks, const char *name) return (hook); } -static void -hooks_emptyfn(struct cmd_q *hooks_cmdq) -{ - struct cmd_q *cmdq = hooks_cmdq->data; - - if (cmdq != NULL) { - if (hooks_cmdq->client_exit >= 0) - cmdq->client_exit = hooks_cmdq->client_exit; - if (!cmdq_free(cmdq)) - cmdq_continue(cmdq); - } - cmdq_free(hooks_cmdq); -} - -int +void hooks_run(struct hooks *hooks, struct client *c, struct cmd_find_state *fs, const char *fmt, ...) { struct hook *hook; - struct cmd_q *hooks_cmdq; va_list ap; char *name; + struct cmd_q *new_cmdq, *loop; va_start(ap, fmt); xvasprintf(&name, fmt, ap); @@ -170,34 +155,30 @@ hooks_run(struct hooks *hooks, struct client *c, struct cmd_find_state *fs, hook = hooks_find(hooks, name); if (hook == NULL) { free(name); - return (-1); + return; } log_debug("running hook %s", name); + + new_cmdq = cmdq_get_command(hook->cmdlist, fs, NULL, CMD_Q_NOHOOKS); + + for (loop = new_cmdq; loop != NULL; loop = loop->next) + loop->hook = xstrdup(name); free(name); - hooks_cmdq = cmdq_new(c); - hooks_cmdq->flags |= CMD_Q_NOHOOKS; - - if (fs != NULL) - cmd_find_copy_state(&hooks_cmdq->current, fs); - hooks_cmdq->parent = NULL; - - cmdq_run(hooks_cmdq, hook->cmdlist, NULL); - cmdq_free(hooks_cmdq); - return (0); + cmdq_append(c, new_cmdq); } -int -hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs, +void +hooks_insert(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs, const char *fmt, ...) { struct hook *hook; - struct cmd_q *hooks_cmdq; va_list ap; char *name; + struct cmd_q *new_cmdq, *loop; if (cmdq->flags & CMD_Q_NOHOOKS) - return (-1); + return; va_start(ap, fmt); xvasprintf(&name, fmt, ap); @@ -206,23 +187,18 @@ hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs, hook = hooks_find(hooks, name); if (hook == NULL) { free(name); - return (-1); + return; } log_debug("running hook %s (parent %p)", name, cmdq); + + new_cmdq = cmdq_get_command(hook->cmdlist, fs, NULL, CMD_Q_NOHOOKS); + + for (loop = new_cmdq; loop != NULL; loop = loop->next) + loop->hook = xstrdup(name); free(name); - hooks_cmdq = cmdq_new(cmdq->client); - hooks_cmdq->flags |= CMD_Q_NOHOOKS; - - if (fs != NULL) - cmd_find_copy_state(&hooks_cmdq->current, fs); - hooks_cmdq->parent = cmdq; - - hooks_cmdq->emptyfn = hooks_emptyfn; - hooks_cmdq->data = cmdq; - if (cmdq != NULL) - cmdq->references++; - cmdq_run(hooks_cmdq, hook->cmdlist, NULL); - return (0); + cmdq_insert_after(cmdq, new_cmdq); + else + cmdq_append(NULL, new_cmdq); } diff --git a/key-bindings.c b/key-bindings.c index b464c199..af53720c 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -377,18 +377,22 @@ key_bindings_init(void) struct cmd_list *cmdlist; char *cause; int error; - struct cmd_q *cmdq; - cmdq = cmdq_new(NULL); for (i = 0; i < nitems(defaults); i++) { error = cmd_string_parse(defaults[i], &cmdlist, "<default-keys>", i, &cause); if (error != 0) fatalx("bad default key"); - cmdq_run(cmdq, cmdlist, NULL); + cmdq_append(NULL, cmdq_get_command(cmdlist, NULL, NULL, 0)); cmd_list_free(cmdlist); } - cmdq_free(cmdq); +} + +static enum cmd_retval +key_bindings_read_only(struct cmd_q *cmdq, __unused void *data) +{ + cmdq_error(cmdq, "client is read-only"); + return (CMD_RETURN_ERROR); } void @@ -403,10 +407,8 @@ key_bindings_dispatch(struct key_binding *bd, struct client *c, if (!(cmd->entry->flags & CMD_READONLY)) readonly = 0; } - if (!readonly && (c->flags & CLIENT_READONLY)) { - cmdq_error(c->cmdq, "client is read-only"); - return; - } - - cmdq_run(c->cmdq, bd->cmdlist, m); + if (!readonly && (c->flags & CLIENT_READONLY)) + cmdq_append(c, cmdq_get_callback(key_bindings_read_only, NULL)); + else + cmdq_append(c, cmdq_get_command(bd->cmdlist, NULL, m, 0)); } diff --git a/notify.c b/notify.c index 416b81f6..a75b3182 100644 --- a/notify.c +++ b/notify.c @@ -66,7 +66,7 @@ notify_hook(struct notify_entry *ne) const char *name; struct cmd_find_state fs; struct hook *hook; - struct cmd_q *hooks_cmdq; + struct cmd_q *new_cmdq, *loop; name = notify_hooks[ne->type]; if (name == NULL) @@ -87,14 +87,12 @@ notify_hook(struct notify_entry *ne) return; log_debug("notify hook %s", name); - hooks_cmdq = cmdq_new(NULL); - hooks_cmdq->flags |= CMD_Q_NOHOOKS; + new_cmdq = cmdq_get_command(hook->cmdlist, &fs, NULL, CMD_Q_NOHOOKS); - cmd_find_copy_state(&hooks_cmdq->current, &fs); - hooks_cmdq->parent = NULL; + for (loop = new_cmdq; loop != NULL; loop = loop->next) + loop->hook = xstrdup(name); - cmdq_run(hooks_cmdq, hook->cmdlist, NULL); - cmdq_free(hooks_cmdq); + cmdq_append(NULL, new_cmdq); } static void diff --git a/server-client.c b/server-client.c index 7e4d1ee4..7ad87cd1 100644 --- a/server-client.c +++ b/server-client.c @@ -126,8 +126,7 @@ server_client_create(int fd) c->fd = -1; c->cwd = NULL; - c->cmdq = cmdq_new(c); - c->cmdq->client_exit = 1; + TAILQ_INIT(&c->queue); c->stdin_data = evbuffer_new(); c->stdout_data = evbuffer_new(); @@ -244,10 +243,6 @@ server_client_lost(struct client *c) free(c->prompt_string); free(c->prompt_buffer); - c->cmdq->flags |= CMD_Q_DEAD; - cmdq_free(c->cmdq); - c->cmdq = NULL; - environ_free(c->environ); proc_remove_peer(c->peer); @@ -281,6 +276,9 @@ server_client_free(__unused int fd, __unused short events, void *arg) log_debug("free client %p (%d references)", c, c->references); + if (!TAILQ_EMPTY(&c->queue)) + fatalx("queue not empty"); + if (c->references == 0) free(c); } @@ -1254,6 +1252,29 @@ server_client_dispatch(struct imsg *imsg, void *arg) } } +/* Callback when command is done. */ +static enum cmd_retval +server_client_command_done(struct cmd_q *cmdq, __unused void *data) +{ + struct client *c = cmdq->client; + + if (~c->flags & CLIENT_ATTACHED) + c->flags |= CLIENT_EXIT; + return (CMD_RETURN_NORMAL); +} + +/* Show an error message. */ +static enum cmd_retval +server_client_command_error(struct cmd_q *cmdq, void *data) +{ + char *error = data; + + cmdq_error(cmdq, "%s", error); + free(error); + + return (CMD_RETURN_NORMAL); +} + /* Handle command message. */ static void server_client_dispatch_command(struct client *c, struct imsg *imsg) @@ -1276,7 +1297,7 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) argc = data.argc; if (cmd_unpack_argv(buf, len, argc, &argv) != 0) { - cmdq_error(c->cmdq, "command too long"); + cause = xstrdup("command too long"); goto error; } @@ -1287,20 +1308,19 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) } if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) { - cmdq_error(c->cmdq, "%s", cause); cmd_free_argv(argc, argv); goto error; } cmd_free_argv(argc, argv); - if (c != cfg_client || cfg_finished) - cmdq_run(c->cmdq, cmdlist, NULL); - else - cmdq_append(c->cmdq, cmdlist, NULL); + cmdq_append(c, cmdq_get_command(cmdlist, NULL, NULL, 0)); + cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL)); cmd_list_free(cmdlist); return; error: + cmdq_append(c, cmdq_get_callback(server_client_command_error, cause)); + if (cmdlist != NULL) cmd_list_free(cmdlist); diff --git a/server.c b/server.c index e6d1c09f..15fd5546 100644 --- a/server.c +++ b/server.c @@ -190,9 +190,17 @@ static int server_loop(void) { struct client *c; + u_int items; + + notify_drain(); + + do { + items = cmdq_next(NULL); + TAILQ_FOREACH(c, &clients, entry) + items += cmdq_next(c); + } while (items != 0); server_client_loop(); - notify_drain(); if (!options_get_number(global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) diff --git a/tmux.1 b/tmux.1 index a07b5e6c..88697bcf 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3456,8 +3456,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 "command" Ta "" Ta "Name of command in use, if any" .It Li "command_list_name" Ta "" Ta "Command name if listing commands" .It Li "command_list_alias" Ta "" Ta "Command alias if listing commands" .It Li "command_list_usage" Ta "" Ta "Command usage if listing commands" @@ -3467,6 +3466,7 @@ The following variables are available, where appropriate: .It Li "history_bytes" Ta "" Ta "Number of bytes in window history" .It Li "history_limit" Ta "" Ta "Maximum window history lines" .It Li "history_size" Ta "" Ta "Size of history in bytes" +.It Li "hook" Ta "" Ta "Name of running hook, if any" .It Li "host" Ta "#H" Ta "Hostname of local host" .It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)" .It Li "insert_flag" Ta "" Ta "Pane insert flag" diff --git a/tmux.h b/tmux.h index bbb345b3..b5e5d96c 100644 --- a/tmux.h +++ b/tmux.h @@ -40,6 +40,8 @@ extern char **environ; struct args; struct client; +struct cmd_q; +struct cmd_q_list; struct environ; struct input_ctx; struct mode_key_cmdstr; @@ -631,7 +633,6 @@ struct grid { struct hook { const char *name; - struct cmd_q *cmdq; struct cmd_list *cmdlist; RB_ENTRY(hook) entry; @@ -1160,101 +1161,6 @@ struct message_entry { TAILQ_ENTRY(message_entry) entry; }; -/* Client connection. */ -struct client { - struct tmuxpeer *peer; - - pid_t pid; - int fd; - struct event event; - int retval; - - struct timeval creation_time; - struct timeval activity_time; - - struct environ *environ; - - char *title; - const char *cwd; - - char *term; - char *ttyname; - struct tty tty; - - void (*stdin_callback)(struct client *, int, void *); - void *stdin_callback_data; - struct evbuffer *stdin_data; - int stdin_closed; - struct evbuffer *stdout_data; - struct evbuffer *stderr_data; - - struct event repeat_timer; - - struct event click_timer; - u_int click_button; - - struct event status_timer; - struct screen status; - -#define CLIENT_TERMINAL 0x1 -#define CLIENT_LOGIN 0x2 -#define CLIENT_EXIT 0x4 -#define CLIENT_REDRAW 0x8 -#define CLIENT_STATUS 0x10 -#define CLIENT_REPEAT 0x20 -#define CLIENT_SUSPENDED 0x40 -/* 0x80 unused */ -#define CLIENT_IDENTIFY 0x100 -#define CLIENT_DEAD 0x200 -#define CLIENT_BORDERS 0x400 -#define CLIENT_READONLY 0x800 -#define CLIENT_REDRAWWINDOW 0x1000 -#define CLIENT_CONTROL 0x2000 -#define CLIENT_CONTROLCONTROL 0x4000 -#define CLIENT_FOCUSED 0x8000 -#define CLIENT_UTF8 0x10000 -#define CLIENT_256COLOURS 0x20000 -#define CLIENT_IDENTIFIED 0x40000 -#define CLIENT_STATUSFORCE 0x80000 -#define CLIENT_DOUBLECLICK 0x100000 -#define CLIENT_TRIPLECLICK 0x200000 - int flags; - struct key_table *keytable; - - struct event identify_timer; - void (*identify_callback)(struct client *, struct window_pane *); - void *identify_callback_data; - - char *message_string; - struct event message_timer; - u_int message_next; - TAILQ_HEAD(, message_entry) message_log; - - char *prompt_string; - struct utf8_data *prompt_buffer; - size_t prompt_index; - int (*prompt_callbackfn)(void *, const char *); - void (*prompt_freefn)(void *); - void *prompt_data; - u_int prompt_hindex; - enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; - -#define PROMPT_SINGLE 0x1 -#define PROMPT_NUMERIC 0x2 - int prompt_flags; - - struct session *session; - struct session *last_session; - - int wlmouse; - - struct cmd_q *cmdq; - int references; - - TAILQ_ENTRY(client) entry; -}; -TAILQ_HEAD(clients, client); - /* Parsed arguments structures. */ struct args_entry; RB_HEAD(args_tree, args_entry); @@ -1324,42 +1230,46 @@ enum cmd_retval { CMD_RETURN_STOP }; -/* Command queue entry. */ -struct cmd_q_item { - struct cmd_list *cmdlist; - - struct mouse_event mouse; - - TAILQ_ENTRY(cmd_q_item) qentry; +/* Command queue item type. */ +enum cmd_q_type { + CMD_Q_COMMAND, + CMD_Q_CALLBACK, }; -TAILQ_HEAD(cmd_q_items, cmd_q_item); -/* Command queue. */ +/* Command queue item. */ +typedef enum cmd_retval (*cmd_q_cb) (struct cmd_q *, void *); struct cmd_q { - int references; - int flags; -#define CMD_Q_DEAD 0x1 -#define CMD_Q_NOHOOKS 0x2 + struct cmd_q_list *queue; + struct cmd_q *next; struct client *client; - int client_exit; - struct cmd_q_items queue; - struct cmd_q_item *item; + enum cmd_q_type type; + u_int group; + + u_int number; + time_t time; + + const char *hook; + int flags; +#define CMD_Q_FIRED 0x1 +#define CMD_Q_WAITING 0x2 +#define CMD_Q_NOHOOKS 0x4 + + struct cmd_list *cmdlist; struct cmd *cmd; - struct cmd_q *parent; + + cmd_q_cb cb; + void *data; struct cmd_find_state current; struct cmd_state state; - time_t time; - u_int number; + struct mouse_event mouse; - void (*emptyfn)(struct cmd_q *); - void *data; - - TAILQ_ENTRY(cmd_q) waitentry; + TAILQ_ENTRY(cmd_q) entry; }; +TAILQ_HEAD(cmd_q_list, cmd_q); /* Command -c, -t or -s flags. */ enum cmd_entry_flag { @@ -1409,6 +1319,101 @@ struct cmd_entry { enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; +/* Client connection. */ +struct client { + struct tmuxpeer *peer; + struct cmd_q_list queue; + + pid_t pid; + int fd; + struct event event; + int retval; + + struct timeval creation_time; + struct timeval activity_time; + + struct environ *environ; + + char *title; + const char *cwd; + + char *term; + char *ttyname; + struct tty tty; + + void (*stdin_callback)(struct client *, int, void *); + void *stdin_callback_data; + struct evbuffer *stdin_data; + int stdin_closed; + struct evbuffer *stdout_data; + struct evbuffer *stderr_data; + + struct event repeat_timer; + + struct event click_timer; + u_int click_button; + + struct event status_timer; + struct screen status; + +#define CLIENT_TERMINAL 0x1 +#define CLIENT_LOGIN 0x2 +#define CLIENT_EXIT 0x4 +#define CLIENT_REDRAW 0x8 +#define CLIENT_STATUS 0x10 +#define CLIENT_REPEAT 0x20 +#define CLIENT_SUSPENDED 0x40 +#define CLIENT_ATTACHED 0x80 +#define CLIENT_IDENTIFY 0x100 +#define CLIENT_DEAD 0x200 +#define CLIENT_BORDERS 0x400 +#define CLIENT_READONLY 0x800 +#define CLIENT_REDRAWWINDOW 0x1000 +#define CLIENT_CONTROL 0x2000 +#define CLIENT_CONTROLCONTROL 0x4000 +#define CLIENT_FOCUSED 0x8000 +#define CLIENT_UTF8 0x10000 +#define CLIENT_256COLOURS 0x20000 +#define CLIENT_IDENTIFIED 0x40000 +#define CLIENT_STATUSFORCE 0x80000 +#define CLIENT_DOUBLECLICK 0x100000 +#define CLIENT_TRIPLECLICK 0x200000 + int flags; + struct key_table *keytable; + + struct event identify_timer; + void (*identify_callback)(struct client *, struct window_pane *); + void *identify_callback_data; + + char *message_string; + struct event message_timer; + u_int message_next; + TAILQ_HEAD(, message_entry) message_log; + + char *prompt_string; + struct utf8_data *prompt_buffer; + size_t prompt_index; + int (*prompt_callbackfn)(void *, const char *); + void (*prompt_freefn)(void *); + void *prompt_data; + u_int prompt_hindex; + enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; + +#define PROMPT_SINGLE 0x1 +#define PROMPT_NUMERIC 0x2 + int prompt_flags; + + struct session *session; + struct session *last_session; + + int wlmouse; + + int references; + + TAILQ_ENTRY(client) entry; +}; +TAILQ_HEAD(clients, client); + /* Key binding and key table. */ struct key_binding { key_code key; @@ -1504,10 +1509,9 @@ void proc_kill_peer(struct tmuxpeer *); /* cfg.c */ extern int cfg_finished; -extern int cfg_references; extern struct client *cfg_client; void start_cfg(void); -int load_cfg(const char *, struct cmd_q *, int); +int load_cfg(const char *, struct client *, struct cmd_q *, int); void set_cfg_file(const char *); void printflike(1, 2) cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmd_q *); @@ -1557,9 +1561,9 @@ void hooks_add(struct hooks *, const char *, struct cmd_list *); void hooks_copy(struct hooks *, struct hooks *); void hooks_remove(struct hooks *, const char *); struct hook *hooks_find(struct hooks *, const char *); -int printflike(4, 5) hooks_run(struct hooks *, struct client *, +void printflike(4, 5) hooks_run(struct hooks *, struct client *, struct cmd_find_state *, const char *, ...); -int printflike(4, 5) hooks_wait(struct hooks *, struct cmd_q *, +void printflike(4, 5) hooks_insert(struct hooks *, struct cmd_q *, struct cmd_find_state *, const char *, ...); /* mode-key.c */ @@ -1755,8 +1759,7 @@ char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); -int cmd_prepare_state(struct cmd *, struct cmd_q *, - struct cmd_q *); +int cmd_prepare_state(struct cmd *, struct cmd_q *); char *cmd_print(struct cmd *); int cmd_mouse_at(struct window_pane *, struct mouse_event *, u_int *, u_int *, int); @@ -1776,16 +1779,15 @@ void cmd_list_free(struct cmd_list *); char *cmd_list_print(struct cmd_list *); /* cmd-queue.c */ -struct cmd_q *cmdq_new(struct client *); -int cmdq_free(struct cmd_q *); +struct cmd_q *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, + struct mouse_event *, int); +struct cmd_q *cmdq_get_callback(cmd_q_cb, void *); +void cmdq_insert_after(struct cmd_q *, struct cmd_q *); +void cmdq_append(struct client *, struct cmd_q *); +u_int cmdq_next(struct client *); +void cmdq_guard(struct cmd_q *, const char *, int); void printflike(2, 3) cmdq_print(struct cmd_q *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmd_q *, const char *, ...); -void cmdq_guard(struct cmd_q *, const char *, int); -void cmdq_run(struct cmd_q *, struct cmd_list *, - struct mouse_event *); -void cmdq_append(struct cmd_q *, struct cmd_list *, - struct mouse_event *); -int cmdq_continue(struct cmd_q *); /* cmd-string.c */ int cmd_string_parse(const char *, struct cmd_list **, const char *, diff --git a/window-choose.c b/window-choose.c index 25dc77d1..b7720e8d 100644 --- a/window-choose.c +++ b/window-choose.c @@ -246,6 +246,7 @@ window_choose_data_run(struct window_choose_data *cdata) { struct cmd_list *cmdlist; char *cause; + struct cmd_q *cmdq; /* * The command template will have already been replaced. But if it's @@ -263,7 +264,8 @@ window_choose_data_run(struct window_choose_data *cdata) return; } - cmdq_run(cdata->start_client->cmdq, cmdlist, NULL); + cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0); + cmdq_append(cdata->start_client, cmdq); cmd_list_free(cmdlist); }