diff options
author | nicm <nicm> | 2016-10-16 17:55:14 +0000 |
---|---|---|
committer | nicm <nicm> | 2016-10-16 17:55:14 +0000 |
commit | ddc4512d2e0eda6c705e002cb5dbf80719d709e1 (patch) | |
tree | cd9ff99f4b1ccb3e76ab99633d59772ae07c3092 | |
parent | bfe14b531267f3762a518f98b72a3148a134396a (diff) |
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.
-rw-r--r-- | cfg.c | 70 | ||||
-rw-r--r-- | cmd-attach-session.c | 2 | ||||
-rw-r--r-- | cmd-command-prompt.c | 30 | ||||
-rw-r--r-- | cmd-confirm-before.c | 27 | ||||
-rw-r--r-- | cmd-copy-mode.c | 4 | ||||
-rw-r--r-- | cmd-display-panes.c | 50 | ||||
-rw-r--r-- | cmd-find.c | 2 | ||||
-rw-r--r-- | cmd-if-shell.c | 112 | ||||
-rw-r--r-- | cmd-load-buffer.c | 45 | ||||
-rw-r--r-- | cmd-new-session.c | 6 | ||||
-rw-r--r-- | cmd-new-window.c | 4 | ||||
-rw-r--r-- | cmd-queue.c | 489 | ||||
-rw-r--r-- | cmd-resize-pane.c | 4 | ||||
-rw-r--r-- | cmd-run-shell.c | 28 | ||||
-rw-r--r-- | cmd-send-keys.c | 2 | ||||
-rw-r--r-- | cmd-source-file.c | 54 | ||||
-rw-r--r-- | cmd-split-window.c | 4 | ||||
-rw-r--r-- | cmd-wait-for.c | 69 | ||||
-rw-r--r-- | cmd.c | 51 | ||||
-rw-r--r-- | control.c | 29 | ||||
-rw-r--r-- | format.c | 9 | ||||
-rw-r--r-- | hooks.c | 64 | ||||
-rw-r--r-- | key-bindings.c | 22 | ||||
-rw-r--r-- | notify.c | 12 | ||||
-rw-r--r-- | server-client.c | 44 | ||||
-rw-r--r-- | server.c | 10 | ||||
-rw-r--r-- | tmux.1 | 4 | ||||
-rw-r--r-- | tmux.h | 268 | ||||
-rw-r--r-- | window-choose.c | 4 |
29 files changed, 817 insertions, 702 deletions
@@ -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 (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 (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) { + 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; @@ -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; - - if (cmd_string_parse(cmd, &cmdlist, cdata->file, cdata->line, - &cause) != 0) { - if (cause != NULL) { - cmdq_error(cmdq, "%s", cause); - free(cause); - } - return; + goto out; + + 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; - - 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); + if (new_cmdq != NULL) { + if (cdata->cmdq == NULL) + cmdq_append(c, new_cmdq); + else + cmdq_insert_after(cdata->cmdq, new_cmdq); + } - 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; + 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); +} - cmdq = xcalloc(1, sizeof *cmdq); - cmdq->references = 1; - cmdq->flags = 0; +/* 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; - cmdq->client_exit = -1; + if (retval == CMD_RETURN_ERROR) + cmdq_guard(cmdq, "error", flags); + else + cmdq_guard(cmdq, "end", flags); + return (retval); +} - TAILQ_INIT(&cmdq->queue); - cmdq->item = NULL; - cmdq->cmd = NULL; +/* 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->type = CMD_Q_CALLBACK; + cmdq->group = 0; + cmdq->flags = 0; - 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)); +} + +/* 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 (--cmdq->references != 0) { - if (cmdq->flags & CMD_Q_DEAD) - return (1); + 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< |