diff --git a/cfg.c b/cfg.c index 5e3e47e9..1153de67 100644 --- a/cfg.c +++ b/cfg.c @@ -27,79 +27,33 @@ #include "tmux.h" -/* - * Config file parser. Pretty quick and simple, each line is parsed into a - * argv array and executed as a command. - */ - -void printflike2 cfg_print(struct cmd_ctx *, const char *, ...); -void printflike2 cfg_error(struct cmd_ctx *, const char *, ...); - -char *cfg_cause; +struct cmd_q *cfg_cmd_q; int cfg_finished; -int cfg_references; +int cfg_references; struct causelist cfg_causes; -/* ARGSUSED */ -void printflike2 -cfg_print(unused struct cmd_ctx *ctx, unused const char *fmt, ...) -{ -} - -/* ARGSUSED */ -void printflike2 -cfg_error(unused struct cmd_ctx *ctx, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - xvasprintf(&cfg_cause, fmt, ap); - va_end(ap); -} - -void printflike2 -cfg_add_cause(struct causelist *causes, const char *fmt, ...) -{ - char *cause; - va_list ap; - - va_start(ap, fmt); - xvasprintf(&cause, fmt, ap); - va_end(ap); - - ARRAY_ADD(causes, cause); -} - -/* - * Load configuration file. Returns -1 for an error with a list of messages in - * causes. Note that causes must be initialised by the caller! - */ -enum cmd_retval -load_cfg(const char *path, struct cmd_ctx *ctxin, struct causelist *causes) +int +load_cfg(const char *path, struct cmd_q *cmdq, char **cause) { FILE *f; - u_int n; - char *buf, *copy, *line, *cause; + u_int n, found; + char *buf, *copy, *line, *cause1, *msg; size_t len, oldlen; struct cmd_list *cmdlist; - struct cmd_ctx ctx; - enum cmd_retval retval; + log_debug("loading %s", path); if ((f = fopen(path, "rb")) == NULL) { - cfg_add_cause(causes, "%s: %s", path, strerror(errno)); - return (CMD_RETURN_ERROR); + xasprintf(cause, "%s: %s", path, strerror(errno)); + return (-1); } - cfg_references++; - - n = 0; + n = found = 0; line = NULL; - retval = CMD_RETURN_NORMAL; while ((buf = fgetln(f, &len))) { /* Trim \n. */ if (buf[len - 1] == '\n') len--; - log_debug ("%s: %s", path, buf); + log_debug("%s: %.*s", path, (int)len, buf); /* Current line is the continuation of the previous one. */ if (line != NULL) { @@ -131,68 +85,52 @@ load_cfg(const char *path, struct cmd_ctx *ctxin, struct causelist *causes) buf = copy; while (isspace((u_char)*buf)) buf++; - if (*buf == '\0') - continue; - - if (cmd_string_parse(buf, &cmdlist, &cause) != 0) { + if (*buf == '\0') { free(copy); - if (cause == NULL) + continue; + } + + /* Parse and run the command. */ + if (cmd_string_parse(buf, &cmdlist, path, n, &cause1) != 0) { + free(copy); + if (cause1 == NULL) continue; - cfg_add_cause(causes, "%s: %u: %s", path, n, cause); - free(cause); + xasprintf(&msg, "%s:%u: %s", path, n, cause1); + ARRAY_ADD(&cfg_causes, msg); + free(cause1); continue; } free(copy); + if (cmdlist == NULL) continue; - - if (ctxin == NULL) { - ctx.msgdata = NULL; - ctx.curclient = NULL; - ctx.cmdclient = NULL; - } else { - ctx.msgdata = ctxin->msgdata; - ctx.curclient = ctxin->curclient; - ctx.cmdclient = ctxin->cmdclient; - } - - ctx.error = cfg_error; - ctx.print = cfg_print; - ctx.info = cfg_print; - - cfg_cause = NULL; - switch (cmd_list_exec(cmdlist, &ctx)) { - case CMD_RETURN_YIELD: - if (retval != CMD_RETURN_ATTACH) - retval = CMD_RETURN_YIELD; - break; - case CMD_RETURN_ATTACH: - retval = CMD_RETURN_ATTACH; - break; - case CMD_RETURN_ERROR: - case CMD_RETURN_NORMAL: - break; - } + cmdq_append(cmdq, cmdlist); cmd_list_free(cmdlist); - if (cfg_cause != NULL) { - cfg_add_cause(causes, "%s: %d: %s", path, n, cfg_cause); - free(cfg_cause); - } + found++; } - if (line != NULL) { - cfg_add_cause(causes, - "%s: %d: line continuation at end of file", path, n); + if (line != NULL) free(line); - } fclose(f); - cfg_references--; - - return (retval); + return (found); } void -show_cfg_causes(struct session *s) +cfg_default_done(unused struct cmd_q *cmdq) +{ + if (--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; +} + +void +cfg_show_causes(struct session *s) { struct window_pane *wp; char *cause; @@ -200,7 +138,6 @@ show_cfg_causes(struct session *s) if (s == NULL || ARRAY_EMPTY(&cfg_causes)) return; - wp = s->curw->window->active; window_pane_set_mode(wp, &window_copy_mode); diff --git a/client.c b/client.c index ae4f5066..91f47650 100644 --- a/client.c +++ b/client.c @@ -188,7 +188,8 @@ client_main(int argc, char **argv, int flags) * later in server) but it is necessary to get the start server * flag. */ - if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) { + cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause); + if (cmdlist == NULL) { fprintf(stderr, "%s\n", cause); return (1); } @@ -269,7 +270,7 @@ client_main(int argc, char **argv, int flags) if (msg == MSG_COMMAND) { /* Fill in command line arguments. */ cmddata.pid = environ_pid; - cmddata.idx = environ_idx; + cmddata.session_id = environ_session_id; /* Prepare command for server. */ cmddata.argc = argc; @@ -295,8 +296,16 @@ client_main(int argc, char **argv, int flags) ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); - } else if (flags & IDENTIFY_TERMIOS) + } else if (flags & IDENTIFY_TERMIOS) { + if (flags & IDENTIFY_CONTROL) { + if (client_exitreason != CLIENT_EXIT_NONE) + printf("%%exit %s\n", client_exit_message()); + else + printf("%%exit\n"); + printf("\033\\"); + } tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); + } setblocking(STDIN_FILENO, 1); return (client_exitval); } @@ -364,7 +373,6 @@ client_update_event(void) } /* Callback to handle signals in the client. */ -/* ARGSUSED */ void client_signal(int sig, unused short events, unused void *data) { @@ -411,7 +419,6 @@ client_signal(int sig, unused short events, unused void *data) } /* Callback for client imsg read events. */ -/* ARGSUSED */ void client_callback(unused int fd, short events, void *data) { @@ -446,7 +453,6 @@ lost_server: } /* Callback for client stdin read events. */ -/* ARGSUSED */ void client_stdin_callback(unused int fd, unused short events, unused void *data1) { @@ -518,6 +524,7 @@ client_dispatch_wait(void *data) event_del(&client_stdin); client_attached = 1; + client_write_server(MSG_RESIZE, NULL, 0); break; case MSG_STDIN: if (datalen != 0) @@ -575,7 +582,6 @@ client_dispatch_wait(void *data) } /* Dispatch imsgs in attached state (after MSG_READY). */ -/* ARGSUSED */ int client_dispatch_attached(void) { diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 18a20244..07185737 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -26,7 +26,7 @@ * Attach existing session to the current terminal. */ -enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", @@ -39,9 +39,8 @@ const struct cmd_entry cmd_attach_session_entry = { }; enum cmd_retval -cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag) { - struct args *args = self->args; struct session *s; struct client *c; const char *update; @@ -49,18 +48,18 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx) u_int i; if (RB_EMPTY(&sessions)) { - ctx->error(ctx, "no sessions"); + cmdq_error(cmdq, "no sessions"); return (CMD_RETURN_ERROR); } - if ((s = cmd_find_session(ctx, args_get(args, 't'), 1)) == NULL) + if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) return (CMD_RETURN_ERROR); - if (ctx->cmdclient == NULL && ctx->curclient == NULL) + if (cmdq->client == NULL) return (CMD_RETURN_NORMAL); - if (ctx->cmdclient == NULL) { - if (args_has(self->args, 'd')) { + if (cmdq->client->session != NULL) { + if (dflag) { /* * Can't use server_write_session in case attaching to * the same session as currently attached to. @@ -69,43 +68,53 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx) c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; - if (c == ctx->curclient) + if (c == cmdq->client) continue; server_write_client(c, MSG_DETACH, NULL, 0); } } - ctx->curclient->session = s; - notify_attached_session_changed(ctx->curclient); + cmdq->client->session = s; + notify_attached_session_changed(cmdq->client); session_update_activity(s); - server_redraw_client(ctx->curclient); + server_redraw_client(cmdq->client); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { - if (server_client_open(ctx->cmdclient, s, &cause) != 0) { - ctx->error(ctx, "open terminal failed: %s", cause); + if (server_client_open(cmdq->client, s, &cause) != 0) { + cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } - if (args_has(self->args, 'r')) - ctx->cmdclient->flags |= CLIENT_READONLY; + if (rflag) + cmdq->client->flags |= CLIENT_READONLY; - if (args_has(self->args, 'd')) + if (dflag) server_write_session(s, MSG_DETACH, NULL, 0); - ctx->cmdclient->session = s; - notify_attached_session_changed(ctx->cmdclient); - session_update_activity(s); - server_write_ready(ctx->cmdclient); - update = options_get_string(&s->options, "update-environment"); - environ_update(update, &ctx->cmdclient->environ, &s->environ); + environ_update(update, &cmdq->client->environ, &s->environ); - server_redraw_client(ctx->cmdclient); + cmdq->client->session = s; + notify_attached_session_changed(cmdq->client); + session_update_activity(s); + server_redraw_client(cmdq->client); s->curw->flags &= ~WINLINK_ALERTFLAGS; + + server_write_ready(cmdq->client); + cmdq->client_exit = 0; } recalculate_sizes(); server_update_socket(); - return (CMD_RETURN_ATTACH); + return (CMD_RETURN_NORMAL); +} + +enum cmd_retval +cmd_attach_session_exec(struct cmd *self, struct cmd_q *cmdq) +{ + struct args *args = self->args; + + return (cmd_attach_session(cmdq, args_get(args, 't'), + args_has(args, 'd'), args_has(args, 'r'))); } diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 28e94e26..71e79ea0 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -28,9 +28,9 @@ */ enum cmd_retval cmd_bind_key_check(struct args *); -enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_ctx *, int); +enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_bind_key_entry = { "bind-key", "bind", @@ -46,7 +46,7 @@ enum cmd_retval cmd_bind_key_check(struct args *args) { if (args_has(args, 't')) { - if (args->argc != 2) + if (args->argc != 2 && args->argc != 3) return (CMD_RETURN_ERROR); } else { if (args->argc < 2) @@ -56,7 +56,7 @@ cmd_bind_key_check(struct args *args) } enum cmd_retval -cmd_bind_key_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; char *cause; @@ -65,16 +65,17 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_ctx *ctx) key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE) { - ctx->error(ctx, "unknown key: %s", args->argv[0]); + cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } if (args_has(args, 't')) - return (cmd_bind_key_table(self, ctx, key)); + return (cmd_bind_key_table(self, cmdq, key)); - cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, &cause); + cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0, + &cause); if (cmdlist == NULL) { - ctx->error(ctx, "%s", cause); + cmdq_error(cmdq, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } @@ -86,36 +87,50 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_ctx *ctx) } enum cmd_retval -cmd_bind_key_table(struct cmd *self, struct cmd_ctx *ctx, int key) +cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) { struct args *args = self->args; const char *tablename; const struct mode_key_table *mtab; struct mode_key_binding *mbind, mtmp; enum mode_key_cmd cmd; + const char *arg; tablename = args_get(args, 't'); if ((mtab = mode_key_findtable(tablename)) == NULL) { - ctx->error(ctx, "unknown key table: %s", tablename); + cmdq_error(cmdq, "unknown key table: %s", tablename); return (CMD_RETURN_ERROR); } cmd = mode_key_fromstring(mtab->cmdstr, args->argv[1]); if (cmd == MODEKEY_NONE) { - ctx->error(ctx, "unknown command: %s", args->argv[1]); + cmdq_error(cmdq, "unknown command: %s", args->argv[1]); return (CMD_RETURN_ERROR); } + if (cmd != MODEKEYCOPY_COPYPIPE) { + if (args->argc != 2) { + cmdq_error(cmdq, "no argument allowed"); + return (CMD_RETURN_ERROR); + } + arg = NULL; + } else { + if (args->argc != 3) { + cmdq_error(cmdq, "no argument given"); + return (CMD_RETURN_ERROR); + } + arg = args->argv[2]; + } + mtmp.key = key; mtmp.mode = !!args_has(args, 'c'); - if ((mbind = RB_FIND(mode_key_tree, mtab->tree, &mtmp)) != NULL) { - mbind->cmd = cmd; - return (CMD_RETURN_NORMAL); + if ((mbind = RB_FIND(mode_key_tree, mtab->tree, &mtmp)) == NULL) { + mbind = xmalloc(sizeof *mbind); + mbind->key = mtmp.key; + mbind->mode = mtmp.mode; + RB_INSERT(mode_key_tree, mtab->tree, mbind); } - mbind = xmalloc(sizeof *mbind); - mbind->key = mtmp.key; - mbind->mode = mtmp.mode; mbind->cmd = cmd; - RB_INSERT(mode_key_tree, mtab->tree, mbind); + mbind->arg = arg != NULL ? xstrdup(arg) : NULL; return (CMD_RETURN_NORMAL); } diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 637105ab..8ed9a1a6 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -26,7 +26,7 @@ * Break pane off into a window. */ -enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_break_pane_entry = { "break-pane", "breakp", @@ -39,7 +39,7 @@ const struct cmd_entry cmd_break_pane_entry = { }; enum cmd_retval -cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; @@ -54,15 +54,17 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx) const char *template; char *cp; - if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); if (window_count_panes(wl->window) == 1) { - ctx->error(ctx, "can't break with only one pane"); + cmdq_error(cmdq, "can't break with only one pane"); return (CMD_RETURN_ERROR); } w = wl->window; + server_unzoom_window(w); + TAILQ_REMOVE(&w->panes, wp, entry); if (wp == w->active) { w->active = w->last; @@ -82,7 +84,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx) name = default_window_name(w); window_set_name(w, name); free(name); - layout_init(w); + layout_init(w, wp); base_idx = options_get_number(&s->options, "base-index"); wl = session_attach(s, w, -1 - base_idx, &cause); /* can't fail */ @@ -93,19 +95,18 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx) server_status_session_group(s); if (args_has(args, 'P')) { - if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; ft = format_create(); - if ((c = cmd_find_client(ctx, NULL)) != NULL) + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) format_client(ft, c); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, wp); cp = format_expand(ft, template); - ctx->print(ctx, "%s", cp); + cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index adb827bb..f59dc2d6 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -24,15 +24,21 @@ #include "tmux.h" /* - * Write the entire contents of a pane to a buffer. + * Write the entire contents of a pane to a buffer or stdout. */ -enum cmd_retval cmd_capture_pane_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_capture_pane_exec(struct cmd *, struct cmd_q *); + +char *cmd_capture_pane_append(char *, size_t *, char *, size_t); +char *cmd_capture_pane_pending(struct args *, struct window_pane *, + size_t *); +char *cmd_capture_pane_history(struct args *, struct cmd_q *, + struct window_pane *, size_t *); const struct cmd_entry cmd_capture_pane_entry = { "capture-pane", "capturep", - "b:E:S:t:", 0, 0, - "[-b buffer-index] [-E end-line] [-S start-line] " + "ab:CeE:JpPqS:t:", 0, 0, + "[-aCeJpPq] [-b buffer-index] [-E end-line] [-S start-line]" CMD_TARGET_PANE_USAGE, 0, NULL, @@ -40,25 +46,69 @@ const struct cmd_entry cmd_capture_pane_entry = { cmd_capture_pane_exec }; -enum cmd_retval -cmd_capture_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +char * +cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen) +{ + buf = xrealloc(buf, 1, *len + linelen + 1); + memcpy(buf + *len, line, linelen); + *len += linelen; + return (buf); +} + +char * +cmd_capture_pane_pending(struct args *args, struct window_pane *wp, + size_t *len) +{ + char *buf, *line, tmp[5]; + size_t linelen; + u_int i; + + if (wp->ictx.since_ground == NULL) + return (xstrdup("")); + + line = EVBUFFER_DATA(wp->ictx.since_ground); + linelen = EVBUFFER_LENGTH(wp->ictx.since_ground); + + buf = xstrdup(""); + if (args_has(args, 'C')) { + for (i = 0; i < linelen; i++) { + if (line[i] >= ' ') { + tmp[0] = line[i]; + tmp[1] = '\0'; + } else + xsnprintf(tmp, sizeof tmp, "\\%03o", line[i]); + buf = cmd_capture_pane_append(buf, len, tmp, + strlen(tmp)); + } + } else + buf = cmd_capture_pane_append(buf, len, line, linelen); + return (buf); +} + +char * +cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq, + struct window_pane *wp, size_t *len) { - struct args *args = self->args; - struct window_pane *wp; - char *buf, *line, *cause; - struct screen *s; struct grid *gd; - int buffer, n; - u_int i, limit, top, bottom, tmp; - size_t len, linelen; + const struct grid_line *gl; + struct grid_cell *gc = NULL; + int n, with_codes, escape_c0, join_lines; + u_int i, sx, top, bottom, tmp; + char *cause, *buf, *line; + size_t linelen; - if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); - s = &wp->base; - gd = s->grid; - - buf = NULL; - len = 0; + sx = screen_size_x(&wp->base); + if (args_has(args, 'a')) { + gd = wp->saved_grid; + if (gd == NULL) { + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "no alternate screen"); + return (NULL); + } + return (xstrdup("")); + } + } else + gd = wp->base.grid; n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); if (cause != NULL) { @@ -88,37 +138,80 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_ctx *ctx) top = tmp; } + with_codes = args_has(args, 'e'); + escape_c0 = args_has(args, 'C'); + join_lines = args_has(args, 'J'); + + buf = NULL; for (i = top; i <= bottom; i++) { - line = grid_string_cells(s->grid, 0, i, screen_size_x(s)); - linelen = strlen(line); + line = grid_string_cells(gd, 0, i, sx, &gc, with_codes, + escape_c0, !join_lines); + linelen = strlen(line); - buf = xrealloc(buf, 1, len + linelen + 1); - memcpy(buf + len, line, linelen); - len += linelen; - buf[len++] = '\n'; + buf = cmd_capture_pane_append(buf, len, line, linelen); - free(line); + gl = grid_peek_line(gd, i); + if (!join_lines || !(gl->flags & GRID_LINE_WRAPPED)) + buf[(*len)++] = '\n'; + + free(line); } + return (buf); +} - limit = options_get_number(&global_options, "buffer-limit"); +enum cmd_retval +cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) +{ + struct args *args = self->args; + struct client *c; + struct window_pane *wp; + char *buf, *cause; + int buffer; + u_int limit; + size_t len; - if (!args_has(args, 'b')) { - paste_add(&global_buffers, buf, len, limit); - return (CMD_RETURN_NORMAL); - } - - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - ctx->error(ctx, "buffer %s", cause); - free(buf); - free(cause); + if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); - } - if (paste_replace(&global_buffers, buffer, buf, len) != 0) { - ctx->error(ctx, "no buffer %d", buffer); - free(buf); + len = 0; + if (args_has(args, 'P')) + buf = cmd_capture_pane_pending(args, wp, &len); + else + buf = cmd_capture_pane_history(args, cmdq, wp, &len); + if (buf == NULL) return (CMD_RETURN_ERROR); + + if (args_has(args, 'p')) { + c = cmdq->client; + if (c == NULL || + (c->session != NULL && !(c->flags & CLIENT_CONTROL))) { + cmdq_error(cmdq, "can't write to stdout"); + return (CMD_RETURN_ERROR); + } + evbuffer_add(c->stdout_data, buf, len); + if (args_has(args, 'P') && len > 0) + evbuffer_add(c->stdout_data, "\n", 1); + server_push_stdout(c); + } else { + limit = options_get_number(&global_options, "buffer-limit"); + if (!args_has(args, 'b')) { + paste_add(&global_buffers, buf, len, limit); + return (CMD_RETURN_NORMAL); + } + + buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); + if (cause != NULL) { + cmdq_error(cmdq, "buffer %s", cause); + free(buf); + free(cause); + return (CMD_RETURN_ERROR); + } + + if (paste_replace(&global_buffers, buffer, buf, len) != 0) { + cmdq_error(cmdq, "no buffer %d", buffer); + free(buf); + return (CMD_RETURN_ERROR); + } } return (CMD_RETURN_NORMAL); diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index 50505ea1..e6b79d91 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -27,10 +27,7 @@ * Enter choice mode to choose a buffer. */ -enum cmd_retval cmd_choose_buffer_exec(struct cmd *, struct cmd_ctx *); - -void cmd_choose_buffer_callback(struct window_choose_data *); -void cmd_choose_buffer_free(struct window_choose_data *); +enum cmd_retval cmd_choose_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_buffer_entry = { "choose-buffer", NULL, @@ -43,9 +40,10 @@ const struct cmd_entry cmd_choose_buffer_entry = { }; enum cmd_retval -cmd_choose_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct client *c; struct window_choose_data *cdata; struct winlink *wl; struct paste_buffer *pb; @@ -53,15 +51,15 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) const char *template; u_int idx; - if (ctx->curclient == NULL) { - ctx->error(ctx, "must be run interactively"); + if ((c = cmd_current_client(cmdq)) == NULL) { + cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } if ((template = args_get(args, 'F')) == NULL) template = CHOOSE_BUFFER_TEMPLATE; - if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); if (paste_get_top(&global_buffers) == NULL) @@ -77,9 +75,8 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) idx = 0; while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) { - cdata = window_choose_data_create(ctx); + cdata = window_choose_data_create(TREE_OTHER, c, c->session); cdata->idx = idx - 1; - cdata->client->references++; cdata->ft_template = xstrdup(template); format_add(cdata->ft, "line", "%u", idx - 1); @@ -93,34 +90,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) } free(action); - window_choose_ready(wl->window->active, - 0, cmd_choose_buffer_callback, cmd_choose_buffer_free); + window_choose_ready(wl->window->active, 0, NULL); return (CMD_RETURN_NORMAL); } - -void -cmd_choose_buffer_callback(struct window_choose_data *cdata) -{ - if (cdata == NULL) - return; - if (cdata->client->flags & CLIENT_DEAD) - return; - - window_choose_ctx(cdata); -} - -void -cmd_choose_buffer_free(struct window_choose_data *data) -{ - struct window_choose_data *cdata = data; - - if (cdata == NULL) - return; - - cdata->client->references--; - - free(cdata->command); - free(cdata->ft_template); - free(cdata); -} diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 75ef8f2c..40752a70 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -27,10 +27,9 @@ * Enter choice mode to choose a client. */ -enum cmd_retval cmd_choose_client_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_choose_client_exec(struct cmd *, struct cmd_q *); void cmd_choose_client_callback(struct window_choose_data *); -void cmd_choose_client_free(struct window_choose_data *); const struct cmd_entry cmd_choose_client_entry = { "choose-client", NULL, @@ -47,22 +46,23 @@ struct cmd_choose_client_data { }; enum cmd_retval -cmd_choose_client_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_choose_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct client *c; + struct client *c1; struct window_choose_data *cdata; struct winlink *wl; - struct client *c; const char *template; char *action; u_int i, idx, cur; - if (ctx->curclient == NULL) { - ctx->error(ctx, "must be run interactively"); + if ((c = cmd_current_client(cmdq)) == NULL) { + cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) @@ -78,30 +78,29 @@ cmd_choose_client_exec(struct cmd *self, struct cmd_ctx *ctx) cur = idx = 0; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) + c1 = ARRAY_ITEM(&clients, i); + if (c1 == NULL || c1->session == NULL || c1->tty.path == NULL) continue; - if (c == ctx->curclient) + if (c1 == cmdq->client) cur = idx; idx++; - cdata = window_choose_data_create(ctx); + cdata = window_choose_data_create(TREE_OTHER, c, c->session); cdata->idx = i; - cdata->client->references++; cdata->ft_template = xstrdup(template); format_add(cdata->ft, "line", "%u", i); - format_session(cdata->ft, c->session); - format_client(cdata->ft, c); + format_session(cdata->ft, c1->session); + format_client(cdata->ft, c1); - cdata->command = cmd_template_replace(action, c->tty.path, 1); + cdata->command = cmd_template_replace(action, c1->tty.path, 1); window_choose_add(wl->window->active, cdata); } free(action); - window_choose_ready(wl->window->active, - cur, cmd_choose_client_callback, cmd_choose_client_free); + window_choose_ready(wl->window->active, cur, + cmd_choose_client_callback); return (CMD_RETURN_NORMAL); } @@ -113,7 +112,7 @@ cmd_choose_client_callback(struct window_choose_data *cdata) if (cdata == NULL) return; - if (cdata->client->flags & CLIENT_DEAD) + if (cdata->start_client->flags & CLIENT_DEAD) return; if (cdata->idx > ARRAY_LENGTH(&clients) - 1) @@ -122,19 +121,5 @@ cmd_choose_client_callback(struct window_choose_data *cdata) if (c == NULL || c->session == NULL) return; - window_choose_ctx(cdata); -} - -void -cmd_choose_client_free(struct window_choose_data *cdata) -{ - if (cdata == NULL) - return; - - cdata->client->references--; - - free(cdata->ft_template); - free(cdata->command); - format_free(cdata->ft); - free(cdata); + window_choose_data_run(cdata); } diff --git a/cmd-choose-list.c b/cmd-choose-list.c index 4c32e694..15f87294 100644 --- a/cmd-choose-list.c +++ b/cmd-choose-list.c @@ -31,10 +31,7 @@ * Enter choose mode to choose a custom list. */ -enum cmd_retval cmd_choose_list_exec(struct cmd *, struct cmd_ctx *); - -void cmd_choose_list_callback(struct window_choose_data *); -void cmd_choose_list_free(struct window_choose_data *); +enum cmd_retval cmd_choose_list_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_list_entry = { "choose-list", NULL, @@ -47,23 +44,24 @@ const struct cmd_entry cmd_choose_list_entry = { }; enum cmd_retval -cmd_choose_list_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_choose_list_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct client *c; struct winlink *wl; const char *list1; char *template, *item, *copy, *list; u_int idx; - if (ctx->curclient == NULL) { - ctx->error(ctx, "must be run interactively"); + if ((c = cmd_current_client(cmdq)) == NULL) { + cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } if ((list1 = args_get(args, 'l')) == NULL) return (CMD_RETURN_ERROR); - if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) @@ -80,7 +78,7 @@ cmd_choose_list_exec(struct cmd *self, struct cmd_ctx *ctx) { if (*item == '\0') /* no empty entries */ continue; - window_choose_add_item(wl->window->active, ctx, wl, item, + window_choose_add_item(wl->window->active, c, wl, item, template, idx); idx++; } @@ -92,32 +90,9 @@ cmd_choose_list_exec(struct cmd *self, struct cmd_ctx *ctx) return (CMD_RETURN_ERROR); } - window_choose_ready(wl->window->active, 0, cmd_choose_list_callback, - cmd_choose_list_free); + window_choose_ready(wl->window->active, 0, NULL); free(template); return (CMD_RETURN_NORMAL); } - -void -cmd_choose_list_callback(struct window_choose_data *cdata) -{ - if (cdata == NULL || (cdata->client->flags & CLIENT_DEAD)) - return; - - window_choose_ctx(cdata); -} - -void -cmd_choose_list_free(struct window_choose_data *cdata) -{ - cdata->session->references--; - cdata->client->references--; - - free(cdata->ft_template); - free(cdata->command); - format_free(cdata->ft); - free(cdata); - -} diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index cf0b772a..e2d382b3 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -32,15 +32,12 @@ * Enter choice mode to choose a session and/or window. */ -enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmd_ctx *); - -void cmd_choose_tree_callback(struct window_choose_data *); -void cmd_choose_tree_free(struct window_choose_data *); +enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_tree_entry = { "choose-tree", NULL, "S:W:swub:c:t:", 0, 1, - "[-swu] [-b session-template] [-c window template] [-S format] " \ + "[-suw] [-b session-template] [-c window template] [-S format] " \ "[-W format] " CMD_TARGET_WINDOW_USAGE, 0, NULL, @@ -69,11 +66,12 @@ const struct cmd_entry cmd_choose_window_entry = { }; enum cmd_retval -cmd_choose_tree_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl, *wm; struct session *s, *s2; + struct client *c; struct window_choose_data *wcd = NULL; const char *ses_template, *win_template; char *final_win_action, *cur_win_template; @@ -86,14 +84,15 @@ cmd_choose_tree_exec(struct cmd *self, struct cmd_ctx *ctx) ses_template = win_template = NULL; ses_action = win_action = NULL; - if (ctx->curclient == NULL) { - ctx->error(ctx, "must be run interactively"); + if ((c = cmd_current_client(cmdq)) == NULL) { + cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - s = ctx->curclient->session; + if ((s = c->session) == NULL) + return (CMD_RETURN_ERROR); - if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) @@ -175,7 +174,7 @@ cmd_choose_tree_exec(struct cmd *self, struct cmd_ctx *ctx) } wcd = window_choose_add_session(wl->window->active, - ctx, s2, ses_template, (char *)ses_action, idx_ses); + c, s2, ses_template, ses_action, idx_ses); /* If we're just choosing sessions, skip choosing windows. */ if (sflag && !wflag) { @@ -204,8 +203,9 @@ windows_only: cur_win = idx_ses; } - xasprintf(&final_win_action, "%s ; %s", win_action, - wcd ? wcd->command : ""); + xasprintf(&final_win_action, "%s %s %s", + wcd != NULL ? wcd->command : "", + wcd != NULL ? ";" : "", win_action); if (win_ses != win_max) cur_win_template = final_win_template_middle; @@ -213,7 +213,7 @@ windows_only: cur_win_template = final_win_template_last; window_choose_add_window(wl->window->active, - ctx, s2, wm, cur_win_template, + c, s2, wm, cur_win_template, final_win_action, (wflag && !sflag) ? win_ses : idx_ses); @@ -230,35 +230,10 @@ windows_only: free(final_win_template_middle); free(final_win_template_last); - window_choose_ready(wl->window->active, cur_win, - cmd_choose_tree_callback, cmd_choose_tree_free); + window_choose_ready(wl->window->active, cur_win, NULL); if (args_has(args, 'u')) window_choose_expand_all(wl->window->active); return (CMD_RETURN_NORMAL); } - -void -cmd_choose_tree_callback(struct window_choose_data *cdata) -{ - if (cdata == NULL) - return; - - if (cdata->client->flags & CLIENT_DEAD) - return; - - window_choose_ctx(cdata); -} - -void -cmd_choose_tree_free(struct window_choose_data *cdata) -{ - cdata->session->references--; - cdata->client->references--; - - free(cdata->ft_template); - free(cdata->command); - format_free(cdata->ft); - free(cdata); -} diff --git a/cmd-clear-history.c b/cmd-clear-history.c index 27f73dbb..aebaa27d 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -24,7 +24,7 @@ * Clear pane history. */ -enum cmd_retval cmd_clear_history_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_clear_history_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_clear_history_entry = { "clear-history", "clearhist", @@ -37,13 +37,13 @@ const struct cmd_entry cmd_clear_history_entry = { }; enum cmd_retval -cmd_clear_history_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_clear_history_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp; struct grid *gd; - if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) + if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); gd = wp->base.grid; diff --git a/cmd-clock-mode.c b/cmd-clock-mode.c index 139e7157..872f3d53 100644 --- a/cmd-clock-mode.c +++ b/cmd-clock-mode.c @@ -24,7 +24,7 @@ * Enter clock mode. */ -enum cmd_retval cmd_clock_mode_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_clock_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_clock_mode_entry = { "clock-mode", NULL, @@ -37,12 +37,12 @@ const struct cmd_entry cmd_clock_mode_entry = { }; enum cmd_retval -cmd_clock_mode_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_clock_mode_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp; - if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) + if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); window_pane_set_mode(wp, &window_clock_mode); diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index ea380353..3bb79ed9 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -31,7 +31,7 @@ void cmd_command_prompt_key_binding(struct cmd *, int); int cmd_command_prompt_check(struct args *); -enum cmd_retval cmd_command_prompt_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_command_prompt_exec(struct cmd *, struct cmd_q *); int cmd_command_prompt_callback(void *, const char *); void cmd_command_prompt_free(void *); @@ -85,7 +85,7 @@ cmd_command_prompt_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_command_prompt_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; const char *inputs, *prompts; @@ -94,7 +94,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx) char *prompt, *ptr, *input = NULL; size_t n; - if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); if (c->prompt_string != NULL) @@ -150,7 +150,6 @@ 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_ctx ctx; char *cause, *new_template, *prompt, *ptr; char *input = NULL; @@ -175,7 +174,7 @@ cmd_command_prompt_callback(void *data, const char *s) return (1); } - if (cmd_string_parse(new_template, &cmdlist, &cause) != 0) { + 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); @@ -184,16 +183,7 @@ cmd_command_prompt_callback(void *data, const char *s) return (0); } - ctx.msgdata = NULL; - ctx.curclient = c; - - ctx.error = key_bindings_error; - ctx.print = key_bindings_print; - ctx.info = key_bindings_info; - - ctx.cmdclient = NULL; - - cmd_list_exec(cmdlist, &ctx); + cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); if (c->prompt_callbackfn != (void *) &cmd_command_prompt_callback) diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 329027cc..e670f69c 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -27,7 +27,7 @@ */ void cmd_confirm_before_key_binding(struct cmd *, int); -enum cmd_retval cmd_confirm_before_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_confirm_before_exec(struct cmd *, struct cmd_q *); int cmd_confirm_before_callback(void *, const char *); void cmd_confirm_before_free(void *); @@ -43,8 +43,8 @@ const struct cmd_entry cmd_confirm_before_entry = { }; struct cmd_confirm_before_data { - struct client *c; char *cmd; + struct client *client; }; void @@ -66,7 +66,7 @@ cmd_confirm_before_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_confirm_before_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_confirm_before_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_confirm_before_data *cdata; @@ -74,12 +74,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmd_ctx *ctx) char *cmd, *copy, *new_prompt, *ptr; const char *prompt; - if (ctx->curclient == NULL) { - ctx->error(ctx, "must be run interactively"); - return (CMD_RETURN_ERROR); - } - - if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); if ((prompt = args_get(args, 'p')) != NULL) @@ -93,48 +88,43 @@ cmd_confirm_before_exec(struct cmd *self, struct cmd_ctx *ctx) cdata = xmalloc(sizeof *cdata); cdata->cmd = xstrdup(args->argv[0]); - cdata->c = c; - status_prompt_set(cdata->c, new_prompt, NULL, + + cdata->client = c; + cdata->client->references++; + + status_prompt_set(c, new_prompt, NULL, cmd_confirm_before_callback, cmd_confirm_before_free, cdata, PROMPT_SINGLE); free(new_prompt); - return (CMD_RETURN_YIELD); + return (CMD_RETURN_NORMAL); } int cmd_confirm_before_callback(void *data, const char *s) { struct cmd_confirm_before_data *cdata = data; - struct client *c = cdata->c; + struct client *c = cdata->client; struct cmd_list *cmdlist; - struct cmd_ctx ctx; char *cause; + if (c->flags & CLIENT_DEAD) + return (0); + if (s == NULL || *s == '\0') return (0); if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') return (0); - if (cmd_string_parse(cdata->cmd, &cmdlist, &cause) != 0) { + if (cmd_string_parse(cdata->cmd, &cmdlist, NULL, 0, &cause) != 0) { if (cause != NULL) { - *cause = toupper((u_char) *cause); - status_message_set(c, "%s", cause); + cmdq_error(c->cmdq, "%s", cause); free(cause); } return (0); } - ctx.msgdata = NULL; - ctx.curclient = c; - - ctx.error = key_bindings_error; - ctx.print = key_bindings_print; - ctx.info = key_bindings_info; - - ctx.cmdclient = NULL; - - cmd_list_exec(cmdlist, &ctx); + cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); return (0); @@ -144,6 +134,9 @@ void cmd_confirm_before_free(void *data) { struct cmd_confirm_before_data *cdata = data; + struct client *c = cdata->client; + + c->references--; free(cdata->cmd); free(cdata); diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 8a5b5fc4..f014be83 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -25,7 +25,7 @@ */ void cmd_copy_mode_key_binding(struct cmd *, int); -enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { "copy-mode", NULL, @@ -46,12 +46,12 @@ cmd_copy_mode_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_copy_mode_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp; - if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) + if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); if (window_pane_set_mode(wp, &window_copy_mode) != 0) diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c index f9d2587c..bc3982ca 100644 --- a/cmd-delete-buffer.c +++ b/cmd-delete-buffer.c @@ -26,7 +26,7 @@ * Delete a paste buffer. */ -enum cmd_retval cmd_delete_buffer_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_delete_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_delete_buffer_entry = { "delete-buffer", "deleteb", @@ -39,7 +39,7 @@ const struct cmd_entry cmd_delete_buffer_entry = { }; enum cmd_retval -cmd_delete_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; char *cause; @@ -52,13 +52,13 @@ cmd_delete_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { - ctx->error(ctx, "buffer %s", cause); + cmdq_error(cmdq, "buffer %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (paste_free_index(&global_buffers, buffer) != 0) { - ctx->error(ctx, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); } diff --git a/cmd-detach-client.c b/cmd-detach-client.c index f75b37a3..6e00e079 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -24,7 +24,7 @@ * Detach a client. */ -enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_detach_client_entry = { "detach-client", "detach", @@ -37,7 +37,7 @@ const struct cmd_entry cmd_detach_client_entry = { }; enum cmd_retval -cmd_detach_client_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c, *c2; @@ -51,7 +51,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_ctx *ctx) msgtype = MSG_DETACH; if (args_has(args, 's')) { - s = cmd_find_session(ctx, args_get(args, 's'), 0); + s = cmd_find_session(cmdq, args_get(args, 's'), 0); if (s == NULL) return (CMD_RETURN_ERROR); @@ -61,7 +61,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_ctx *ctx) server_write_client(c, msgtype, NULL, 0); } } else { - c = cmd_find_client(ctx, args_get(args, 't')); + c = cmd_find_client(cmdq, args_get(args, 't'), 0); if (c == NULL) return (CMD_RETURN_ERROR); @@ -76,5 +76,5 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_ctx *ctx) server_write_client(c, msgtype, NULL, 0); } - return (CMD_RETURN_NORMAL); + return (CMD_RETURN_STOP); } diff --git a/cmd-display-message.c b/cmd-display-message.c index 244c557c..485ccf08 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -27,7 +27,7 @@ * Displays a message in the status line. */ -enum cmd_retval cmd_display_message_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_display_message_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_message_entry = { "display-message", "display", @@ -41,7 +41,7 @@ const struct cmd_entry cmd_display_message_entry = { }; enum cmd_retval -cmd_display_message_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; @@ -55,24 +55,33 @@ cmd_display_message_exec(struct cmd *self, struct cmd_ctx *ctx) time_t t; size_t len; - if ((c = cmd_find_client(ctx, args_get(args, 'c'))) == NULL) - return (CMD_RETURN_ERROR); - if (args_has(args, 't')) { - wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp); + wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); if (wl == NULL) return (CMD_RETURN_ERROR); } else { - wl = cmd_find_pane(ctx, NULL, &s, &wp); + wl = cmd_find_pane(cmdq, NULL, &s, &wp); if (wl == NULL) return (CMD_RETURN_ERROR); } if (args_has(args, 'F') && args->argc != 0) { - ctx->error(ctx, "only one of -F or argument must be given"); + cmdq_error(cmdq, "only one of -F or argument must be given"); return (CMD_RETURN_ERROR); } + if (args_has(args, 'c')) { + c = cmd_find_client(cmdq, args_get(args, 'c'), 0); + if (c == NULL) + return (CMD_RETURN_ERROR); + } else { + c = cmd_current_client(cmdq); + if (c == NULL && !args_has(self->args, 'p')) { + cmdq_error(cmdq, "no client available"); + return (CMD_RETURN_ERROR); + } + } + template = args_get(args, 'F'); if (args->argc != 0) template = args->argv[0]; @@ -80,7 +89,8 @@ cmd_display_message_exec(struct cmd *self, struct cmd_ctx *ctx) template = DISPLAY_MESSAGE_TEMPLATE; ft = format_create(); - format_client(ft, c); + if (c != NULL) + format_client(ft, c); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, wp); @@ -91,11 +101,11 @@ cmd_display_message_exec(struct cmd *self, struct cmd_ctx *ctx) msg = format_expand(ft, out); if (args_has(self->args, 'p')) - ctx->print(ctx, "%s", msg); + cmdq_print(cmdq, "%s", msg); else status_message_set(c, "%s", msg); - free(msg); format_free(ft); + return (CMD_RETURN_NORMAL); } diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 2745ac59..4a8731a4 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -24,7 +24,7 @@ * Display panes on a client. */ -enum cmd_retval cmd_display_panes_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_display_panes_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_panes_entry = { "display-panes", "displayp", @@ -37,12 +37,12 @@ const struct cmd_entry cmd_display_panes_entry = { }; enum cmd_retval -cmd_display_panes_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_display_panes_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; - if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); server_set_identify(c); diff --git a/cmd-find-window.c b/cmd-find-window.c index 9a0a8a42..02f19307 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -28,10 +28,9 @@ * Find window containing text. */ -enum cmd_retval cmd_find_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_find_window_exec(struct cmd *, struct cmd_q *); void cmd_find_window_callback(struct window_choose_data *); -void cmd_find_window_free(struct window_choose_data *); /* Flags for determining matching behavior. */ #define CMD_FIND_WINDOW_BY_TITLE 0x1 @@ -128,9 +127,10 @@ cmd_find_window_match(struct cmd_find_window_data_list *find_list, } enum cmd_retval -cmd_find_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct client *c; struct window_choose_data *cdata; struct session *s; struct winlink *wl, *wm; @@ -139,13 +139,13 @@ cmd_find_window_exec(struct cmd *self, struct cmd_ctx *ctx) const char *template; u_int i, match_flags; - if (ctx->curclient == NULL) { - ctx->error(ctx, "must be run interactively"); + if ((c = cmd_current_client(cmdq)) == NULL) { + cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - s = ctx->curclient->session; + s = c->session; - if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); if ((template = args_get(args, 'F')) == NULL) @@ -162,7 +162,7 @@ cmd_find_window_exec(struct cmd *self, struct cmd_ctx *ctx) free(searchstr); if (ARRAY_LENGTH(&find_list) == 0) { - ctx->error(ctx, "no windows matching: %s", str); + cmdq_error(cmdq, "no windows matching: %s", str); ARRAY_FREE(&find_list); return (CMD_RETURN_ERROR); } @@ -180,9 +180,8 @@ cmd_find_window_exec(struct cmd *self, struct cmd_ctx *ctx) for (i = 0; i < ARRAY_LENGTH(&find_list); i++) { wm = ARRAY_ITEM(&find_list, i).wl; - cdata = window_choose_data_create(ctx); + cdata = window_choose_data_create(TREE_OTHER, c, c->session); cdata->idx = wm->idx; - cdata->client->references++; cdata->wl = wm; cdata->ft_template = xstrdup(template); @@ -198,8 +197,7 @@ cmd_find_window_exec(struct cmd *self, struct cmd_ctx *ctx) window_choose_add(wl->window->active, cdata); } - window_choose_ready(wl->window->active, - 0, cmd_find_window_callback, cmd_find_window_free); + window_choose_ready(wl->window->active, 0, cmd_find_window_callback); out: ARRAY_FREE(&find_list); @@ -215,7 +213,7 @@ cmd_find_window_callback(struct window_choose_data *cdata) if (cdata == NULL) return; - s = cdata->session; + s = cdata->start_session; if (!session_alive(s)) return; @@ -228,16 +226,3 @@ cmd_find_window_callback(struct window_choose_data *cdata) recalculate_sizes(); } } - -void -cmd_find_window_free(struct window_choose_data *cdata) -{ - if (cdata == NULL) - return; - - cdata->session->references--; - - free(cdata->ft_template); - format_free(cdata->ft); - free(cdata); -} diff --git a/cmd-has-session.c b/cmd-has-session.c index 9f19c4db..c4286b86 100644 --- a/cmd-has-session.c +++ b/cmd-has-session.c @@ -24,7 +24,7 @@ * Cause client to report an error and exit with 1 if session doesn't exist. */ -enum cmd_retval cmd_has_session_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_has_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_has_session_entry = { "has-session", "has", @@ -37,11 +37,11 @@ const struct cmd_entry cmd_has_session_entry = { }; enum cmd_retval -cmd_has_session_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_has_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - if (cmd_find_session(ctx, args_get(args, 't'), 0) == NULL) + if (cmd_find_session(cmdq, args_get(args, 't'), 0) == NULL) return (CMD_RETURN_ERROR); return (CMD_RETURN_NORMAL); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 636cf805..dddd5d4e 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -29,15 +29,16 @@ * Executes a tmux command if a shell command returns true or false. */ -enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmd_q *); void cmd_if_shell_callback(struct job *); +void cmd_if_shell_done(struct cmd_q *); void cmd_if_shell_free(void *); const struct cmd_entry cmd_if_shell_entry = { "if-shell", "if", - "", 2, 3, - "shell-command command [command]", + "bt:", 2, 3, + "[-b] " CMD_TARGET_PANE_USAGE " shell-command command [command]", 0, NULL, NULL, @@ -47,15 +48,34 @@ const struct cmd_entry cmd_if_shell_entry = { struct cmd_if_shell_data { char *cmd_if; char *cmd_else; - struct cmd_ctx ctx; + struct cmd_q *cmdq; + int bflag; + int started; }; enum cmd_retval -cmd_if_shell_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_if_shell_data *cdata; - const char *shellcmd = args->argv[0]; + char *shellcmd; + struct session *s = NULL; + struct winlink *wl = NULL; + struct window_pane *wp = NULL; + struct format_tree *ft; + + if (args_has(args, 't')) + wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); + + ft = format_create(); + if (s != NULL) + format_session(ft, s); + if (s != NULL && wl != NULL) + format_winlink(ft, s, wl); + if (wp != NULL) + format_window_pane(ft, wp); + shellcmd = format_expand(ft, args->argv[0]); + format_free(ft); cdata = xmalloc(sizeof *cdata); cdata->cmd_if = xstrdup(args->argv[1]); @@ -63,56 +83,83 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_ctx *ctx) cdata->cmd_else = xstrdup(args->argv[2]); else cdata->cmd_else = NULL; - memcpy(&cdata->ctx, ctx, sizeof cdata->ctx); + cdata->bflag = args_has(args, 'b'); - if (ctx->cmdclient != NULL) - ctx->cmdclient->references++; - if (ctx->curclient != NULL) - ctx->curclient->references++; + cdata->started = 0; + cdata->cmdq = cmdq; + cmdq->references++; - job_run(shellcmd, cmd_if_shell_callback, cmd_if_shell_free, cdata); + job_run(shellcmd, s, cmd_if_shell_callback, cmd_if_shell_free, cdata); + free(shellcmd); - return (CMD_RETURN_YIELD); /* don't let client exit */ + if (cdata->bflag) + return (CMD_RETURN_NORMAL); + return (CMD_RETURN_WAIT); } void cmd_if_shell_callback(struct job *job) { struct cmd_if_shell_data *cdata = job->data; - struct cmd_ctx *ctx = &cdata->ctx; + struct cmd_q *cmdq = cdata->cmdq, *cmdq1; struct cmd_list *cmdlist; char *cause, *cmd; - if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0) { + if (cmdq->dead) + return; + + if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0) cmd = cdata->cmd_else; - if (cmd == NULL) - return; - } else + else cmd = cdata->cmd_if; - if (cmd_string_parse(cmd, &cmdlist, &cause) != 0) { + if (cmd == NULL) + return; + + if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { if (cause != NULL) { - ctx->error(ctx, "%s", cause); + cmdq_error(cmdq, "%s", cause); free(cause); } return; } - cmd_list_exec(cmdlist, ctx); + cdata->started = 1; + + cmdq1 = cmdq_new(cmdq->client); + cmdq1->emptyfn = cmd_if_shell_done; + cmdq1->data = cdata; + + cmdq_run(cmdq1, cmdlist); cmd_list_free(cmdlist); } +void +cmd_if_shell_done(struct cmd_q *cmdq1) +{ + struct cmd_if_shell_data *cdata = cmdq1->data; + struct cmd_q *cmdq = cdata->cmdq; + + if (!cmdq_free(cmdq) && !cdata->bflag) + cmdq_continue(cmdq); + + cmdq_free(cmdq1); + + free(cdata->cmd_else); + free(cdata->cmd_if); + free(cdata); +} + void cmd_if_shell_free(void *data) { struct cmd_if_shell_data *cdata = data; - struct cmd_ctx *ctx = &cdata->ctx; + struct cmd_q *cmdq = cdata->cmdq; - if (ctx->cmdclient != NULL) { - ctx->cmdclient->references--; - ctx->cmdclient->flags |= CLIENT_EXIT; - } - if (ctx->curclient != NULL) - ctx->curclient->references--; + if (cdata->started) + return; + + if (!cmdq_free(cmdq) && !cdata->bflag) + cmdq_continue(cmdq); free(cdata->cmd_else); free(cdata->cmd_if); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index a2e7a2dc..2cf587e0 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -29,9 +29,9 @@ */ void cmd_join_pane_key_binding(struct cmd *, int); -enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmd_q *); -enum cmd_retval join_pane(struct cmd *, struct cmd_ctx *, int); +enum cmd_retval join_pane(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_join_pane_entry = { "join-pane", "joinp", @@ -68,13 +68,13 @@ cmd_join_pane_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_join_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_join_pane_exec(struct cmd *self, struct cmd_q *cmdq) { - return (join_pane(self, ctx, self->entry == &cmd_join_pane_entry)); + return (join_pane(self, cmdq, self->entry == &cmd_join_pane_entry)); } enum cmd_retval -join_pane(struct cmd *self, struct cmd_ctx *ctx, int not_same_window) +join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) { struct args *args = self->args; struct session *dst_s; @@ -86,23 +86,25 @@ join_pane(struct cmd *self, struct cmd_ctx *ctx, int not_same_window) enum layout_type type; struct layout_cell *lc; - dst_wl = cmd_find_pane(ctx, args_get(args, 't'), &dst_s, &dst_wp); + dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), &dst_s, &dst_wp); if (dst_wl == NULL) return (CMD_RETURN_ERROR); dst_w = dst_wl->window; dst_idx = dst_wl->idx; + server_unzoom_window(dst_w); - src_wl = cmd_find_pane(ctx, args_get(args, 's'), NULL, &src_wp); + src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; + server_unzoom_window(src_w); if (not_same_window && src_w == dst_w) { - ctx->error(ctx, "can't join a pane to its own window"); + cmdq_error(cmdq, "can't join a pane to its own window"); return (CMD_RETURN_ERROR); } if (!not_same_window && src_wp == dst_wp) { - ctx->error(ctx, "source and target panes must be different"); + cmdq_error(cmdq, "source and target panes must be different"); return (CMD_RETURN_ERROR); } @@ -114,14 +116,14 @@ join_pane(struct cmd *self, struct cmd_ctx *ctx, int not_same_window) if (args_has(args, 'l')) { size = args_strtonum(args, 'l', 0, INT_MAX, &cause); if (cause != NULL) { - ctx->error(ctx, "size %s", cause); + cmdq_error(cmdq, "size %s", cause); free(cause); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'p')) { percentage = args_strtonum(args, 'p', 0, 100, &cause); if (cause != NULL) { - ctx->error(ctx, "percentage %s", cause); + cmdq_error(cmdq, "percentage %s", cause); free(cause); return (CMD_RETURN_ERROR); } @@ -132,7 +134,7 @@ join_pane(struct cmd *self, struct cmd_ctx *ctx, int not_same_window) } lc = layout_split_pane(dst_wp, type, size, args_has(args, 'b')); if (lc == NULL) { - ctx->error(ctx, "create pane failed: pane too small"); + cmdq_error(cmdq, "create pane failed: pane too small"); return (CMD_RETURN_ERROR); } diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 4f7af2ea..40761350 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -26,7 +26,7 @@ * Kill pane. */ -enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_pane_entry = { "kill-pane", "killp", @@ -39,14 +39,15 @@ const struct cmd_entry cmd_kill_pane_entry = { }; enum cmd_retval -cmd_kill_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_kill_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; struct window_pane *loopwp, *tmpwp, *wp; - if ((wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) return (CMD_RETURN_ERROR); + server_unzoom_window(wl->window); if (window_count_panes(wl->window) == 1) { /* Only one pane, kill the window. */ diff --git a/cmd-kill-server.c b/cmd-kill-server.c index 6eb39c41..a6065460 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -27,7 +27,7 @@ * Kill the server and do nothing else. */ -enum cmd_retval cmd_kill_server_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_kill_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_server_entry = { "kill-server", NULL, @@ -39,9 +39,8 @@ const struct cmd_entry cmd_kill_server_entry = { cmd_kill_server_exec }; -/* ARGSUSED */ enum cmd_retval -cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx) +cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_q *cmdq) { kill(getpid(), SIGTERM); diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 145f77e9..a12cc8a4 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -27,7 +27,7 @@ * Note this deliberately has no alias to make it hard to hit by accident. */ -enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_session_entry = { "kill-session", NULL, @@ -40,12 +40,12 @@ const struct cmd_entry cmd_kill_session_entry = { }; enum cmd_retval -cmd_kill_session_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_kill_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s, *s2, *s3; - if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'a')) { diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 88f8dd54..34b97499 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -24,7 +24,7 @@ * Destroy window. */ -enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_window_entry = { "kill-window", "killw", @@ -37,13 +37,13 @@ const struct cmd_entry cmd_kill_window_entry = { }; enum cmd_retval -cmd_kill_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_kill_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl, *wl2, *wl3; struct session *s; - if ((wl = cmd_find_window(ctx, args_get(args, 't'), &s)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'a')) { diff --git a/cmd-link-window.c b/cmd-link-window.c index 0e9c55ef..2be8ace0 100644 --- a/cmd-link-window.c +++ b/cmd-link-window.c @@ -26,7 +26,7 @@ * Link a window into another session. */ -enum cmd_retval cmd_link_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_link_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_link_window_entry = { "link-window", "linkw", @@ -39,7 +39,7 @@ const struct cmd_entry cmd_link_window_entry = { }; enum cmd_retval -cmd_link_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_link_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *src, *dst; @@ -47,15 +47,15 @@ cmd_link_window_exec(struct cmd *self, struct cmd_ctx *ctx) char *cause; int idx, kflag, dflag; - if ((wl = cmd_find_window(ctx, args_get(args, 's'), &src)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 's'), &src)) == NULL) return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(ctx, args_get(args, 't'), &dst)) == -2) + if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst)) == -2) return (CMD_RETURN_ERROR); kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { - ctx->error(ctx, "can't link window: %s", cause); + cmdq_error(cmdq, "can't link window: %s", cause); free(cause); return (CMD_RETURN_ERROR); } diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index f0d17877..58af0020 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -27,7 +27,7 @@ * List paste buffers. */ -enum cmd_retval cmd_list_buffers_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_list_buffers_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_buffers_entry = { "list-buffers", "lsb", @@ -39,9 +39,8 @@ const struct cmd_entry cmd_list_buffers_entry = { cmd_list_buffers_exec }; -/* ARGSUSED */ enum cmd_retval -cmd_list_buffers_exec(unused struct cmd *self, struct cmd_ctx *ctx) +cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct paste_buffer *pb; @@ -60,7 +59,7 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_ctx *ctx) format_paste_buffer(ft, pb); line = format_expand(ft, template); - ctx->print(ctx, "%s", line); + cmdq_print(cmdq, "%s", line); free(line); format_free(ft); diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 19976cfd..59f63099 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -28,7 +28,7 @@ * List all clients. */ -enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_clients_entry = { "list-clients", "lsc", @@ -40,9 +40,8 @@ const struct cmd_entry cmd_list_clients_entry = { cmd_list_clients_exec }; -/* ARGSUSED */ enum cmd_retval -cmd_list_clients_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; @@ -53,7 +52,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_ctx *ctx) char *line; if (args_has(args, 't')) { - s = cmd_find_session(ctx, args_get(args, 't'), 0); + s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); } else @@ -76,7 +75,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_ctx *ctx) format_client(ft, c); line = format_expand(ft, template); - ctx->print(ctx, "%s", line); + cmdq_print(cmdq, "%s", line); free(line); format_free(ft); diff --git a/cmd-list-commands.c b/cmd-list-commands.c index 68e0e80d..1bf6e703 100644 --- a/cmd-list-commands.c +++ b/cmd-list-commands.c @@ -24,7 +24,7 @@ * List all commands with usages. */ -enum cmd_retval cmd_list_commands_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_list_commands_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_commands_entry = { "list-commands", "lscm", @@ -36,14 +36,20 @@ const struct cmd_entry cmd_list_commands_entry = { cmd_list_commands_exec }; -/* ARGSUSED */ enum cmd_retval -cmd_list_commands_exec(unused struct cmd *self, struct cmd_ctx *ctx) +cmd_list_commands_exec(unused struct cmd *self, struct cmd_q *cmdq) { const struct cmd_entry **entryp; - for (entryp = cmd_table; *entryp != NULL; entryp++) - ctx->print(ctx, "%s %s", (*entryp)->name, (*entryp)->usage); + for (entryp = cmd_table; *entryp != NULL; entryp++) { + if ((*entryp)->alias != NULL) { + cmdq_print(cmdq, "%s (%s) %s", (*entryp)->name, + (*entryp)->alias, (*entryp)->usage); + } else { + cmdq_print(cmdq, "%s %s", (*entryp)->name, + (*entryp)->usage); + } + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 51eeb674..78998b66 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -26,8 +26,8 @@ * List key bindings. */ -enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmd_ctx *); -enum cmd_retval cmd_list_keys_table(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_list_keys_table(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_keys_entry = { "list-keys", "lsk", @@ -40,7 +40,7 @@ const struct cmd_entry cmd_list_keys_entry = { }; enum cmd_retval -cmd_list_keys_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct key_binding *bd; @@ -50,7 +50,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_ctx *ctx) int width, keywidth; if (args_has(args, 't')) - return (cmd_list_keys_table(self, ctx)); + return (cmd_list_keys_table(self, cmdq)); width = 0; @@ -91,14 +91,14 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_ctx *ctx) continue; cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); - ctx->print(ctx, "bind-key %s", tmp); + cmdq_print(cmdq, "bind-key %s", tmp); } return (CMD_RETURN_NORMAL); } enum cmd_retval -cmd_list_keys_table(struct cmd *self, struct cmd_ctx *ctx) +cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; const char *tablename; @@ -109,7 +109,7 @@ cmd_list_keys_table(struct cmd *self, struct cmd_ctx *ctx) tablename = args_get(args, 't'); if ((mtab = mode_key_findtable(tablename)) == NULL) { - ctx->error(ctx, "unknown key table: %s", tablename); + cmdq_error(cmdq, "unknown key table: %s", tablename); return (CMD_RETURN_ERROR); } @@ -138,9 +138,12 @@ cmd_list_keys_table(struct cmd *self, struct cmd_ctx *ctx) mode = "c"; cmdstr = mode_key_tostring(mtab->cmdstr, mbind->cmd); if (cmdstr != NULL) { - ctx->print(ctx, "bind-key -%st %s%s %*s %s", + cmdq_print(cmdq, "bind-key -%st %s%s %*s %s%s%s%s", mode, any_mode && *mode == '\0' ? " " : "", - mtab->name, (int) width, key, cmdstr); + mtab->name, (int) width, key, cmdstr, + mbind->arg != NULL ? " \"" : "", + mbind->arg != NULL ? mbind->arg : "", + mbind->arg != NULL ? "\"": ""); } } diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 4ce25a44..0d498e28 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -27,13 +27,13 @@ * List panes on given window. */ -enum cmd_retval cmd_list_panes_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_list_panes_exec(struct cmd *, struct cmd_q *); -void cmd_list_panes_server(struct cmd *, struct cmd_ctx *); +void cmd_list_panes_server(struct cmd *, struct cmd_q *); void cmd_list_panes_session( - struct cmd *, struct session *, struct cmd_ctx *, int); + struct cmd *, struct session *, struct cmd_q *, int); void cmd_list_panes_window(struct cmd *, - struct session *, struct winlink *, struct cmd_ctx *, int); + struct session *, struct winlink *, struct cmd_q *, int); const struct cmd_entry cmd_list_panes_entry = { "list-panes", "lsp", @@ -46,51 +46,51 @@ const struct cmd_entry cmd_list_panes_entry = { }; enum cmd_retval -cmd_list_panes_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_list_panes_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; struct winlink *wl; if (args_has(args, 'a')) - cmd_list_panes_server(self, ctx); + cmd_list_panes_server(self, cmdq); else if (args_has(args, 's')) { - s = cmd_find_session(ctx, args_get(args, 't'), 0); + s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); - cmd_list_panes_session(self, s, ctx, 1); + cmd_list_panes_session(self, s, cmdq, 1); } else { - wl = cmd_find_window(ctx, args_get(args, 't'), &s); + wl = cmd_find_window(cmdq, args_get(args, 't'), &s); if (wl == NULL) return (CMD_RETURN_ERROR); - cmd_list_panes_window(self, s, wl, ctx, 0); + cmd_list_panes_window(self, s, wl, cmdq, 0); } return (CMD_RETURN_NORMAL); } void -cmd_list_panes_server(struct cmd *self, struct cmd_ctx *ctx) +cmd_list_panes_server(struct cmd *self, struct cmd_q *cmdq) { struct session *s; RB_FOREACH(s, sessions, &sessions) - cmd_list_panes_session(self, s, ctx, 2); + cmd_list_panes_session(self, s, cmdq, 2); } void cmd_list_panes_session( - struct cmd *self, struct session *s, struct cmd_ctx *ctx, int type) + struct cmd *self, struct session *s, struct cmd_q *cmdq, int type) { struct winlink *wl; RB_FOREACH(wl, winlinks, &s->windows) - cmd_list_panes_window(self, s, wl, ctx, type); + cmd_list_panes_window(self, s, wl, cmdq, type); } void cmd_list_panes_window(struct cmd *self, - struct session *s, struct winlink *wl, struct cmd_ctx *ctx, int type) + struct session *s, struct winlink *wl, struct cmd_q *cmdq, int type) { struct args *args = self->args; struct window_pane *wp; @@ -135,7 +135,7 @@ cmd_list_panes_window(struct cmd *self, format_window_pane(ft, wp); line = format_expand(ft, template); - ctx->print(ctx, "%s", line); + cmdq_print(cmdq, "%s", line); free(line); format_free(ft); diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index e8db83ed..61c12f4e 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -28,7 +28,7 @@ * List all sessions. */ -enum cmd_retval cmd_list_sessions_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_list_sessions_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_sessions_entry = { "list-sessions", "ls", @@ -41,7 +41,7 @@ const struct cmd_entry cmd_list_sessions_entry = { }; enum cmd_retval -cmd_list_sessions_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_list_sessions_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; @@ -60,7 +60,7 @@ cmd_list_sessions_exec(struct cmd *self, struct cmd_ctx *ctx) format_session(ft, s); line = format_expand(ft, template); - ctx->print(ctx, "%s", line); + cmdq_print(cmdq, "%s", line); free(line); format_free(ft); diff --git a/cmd-list-windows.c b/cmd-list-windows.c index b36b5ee8..5c2a2b95 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -27,11 +27,11 @@ * List windows on given session. */ -enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmd_q *); -void cmd_list_windows_server(struct cmd *, struct cmd_ctx *); +void cmd_list_windows_server(struct cmd *, struct cmd_q *); void cmd_list_windows_session( - struct cmd *, struct session *, struct cmd_ctx *, int); + struct cmd *, struct session *, struct cmd_q *, int); const struct cmd_entry cmd_list_windows_entry = { "list-windows", "lsw", @@ -44,35 +44,35 @@ const struct cmd_entry cmd_list_windows_entry = { }; enum cmd_retval -cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_list_windows_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; if (args_has(args, 'a')) - cmd_list_windows_server(self, ctx); + cmd_list_windows_server(self, cmdq); else { - s = cmd_find_session(ctx, args_get(args, 't'), 0); + s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); - cmd_list_windows_session(self, s, ctx, 0); + cmd_list_windows_session(self, s, cmdq, 0); } return (CMD_RETURN_NORMAL); } void -cmd_list_windows_server(struct cmd *self, struct cmd_ctx *ctx) +cmd_list_windows_server(struct cmd *self, struct cmd_q *cmdq) { struct session *s; RB_FOREACH(s, sessions, &sessions) - cmd_list_windows_session(self, s, ctx, 1); + cmd_list_windows_session(self, s, cmdq, 1); } void cmd_list_windows_session( - struct cmd *self, struct session *s, struct cmd_ctx *ctx, int type) + struct cmd *self, struct session *s, struct cmd_q *cmdq, int type) { struct args *args = self->args; struct winlink *wl; @@ -102,7 +102,7 @@ cmd_list_windows_session( format_window_pane(ft, wl->window->active); line = format_expand(ft, template); - ctx->print(ctx, "%s", line); + cmdq_print(cmdq, "%s", line); free(line); format_free(ft); diff --git a/cmd-list.c b/cmd-list.c index 1717ec3b..08e2067c 100644 --- a/cmd-list.c +++ b/cmd-list.c @@ -24,7 +24,8 @@ #include "tmux.h" struct cmd_list * -cmd_list_parse(int argc, char **argv, char **cause) +cmd_list_parse(int argc, char **argv, const char* file, u_int line, + char **cause) { struct cmd_list *cmdlist; struct cmd *cmd; @@ -34,7 +35,7 @@ cmd_list_parse(int argc, char **argv, char **cause) copy_argv = cmd_copy_argv(argc, argv); - cmdlist = xmalloc(sizeof *cmdlist); + cmdlist = xcalloc(1, sizeof *cmdlist); cmdlist->references = 1; TAILQ_INIT(&cmdlist->list); @@ -55,7 +56,7 @@ cmd_list_parse(int argc, char **argv, char **cause) if (arglen != 1) new_argc++; - cmd = cmd_parse(new_argc, new_argv, cause); + cmd = cmd_parse(new_argc, new_argv, file, line, cause); if (cmd == NULL) goto bad; TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); @@ -64,7 +65,8 @@ cmd_list_parse(int argc, char **argv, char **cause) } if (lastsplit != argc) { - cmd = cmd_parse(argc - lastsplit, copy_argv + lastsplit, cause); + cmd = cmd_parse(argc - lastsplit, copy_argv + lastsplit, + file, line, cause); if (cmd == NULL) goto bad; TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); @@ -79,75 +81,21 @@ bad: return (NULL); } -enum cmd_retval -cmd_list_exec(struct cmd_list *cmdlist, struct cmd_ctx *ctx) -{ - struct client *c = ctx->curclient; - struct cmd *cmd; - enum cmd_retval retval; - int guards, n; - - guards = 0; - if (c != NULL && c->session != NULL) - guards = c->flags & CLIENT_CONTROL; - - notify_disable(); - - retval = 0; - TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { - if (guards) - ctx->print(ctx, "%%begin"); - n = cmd_exec(cmd, ctx); - if (guards) - ctx->print(ctx, "%%end"); - - switch (n) - { - case CMD_RETURN_ERROR: - return (CMD_RETURN_ERROR); - case CMD_RETURN_ATTACH: - /* Client is being attached (send MSG_READY). */ - retval = CMD_RETURN_ATTACH; - - /* - * Mangle the context to treat any following commands - * as if they were called from inside. - */ - if (ctx->curclient == NULL) { - ctx->curclient = ctx->cmdclient; - ctx->cmdclient = NULL; - - ctx->error = key_bindings_error; - ctx->print = key_bindings_print; - ctx->info = key_bindings_info; - } - break; - case CMD_RETURN_YIELD: - if (retval == CMD_RETURN_NORMAL) - retval = CMD_RETURN_YIELD; - break; - case CMD_RETURN_NORMAL: - break; - } - } - - notify_enable(); - return (retval); -} - void cmd_list_free(struct cmd_list *cmdlist) { - struct cmd *cmd; + struct cmd *cmd, *cmd1; if (--cmdlist->references != 0) return; - while (!TAILQ_EMPTY(&cmdlist->list)) { - cmd = TAILQ_FIRST(&cmdlist->list); + TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) { TAILQ_REMOVE(&cmdlist->list, cmd, qentry); - cmd_free(cmd); + args_free(cmd->args); + free(cmd->file); + free(cmd); } + free(cmdlist); } diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index aaf23d99..3be14d6a 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -30,7 +30,7 @@ * Loads a paste buffer from a file. */ -enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmd_q *); void cmd_load_buffer_callback(struct client *, int, void *); const struct cmd_entry cmd_load_buffer_entry = { @@ -44,10 +44,10 @@ const struct cmd_entry cmd_load_buffer_entry = { }; enum cmd_retval -cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c = ctx->cmdclient; + struct client *c = cmdq->client; struct session *s; FILE *f; const char *path, *newpath, *wd; @@ -61,7 +61,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) else { buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { - ctx->error(ctx, "buffer %s", cause); + cmdq_error(cmdq, "buffer %s", cause); free(cause); return (CMD_RETURN_ERROR); } @@ -75,16 +75,16 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) error = server_set_stdin_callback (c, cmd_load_buffer_callback, buffer_ptr, &cause); if (error != 0) { - ctx->error(ctx, "%s: %s", path, cause); + cmdq_error(cmdq, "%s: %s", path, cause); free(cause); return (CMD_RETURN_ERROR); } - return (CMD_RETURN_YIELD); + return (CMD_RETURN_WAIT); } if (c != NULL) wd = c->cwd; - else if ((s = cmd_current_session(ctx, 0)) != NULL) { + else if ((s = cmd_current_session(cmdq, 0)) != NULL) { wd = options_get_string(&s->options, "default-path"); if (*wd == '\0') wd = s->cwd; @@ -96,7 +96,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) path = newpath; } if ((f = fopen(path, "rb")) == NULL) { - ctx->error(ctx, "%s: %s", path, strerror(errno)); + cmdq_error(cmdq, "%s: %s", path, strerror(errno)); return (CMD_RETURN_ERROR); } @@ -105,14 +105,14 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) while ((ch = getc(f)) != EOF) { /* Do not let the server die due to memory exhaustion. */ if ((new_pdata = realloc(pdata, psize + 2)) == NULL) { - ctx->error(ctx, "realloc error: %s", strerror(errno)); + cmdq_error(cmdq, "realloc error: %s", strerror(errno)); goto error; } pdata = new_pdata; pdata[psize++] = ch; } if (ferror(f)) { - ctx->error(ctx, "%s: read error", path); + cmdq_error(cmdq, "%s: read error", path); goto error; } if (pdata != NULL) @@ -126,7 +126,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) return (CMD_RETURN_NORMAL); } if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { - ctx->error(ctx, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %d", buffer); free(pdata); return (CMD_RETURN_ERROR); } @@ -153,12 +153,13 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) c->stdin_callback = NULL; c->references--; - c->flags |= CLIENT_EXIT; + if (c->flags & CLIENT_DEAD) + return; psize = EVBUFFER_LENGTH(c->stdin_data); if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) { free(data); - return; + goto out; } memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); pdata[psize] = '\0'; @@ -174,4 +175,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) } free(data); + +out: + cmdq_continue(c->cmdq); } diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 9d650275..0b6aafe8 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -28,7 +28,7 @@ * Lock commands. */ -enum cmd_retval cmd_lock_server_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_lock_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_lock_server_entry = { "lock-server", "lock", @@ -60,9 +60,8 @@ const struct cmd_entry cmd_lock_client_entry = { cmd_lock_server_exec }; -/* ARGSUSED */ enum cmd_retval -cmd_lock_server_exec(struct cmd *self, unused struct cmd_ctx *ctx) +cmd_lock_server_exec(struct cmd *self, unused struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; @@ -71,11 +70,13 @@ cmd_lock_server_exec(struct cmd *self, unused struct cmd_ctx *ctx) if (self->entry == &cmd_lock_server_entry) server_lock(); else if (self->entry == &cmd_lock_session_entry) { - if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + s = cmd_find_session(cmdq, args_get(args, 't'), 0); + if (s == NULL) return (CMD_RETURN_ERROR); server_lock_session(s); } else { - if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + c = cmd_find_client(cmdq, args_get(args, 't'), 0); + if (c == NULL) return (CMD_RETURN_ERROR); server_lock_client(c); } diff --git a/cmd-move-window.c b/cmd-move-window.c index 8bd9ffee..1a147c7e 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -26,7 +26,7 @@ * Move a window. */ -enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_move_window_entry = { "move-window", "movew", @@ -39,7 +39,7 @@ const struct cmd_entry cmd_move_window_entry = { }; enum cmd_retval -cmd_move_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *src, *dst, *s; @@ -48,7 +48,7 @@ cmd_move_window_exec(struct cmd *self, struct cmd_ctx *ctx) int idx, kflag, dflag; if (args_has(args, 'r')) { - if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); session_renumber_windows(s); @@ -57,15 +57,15 @@ cmd_move_window_exec(struct cmd *self, struct cmd_ctx *ctx) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_window(ctx, args_get(args, 's'), &src)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 's'), &src)) == NULL) return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(ctx, args_get(args, 't'), &dst)) == -2) + if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst)) == -2) return (CMD_RETURN_ERROR); kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { - ctx->error(ctx, "can't move window: %s", cause); + cmdq_error(cmdq, "can't move window: %s", cause); free(cause); return (CMD_RETURN_ERROR); } diff --git a/cmd-new-session.c b/cmd-new-session.c index bb4cdcac..4eebe632 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -31,13 +31,13 @@ */ enum cmd_retval cmd_new_session_check(struct args *); -enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { "new-session", "new", - "dn:s:t:x:y:", 0, 1, - "[-d] [-n window-name] [-s session-name] " CMD_TARGET_SESSION_USAGE - " [-x width] [-y height] [command]", + "AdDF:n:Ps:t:x:y:", 0, 1, + "[-AdDP] [-F format] [-n window-name] [-s session-name] " + CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, NULL, cmd_new_session_check, @@ -53,62 +53,57 @@ cmd_new_session_check(struct args *args) } enum cmd_retval -cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s, *old_s, *groupwith; + struct client *c = cmdq->client; + struct session *s, *groupwith; struct window *w; struct environ env; struct termios tio, *tiop; struct passwd *pw; const char *newname, *target, *update, *cwd, *errstr; - char *cmd, *cause; + const char *template; + char *cmd, *cause, *cp; int detached, idx; u_int sx, sy; + int already_attached; + struct format_tree *ft; newname = args_get(args, 's'); if (newname != NULL) { if (!session_check_name(newname)) { - ctx->error(ctx, "bad session name: %s", newname); + cmdq_error(cmdq, "bad session name: %s", newname); return (CMD_RETURN_ERROR); } if (session_find(newname) != NULL) { - ctx->error(ctx, "duplicate session: %s", newname); + if (args_has(args, 'A')) { + return (cmd_attach_session(cmdq, newname, + args_has(args, 'D'), 0)); + } + cmdq_error(cmdq, "duplicate session: %s", newname); return (CMD_RETURN_ERROR); } } target = args_get(args, 't'); if (target != NULL) { - groupwith = cmd_find_session(ctx, target, 0); + groupwith = cmd_find_session(cmdq, target, 0); if (groupwith == NULL) return (CMD_RETURN_ERROR); } else groupwith = NULL; - /* - * There are three cases: - * - * 1. If cmdclient is non-NULL, new-session has been called from the - * command-line - cmdclient is to become a new attached, interactive - * client. Unless -d is given, the terminal must be opened and then - * the client sent MSG_READY. - * - * 2. If cmdclient is NULL, new-session has been called from an - * existing client (such as a key binding). - * - * 3. Both are NULL, the command was in the configuration file. Treat - * this as if -d was given even if it was not. - * - * In all cases, a new additional session needs to be created and - * (unless -d) set as the current session for the client. - */ - /* Set -d if no client. */ detached = args_has(args, 'd'); - if (ctx->cmdclient == NULL && ctx->curclient == NULL) + if (c == NULL) detached = 1; + /* Is this client already attached? */ + already_attached = 0; + if (c != NULL && c->session != NULL) + already_attached = 1; + /* * Save the termios settings, part of which is used for new windows in * this session. @@ -118,25 +113,25 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) * before opening the terminal as that calls tcsetattr() to prepare for * tmux taking over. */ - if (ctx->cmdclient != NULL && ctx->cmdclient->tty.fd != -1) { - if (tcgetattr(ctx->cmdclient->tty.fd, &tio) != 0) + if (!detached && !already_attached && c->tty.fd != -1) { + if (tcgetattr(c->tty.fd, &tio) != 0) fatal("tcgetattr failed"); tiop = &tio; } else tiop = NULL; /* Open the terminal if necessary. */ - if (!detached && ctx->cmdclient != NULL) { - if (server_client_open(ctx->cmdclient, NULL, &cause) != 0) { - ctx->error(ctx, "open terminal failed: %s", cause); + if (!detached && !already_attached) { + if (server_client_open(c, NULL, &cause) != 0) { + cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } } /* Get the new session working directory. */ - if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL) - cwd = ctx->cmdclient->cwd; + if (c != NULL && c->cwd != NULL) + cwd = c->cwd; else { pw = getpwuid(getuid()); if (pw->pw_dir != NULL && *pw->pw_dir != '\0') @@ -146,32 +141,25 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) } /* Find new session size. */ - if (ctx->cmdclient != NULL) { - sx = ctx->cmdclient->tty.sx; - sy = ctx->cmdclient->tty.sy; - } else if (ctx->curclient != NULL) { - sx = ctx->curclient->tty.sx; - sy = ctx->curclient->tty.sy; + if (c != NULL) { + sx = c->tty.sx; + sy = c->tty.sy; } else { sx = 80; sy = 24; } - if (detached) { - if (args_has(args, 'x')) { - sx = strtonum( - args_get(args, 'x'), 1, USHRT_MAX, &errstr); - if (errstr != NULL) { - ctx->error(ctx, "width %s", errstr); - return (CMD_RETURN_ERROR); - } + if (detached && args_has(args, 'x')) { + sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr); + if (errstr != NULL) { + cmdq_error(cmdq, "width %s", errstr); + return (CMD_RETURN_ERROR); } - if (args_has(args, 'y')) { - sy = strtonum( - args_get(args, 'y'), 1, USHRT_MAX, &errstr); - if (errstr != NULL) { - ctx->error(ctx, "height %s", errstr); - return (CMD_RETURN_ERROR); - } + } + if (detached && args_has(args, 'y')) { + sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr); + if (errstr != NULL) { + cmdq_error(cmdq, "height %s", errstr); + return (CMD_RETURN_ERROR); } } if (sy > 0 && options_get_number(&global_s_options, "status")) @@ -192,14 +180,14 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) /* Construct the environment. */ environ_init(&env); update = options_get_string(&global_s_options, "update-environment"); - if (ctx->cmdclient != NULL) - environ_update(update, &ctx->cmdclient->environ, &env); + if (c != NULL) + environ_update(update, &c->environ, &env); /* Create the new session. */ idx = -1 - options_get_number(&global_s_options, "base-index"); s = session_create(newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause); if (s == NULL) { - ctx->error(ctx, "create session failed: %s", cause); + cmdq_error(cmdq, "create session failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } @@ -208,9 +196,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) /* Set the initial window name if one given. */ if (cmd != NULL && args_has(args, 'n')) { w = s->curw->window; - window_set_name(w, args_get(args, 'n')); - options_set_number(&w->options, "automatic-rename", 0); } @@ -229,25 +215,14 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) * taking this session and needs to get MSG_READY and stay around. */ if (!detached) { - if (ctx->cmdclient != NULL) { - server_write_ready(ctx->cmdclient); - - old_s = ctx->cmdclient->session; - if (old_s != NULL) - ctx->cmdclient->last_session = old_s; - ctx->cmdclient->session = s; - notify_attached_session_changed(ctx->cmdclient); - session_update_activity(s); - server_redraw_client(ctx->cmdclient); - } else { - old_s = ctx->curclient->session; - if (old_s != NULL) - ctx->curclient->last_session = old_s; - ctx->curclient->session = s; - notify_attached_session_changed(ctx->curclient); - session_update_activity(s); - server_redraw_client(ctx->curclient); - } + if (!already_attached) + server_write_ready(c); + else if (c->session != NULL) + c->last_session = c->session; + c->session = s; + notify_attached_session_changed(c); + session_update_activity(s); + server_redraw_client(c); } recalculate_sizes(); server_update_socket(); @@ -257,7 +232,26 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) * session's current window into more mode and display them now. */ if (cfg_finished) - show_cfg_causes(s); + cfg_show_causes(s); - return (detached ? CMD_RETURN_NORMAL : CMD_RETURN_ATTACH); + /* Print if requested. */ + if (args_has(args, 'P')) { + if ((template = args_get(args, 'F')) == NULL) + template = NEW_SESSION_TEMPLATE; + + ft = format_create(); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); + format_session(ft, s); + + cp = format_expand(ft, template); + cmdq_print(cmdq, "%s", cp); + free(cp); + + format_free(ft); + } + + if (!detached) + cmdq->client_exit = 0; + return (CMD_RETURN_NORMAL); } diff --git a/cmd-new-window.c b/cmd-new-window.c index d8c576af..cfc0b8bd 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -26,7 +26,7 @@ * Create a new window. */ -enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_window_entry = { "new-window", "neww", @@ -40,21 +40,19 @@ const struct cmd_entry cmd_new_window_entry = { }; enum cmd_retval -cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; struct winlink *wl; struct client *c; - const char *cmd, *cwd; - const char *template; - char *cause; + const char *cmd, *cwd, *template; + char *cause, *cp; int idx, last, detached; struct format_tree *ft; - char *cp; if (args_has(args, 'a')) { - wl = cmd_find_window(ctx, args_get(args, 't'), &s); + wl = cmd_find_window(cmdq, args_get(args, 't'), &s); if (wl == NULL) return (CMD_RETURN_ERROR); idx = wl->idx + 1; @@ -65,7 +63,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx) break; } if (last == INT_MAX) { - ctx->error(ctx, "no free window indexes"); + cmdq_error(cmdq, "no free window indexes"); return (CMD_RETURN_ERROR); } @@ -76,7 +74,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx) server_unlink_window(s, wl); } } else { - if ((idx = cmd_find_index(ctx, args_get(args, 't'), &s)) == -2) + if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &s)) == -2) return (CMD_RETURN_ERROR); } detached = args_has(args, 'd'); @@ -105,13 +103,13 @@ cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; - cwd = cmd_get_default_path(ctx, args_get(args, 'c')); + cwd = cmd_get_default_path(cmdq, args_get(args, 'c')); if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); wl = session_new(s, args_get(args, 'n'), cmd, cwd, idx, &cause); if (wl == NULL) { - ctx->error(ctx, "create window failed: %s", cause); + cmdq_error(cmdq, "create window failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } @@ -126,14 +124,14 @@ cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx) template = NEW_WINDOW_TEMPLATE; ft = format_create(); - if ((c = cmd_find_client(ctx, NULL)) != NULL) - format_client(ft, c); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, wl->window->active); cp = format_expand(ft, template); - ctx->print(ctx, "%s", cp); + cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 05bee5c7..b07c9faf 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -27,7 +27,7 @@ * Paste paste buffer if present. */ -enum cmd_retval cmd_paste_buffer_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_paste_buffer_exec(struct cmd *, struct cmd_q *); void cmd_paste_buffer_filter(struct window_pane *, const char *, size_t, const char *, int); @@ -43,7 +43,7 @@ const struct cmd_entry cmd_paste_buffer_entry = { }; enum cmd_retval -cmd_paste_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp; @@ -54,7 +54,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) int buffer; int pflag; - if (cmd_find_pane(ctx, args_get(args, 't'), &s, &wp) == NULL) + if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) return (CMD_RETURN_ERROR); if (!args_has(args, 'b')) @@ -62,7 +62,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) else { buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { - ctx->error(ctx, "buffer %s", cause); + cmdq_error(cmdq, "buffer %s", cause); free(cause); return (CMD_RETURN_ERROR); } @@ -73,7 +73,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) else { pb = paste_get_index(&global_buffers, buffer); if (pb == NULL) { - ctx->error(ctx, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); } } diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index d2d85003..aa72c699 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -31,7 +31,7 @@ * Open pipe to redirect pane output. If already open, close first. */ -enum cmd_retval cmd_pipe_pane_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_pipe_pane_exec(struct cmd *, struct cmd_q *); void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *); @@ -46,7 +46,7 @@ const struct cmd_entry cmd_pipe_pane_entry = { }; enum cmd_retval -cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; @@ -54,9 +54,9 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx) char *command; int old_fd, pipe_fd[2], null_fd; - if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) + if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); - c = cmd_find_client(ctx, NULL); + c = cmd_find_client(cmdq, NULL, 1); /* Destroy the old pipe. */ old_fd = wp->pipe_fd; @@ -81,14 +81,14 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx) /* Open the new pipe. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fd) != 0) { - ctx->error(ctx, "socketpair error: %s", strerror(errno)); + cmdq_error(cmdq, "socketpair error: %s", strerror(errno)); return (CMD_RETURN_ERROR); } /* Fork the child. */ switch (fork()) { case -1: - ctx->error(ctx, "fork error: %s", strerror(errno)); + cmdq_error(cmdq, "fork error: %s", strerror(errno)); return (CMD_RETURN_ERROR); case 0: /* Child process. */ @@ -130,7 +130,6 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx) } } -/* ARGSUSED */ void cmd_pipe_pane_error_callback( unused struct bufferevent *bufev, unused short what, void *data) diff --git a/cmd-queue.c b/cmd-queue.c new file mode 100644 index 00000000..8bcc9192 --- /dev/null +++ b/cmd-queue.c @@ -0,0 +1,281 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* Create new command queue. */ +struct cmd_q * +cmdq_new(struct client *c) +{ + struct cmd_q *cmdq; + + cmdq = xcalloc(1, sizeof *cmdq); + cmdq->references = 1; + cmdq->dead = 0; + + cmdq->client = c; + cmdq->client_exit = 0; + + TAILQ_INIT(&cmdq->queue); + cmdq->item = NULL; + cmdq->cmd = NULL; + + return (cmdq); +} + +/* Free command queue */ +int +cmdq_free(struct cmd_q *cmdq) +{ + if (--cmdq->references != 0) + return (cmdq->dead); + + cmdq_flush(cmdq); + free(cmdq); + return (1); +} + +/* Show message from command. */ +void printflike2 +cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) +{ + struct client *c = cmdq->client; + struct window *w; + va_list ap; + + va_start(ap, fmt); + + if (c == NULL) + /* nothing */; + else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + va_start(ap, fmt); + evbuffer_add_vprintf(c->stdout_data, fmt, ap); + va_end(ap); + + evbuffer_add(c->stdout_data, "\n", 1); + server_push_stdout(c); + } else { + w = c->session->curw->window; + if (w->active->mode != &window_copy_mode) { + window_pane_reset_mode(w->active); + window_pane_set_mode(w->active, &window_copy_mode); + window_copy_init_for_output(w->active); + } + window_copy_vadd(w->active, fmt, ap); + } + + va_end(ap); +} + +/* Show info from command. */ +void printflike2 +cmdq_info(struct cmd_q *cmdq, const char *fmt, ...) +{ + struct client *c = cmdq->client; + va_list ap; + char *msg; + + if (options_get_number(&global_options, "quiet")) + return; + + va_start(ap, fmt); + + if (c == NULL) + /* nothing */; + else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + va_start(ap, fmt); + evbuffer_add_vprintf(c->stdout_data, fmt, ap); + va_end(ap); + + evbuffer_add(c->stdout_data, "\n", 1); + server_push_stdout(c); + } else { + xvasprintf(&msg, fmt, ap); + *msg = toupper((u_char) *msg); + status_message_set(c, "%s", msg); + free(msg); + } + + va_end(ap); + +} + +/* Show error from command. */ +void printflike2 +cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) +{ + struct client *c = cmdq->client; + struct cmd *cmd = cmdq->cmd; + va_list ap; + char *msg, *cause; + size_t msglen; + + va_start(ap, fmt); + msglen = xvasprintf(&msg, fmt, ap); + va_end(ap); + + if (c == NULL) { + xasprintf(&cause, "%s:%u: %s", cmd->file, cmd->line, msg); + ARRAY_ADD(&cfg_causes, cause); + } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + evbuffer_add(c->stderr_data, msg, msglen); + evbuffer_add(c->stderr_data, "\n", 1); + + server_push_stderr(c); + c->retcode = 1; + } else { + *msg = toupper((u_char) *msg); + status_message_set(c, "%s", msg); + } + + free(msg); +} + +/* Print a guard line. */ +int +cmdq_guard(struct cmd_q *cmdq, const char *guard) +{ + struct client *c = cmdq->client; + + if (c == NULL || c->session == NULL) + return 0; + if (!(c->flags & CLIENT_CONTROL)) + return 0; + + evbuffer_add_printf(c->stdout_data, "%%%s %ld %u\n", guard, + (long) cmdq->time, cmdq->number); + server_push_stdout(c); + return 1; +} + +/* Add command list to queue and begin processing if needed. */ +void +cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist) +{ + cmdq_append(cmdq, cmdlist); + + 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 cmd_q_item *item; + + item = xcalloc(1, sizeof *item); + item->cmdlist = cmdlist; + TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry); + cmdlist->references++; +} + +/* Continue processing command queue. Returns 1 if finishes empty. */ +int +cmdq_continue(struct cmd_q *cmdq) +{ + struct cmd_q_item *next; + enum cmd_retval retval; + int empty, guard; + char s[1024]; + + notify_disable(); + + 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 { + next = TAILQ_NEXT(cmdq->item, qentry); + + while (cmdq->cmd != NULL) { + cmd_print(cmdq->cmd, s, sizeof s); + log_debug("cmdq %p: %s (client %d)", cmdq, s, + cmdq->client != NULL ? cmdq->client->ibuf.fd : -1); + + cmdq->time = time(NULL); + cmdq->number++; + + guard = cmdq_guard(cmdq, "begin"); + retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); + if (guard) { + if (retval == CMD_RETURN_ERROR) + cmdq_guard(cmdq, "error"); + else + cmdq_guard(cmdq, "end"); + } + + 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); + } + + 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: + if (cmdq->client_exit) + cmdq->client->flags |= CLIENT_EXIT; + if (cmdq->emptyfn != NULL) + cmdq->emptyfn(cmdq); /* may free cmdq */ + empty = 1; + +out: + notify_enable(); + return (empty); +} + +/* Flush command queue. */ +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-refresh-client.c b/cmd-refresh-client.c index 52518122..7d9d539f 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -24,12 +24,12 @@ * Refresh client. */ -enum cmd_retval cmd_refresh_client_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_refresh_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_refresh_client_entry = { "refresh-client", "refresh", - "St:", 0, 0, - "[-S] " CMD_TARGET_CLIENT_USAGE, + "C:St:", 0, 0, + "[-S] [-C size]" CMD_TARGET_CLIENT_USAGE, 0, NULL, NULL, @@ -37,15 +37,37 @@ const struct cmd_entry cmd_refresh_client_entry = { }; enum cmd_retval -cmd_refresh_client_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; + const char *size; + u_int w, h; - if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); - if (args_has(args, 'S')) { + if (args_has(args, 'C')) { + if ((size = args_get(args, 'C')) == NULL) { + cmdq_error(cmdq, "missing size"); + return (CMD_RETURN_ERROR); + } + if (sscanf(size, "%u,%u", &w, &h) != 2) { + cmdq_error(cmdq, "bad size argument"); + return (CMD_RETURN_ERROR); + } + if (w < PANE_MINIMUM || w > 5000 || + h < PANE_MINIMUM || h > 5000) { + cmdq_error(cmdq, "size too small or too big"); + return (CMD_RETURN_ERROR); + } + if (!(c->flags & CLIENT_CONTROL)) { + cmdq_error(cmdq, "not a control client"); + return (CMD_RETURN_ERROR); + } + if (tty_set_size(&c->tty, w, h)) + recalculate_sizes(); + } else if (args_has(args, 'S')) { status_update_jobs(c); server_status_client(c); } else diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 74443bc2..3f8a9d8f 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -26,7 +26,7 @@ * Change session name. */ -enum cmd_retval cmd_rename_session_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_rename_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_session_entry = { "rename-session", "rename", @@ -39,7 +39,7 @@ const struct cmd_entry cmd_rename_session_entry = { }; enum cmd_retval -cmd_rename_session_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_rename_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; @@ -47,15 +47,15 @@ cmd_rename_session_exec(struct cmd *self, struct cmd_ctx *ctx) newname = args->argv[0]; if (!session_check_name(newname)) { - ctx->error(ctx, "bad session name: %s", newname); + cmdq_error(cmdq, "bad session name: %s", newname); return (CMD_RETURN_ERROR); } if (session_find(newname) != NULL) { - ctx->error(ctx, "duplicate session: %s", newname); + cmdq_error(cmdq, "duplicate session: %s", newname); return (CMD_RETURN_ERROR); } - if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); RB_REMOVE(sessions, &sessions, s); diff --git a/cmd-rename-window.c b/cmd-rename-window.c index e42dd52f..c756ba1f 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -26,7 +26,7 @@ * Rename a window. */ -enum cmd_retval cmd_rename_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_rename_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_window_entry = { "rename-window", "renamew", @@ -39,13 +39,13 @@ const struct cmd_entry cmd_rename_window_entry = { }; enum cmd_retval -cmd_rename_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_rename_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; struct winlink *wl; - if ((wl = cmd_find_window(ctx, args_get(args, 't'), &s)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); window_set_name(wl->window, args->argv[0]); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 74f6354c..ca2a6cd3 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -27,12 +27,12 @@ */ void cmd_resize_pane_key_binding(struct cmd *, int); -enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_resize_pane_entry = { "resize-pane", "resizep", - "DLRt:U", 0, 1, - "[-DLRU] " CMD_TARGET_PANE_USAGE " [adjustment]", + "DLRt:Ux:y:Z", 0, 1, + "[-DLRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]", 0, cmd_resize_pane_key_binding, NULL, @@ -75,6 +75,10 @@ cmd_resize_pane_key_binding(struct cmd *self, int key) self->args = args_create(1, "5"); args_set(self->args, 'R', NULL); break; + case 'z': + self->args = args_create(0); + args_set(self->args, 'Z', NULL); + break; default: self->args = args_create(0); break; @@ -82,27 +86,63 @@ cmd_resize_pane_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_resize_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; + struct window *w; const char *errstr; + char *cause; struct window_pane *wp; u_int adjust; + int x, y; - if ((wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) return (CMD_RETURN_ERROR); + w = wl->window; + + if (args_has(args, 'Z')) { + if (w->flags & WINDOW_ZOOMED) + window_unzoom(w); + else + window_zoom(wp); + server_redraw_window(w); + server_status_window(w); + return (CMD_RETURN_NORMAL); + } + server_unzoom_window(w); if (args->argc == 0) adjust = 1; else { adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); if (errstr != NULL) { - ctx->error(ctx, "adjustment %s", errstr); + cmdq_error(cmdq, "adjustment %s", errstr); return (CMD_RETURN_ERROR); } } + if (args_has(self->args, 'x')) { + x = args_strtonum(self->args, 'x', PANE_MINIMUM, INT_MAX, + &cause); + if (cause != NULL) { + cmdq_error(cmdq, "width %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x); + } + if (args_has(self->args, 'y')) { + y = args_strtonum(self->args, 'y', PANE_MINIMUM, INT_MAX, + &cause); + if (cause != NULL) { + cmdq_error(cmdq, "height %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y); + } + if (args_has(self->args, 'L')) layout_resize_pane(wp, LAYOUT_LEFTRIGHT, -adjust); else if (args_has(self->args, 'R')) diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index cf3d9303..4486c91f 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -28,7 +28,7 @@ * Respawn a pane (restart the command). Kill existing if -k given. */ -enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_pane_entry = { "respawn-pane", "respawnp", @@ -41,7 +41,7 @@ const struct cmd_entry cmd_respawn_pane_entry = { }; enum cmd_retval -cmd_respawn_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; @@ -53,14 +53,14 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_ctx *ctx) char *cause; u_int idx; - if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; if (!args_has(self->args, 'k') && wp->fd != -1) { if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); - ctx->error(ctx, "pane still active: %s:%u.%u", + cmdq_error(cmdq, "pane still active: %s:%u.%u", s->name, wl->idx, idx); return (CMD_RETURN_ERROR); } @@ -79,7 +79,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_ctx *ctx) else cmd = NULL; if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { - ctx->error(ctx, "respawn pane failed: %s", cause); + cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); environ_free(&env); return (CMD_RETURN_ERROR); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 46d6b0d0..35bd3471 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -27,7 +27,7 @@ * Respawn a window (restart the command). Kill existing if -k given. */ -enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_window_entry = { "respawn-window", "respawnw", @@ -40,7 +40,7 @@ const struct cmd_entry cmd_respawn_window_entry = { }; enum cmd_retval -cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; @@ -51,7 +51,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) const char *cmd; char *cause; - if ((wl = cmd_find_window(ctx, args_get(args, 't'), &s)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; @@ -59,7 +59,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd == -1) continue; - ctx->error(ctx, + cmdq_error(cmdq, "window still active: %s:%d", s->name, wl->idx); return (CMD_RETURN_ERROR); } @@ -81,13 +81,13 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) else cmd = NULL; if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { - ctx->error(ctx, "respawn window failed: %s", cause); + cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); environ_free(&env); server_destroy_pane(wp); return (CMD_RETURN_ERROR); } - layout_init(w); + layout_init(w, wp); window_pane_reset_mode(wp); screen_reinit(&wp->base); input_init(wp); diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 74f4c066..75ca7292 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -25,7 +25,7 @@ */ void cmd_rotate_window_key_binding(struct cmd *, int); -enum cmd_retval cmd_rotate_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_rotate_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rotate_window_entry = { "rotate-window", "rotatew", @@ -46,7 +46,7 @@ cmd_rotate_window_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_rotate_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_rotate_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; @@ -55,7 +55,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmd_ctx *ctx) struct layout_cell *lc; u_int sx, sy, xoff, yoff; - if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 44e796df..4df21ff1 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -29,7 +29,7 @@ * Runs a command without a window. */ -enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmd_q *); void cmd_run_shell_callback(struct job *); void cmd_run_shell_free(void *); @@ -37,8 +37,8 @@ void cmd_run_shell_print(struct job *, const char *); const struct cmd_entry cmd_run_shell_entry = { "run-shell", "run", - "t:", 1, 1, - CMD_TARGET_PANE_USAGE " command", + "bt:", 1, 1, + "[-b] " CMD_TARGET_PANE_USAGE " shell-command", 0, NULL, NULL, @@ -47,20 +47,21 @@ const struct cmd_entry cmd_run_shell_entry = { struct cmd_run_shell_data { char *cmd; - struct cmd_ctx ctx; - u_int wp_id; + struct cmd_q *cmdq; + int bflag; + int wp_id; }; void cmd_run_shell_print(struct job *job, const char *msg) { struct cmd_run_shell_data *cdata = job->data; - struct cmd_ctx *ctx = &cdata->ctx; - struct window_pane *wp; + struct window_pane *wp = NULL; - wp = window_pane_find_by_id(cdata->wp_id); + if (cdata->wp_id != -1) + wp = window_pane_find_by_id(cdata->wp_id); if (wp == NULL) { - ctx->print(ctx, "%s", msg); + cmdq_print(cdata->cmdq, "%s", msg); return; } @@ -71,50 +72,63 @@ cmd_run_shell_print(struct job *job, const char *msg) } enum cmd_retval -cmd_run_shell_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_run_shell_data *cdata; - const char *shellcmd = args->argv[0]; - struct window_pane *wp; + char *shellcmd; + struct session *s = NULL; + struct winlink *wl = NULL; + struct window_pane *wp = NULL; + struct format_tree *ft; - if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); + if (args_has(args, 't')) + wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); + + ft = format_create(); + if (s != NULL) + format_session(ft, s); + if (s != NULL && wl != NULL) + format_winlink(ft, s, wl); + if (wp != NULL) + format_window_pane(ft, wp); + shellcmd = format_expand(ft, args->argv[0]); + format_free(ft); cdata = xmalloc(sizeof *cdata); - cdata->cmd = xstrdup(args->argv[0]); - cdata->wp_id = wp->id; - memcpy(&cdata->ctx, ctx, sizeof cdata->ctx); + cdata->cmd = shellcmd; + cdata->bflag = args_has(args, 'b'); + cdata->wp_id = wp != NULL ? (int) wp->id : -1; - if (ctx->cmdclient != NULL) - ctx->cmdclient->references++; - if (ctx->curclient != NULL) - ctx->curclient->references++; + cdata->cmdq = cmdq; + cmdq->references++; - job_run(shellcmd, cmd_run_shell_callback, cmd_run_shell_free, cdata); + job_run(shellcmd, s, cmd_run_shell_callback, cmd_run_shell_free, cdata); - return (CMD_RETURN_YIELD); /* don't let client exit */ + if (cdata->bflag) + return (CMD_RETURN_NORMAL); + return (CMD_RETURN_WAIT); } void cmd_run_shell_callback(struct job *job) { struct cmd_run_shell_data *cdata = job->data; - struct cmd_ctx *ctx = &cdata->ctx; + struct cmd_q *cmdq = cdata->cmdq; char *cmd, *msg, *line; size_t size; int retcode; u_int lines; - if (ctx->cmdclient != NULL && ctx->cmdclient->flags & CLIENT_DEAD) - return; - if (ctx->curclient != NULL && ctx->curclient->flags & CLIENT_DEAD) + if (cmdq->dead) return; + cmd = cdata->cmd; lines = 0; do { if ((line = evbuffer_readline(job->event->input)) != NULL) { - cmd_run_shell_print (job, line); + cmd_run_shell_print(job, line); + free(line); lines++; } } while (line != NULL); @@ -131,8 +145,6 @@ cmd_run_shell_callback(struct job *job) free(line); } - cmd = cdata->cmd; - msg = NULL; if (WIFEXITED(job->status)) { if ((retcode = WEXITSTATUS(job->status)) != 0) @@ -143,7 +155,7 @@ cmd_run_shell_callback(struct job *job) } if (msg != NULL) { if (lines == 0) - ctx->info(ctx, "%s", msg); + cmdq_info(cmdq, "%s", msg); else cmd_run_shell_print(job, msg); free(msg); @@ -154,14 +166,10 @@ void cmd_run_shell_free(void *data) { struct cmd_run_shell_data *cdata = data; - struct cmd_ctx *ctx = &cdata->ctx; + struct cmd_q *cmdq = cdata->cmdq; - if (ctx->cmdclient != NULL) { - ctx->cmdclient->references--; - ctx->cmdclient->flags |= CLIENT_EXIT; - } - if (ctx->curclient != NULL) - ctx->curclient->references--; + if (!cmdq_free(cmdq) && !cdata->bflag) + cmdq_continue(cmdq); free(cdata->cmd); free(cdata); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 638c5742..f1d2808b 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "tmux.h" @@ -29,7 +30,7 @@ * Saves a paste buffer to a file. */ -enum cmd_retval cmd_save_buffer_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_save_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_save_buffer_entry = { "save-buffer", "saveb", @@ -41,79 +42,132 @@ const struct cmd_entry cmd_save_buffer_entry = { cmd_save_buffer_exec }; +const struct cmd_entry cmd_show_buffer_entry = { + "show-buffer", "showb", + "b:", 0, 0, + CMD_BUFFER_USAGE, + 0, + NULL, + NULL, + cmd_save_buffer_exec +}; + enum cmd_retval -cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c = ctx->cmdclient; + struct client *c; struct session *s; struct paste_buffer *pb; const char *path, *newpath, *wd; - char *cause; + char *cause, *start, *end; + size_t size, used; int buffer; mode_t mask; FILE *f; + char *msg; + size_t msglen; if (!args_has(args, 'b')) { if ((pb = paste_get_top(&global_buffers)) == NULL) { - ctx->error(ctx, "no buffers"); + cmdq_error(cmdq, "no buffers"); return (CMD_RETURN_ERROR); } } else { buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { - ctx->error(ctx, "buffer %s", cause); + cmdq_error(cmdq, "buffer %s", cause); free(cause); return (CMD_RETURN_ERROR); } pb = paste_get_index(&global_buffers, buffer); if (pb == NULL) { - ctx->error(ctx, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); } } - path = args->argv[0]; + if (self->entry == &cmd_show_buffer_entry) + path = "-"; + else + path = args->argv[0]; if (strcmp(path, "-") == 0) { + c = cmdq->client; if (c == NULL) { - ctx->error(ctx, "%s: can't write to stdout", path); + cmdq_error(cmdq, "can't write to stdout"); return (CMD_RETURN_ERROR); } - evbuffer_add(c->stdout_data, pb->data, pb->size); - server_push_stdout(c); - } else { - if (c != NULL) - wd = c->cwd; - else if ((s = cmd_current_session(ctx, 0)) != NULL) { - wd = options_get_string(&s->options, "default-path"); - if (*wd == '\0') - wd = s->cwd; - } else - wd = NULL; - if (wd != NULL && *wd != '\0') { - newpath = get_full_path(wd, path); - if (newpath != NULL) - path = newpath; - } - - mask = umask(S_IRWXG | S_IRWXO); - if (args_has(self->args, 'a')) - f = fopen(path, "ab"); - else - f = fopen(path, "wb"); - umask(mask); - if (f == NULL) { - ctx->error(ctx, "%s: %s", path, strerror(errno)); - return (CMD_RETURN_ERROR); - } - if (fwrite(pb->data, 1, pb->size, f) != pb->size) { - ctx->error(ctx, "%s: fwrite error", path); - fclose(f); - return (CMD_RETURN_ERROR); - } - fclose(f); + if (c->session == NULL || (c->flags & CLIENT_CONTROL)) + goto do_stdout; + goto do_print; } + c = cmdq->client; + if (c != NULL) + wd = c->cwd; + else if ((s = cmd_current_session(cmdq, 0)) != NULL) { + wd = options_get_string(&s->options, "default-path"); + if (*wd == '\0') + wd = s->cwd; + } else + wd = NULL; + if (wd != NULL && *wd != '\0') { + newpath = get_full_path(wd, path); + if (newpath != NULL) + path = newpath; + } + + mask = umask(S_IRWXG | S_IRWXO); + if (args_has(self->args, 'a')) + f = fopen(path, "ab"); + else + f = fopen(path, "wb"); + umask(mask); + if (f == NULL) { + cmdq_error(cmdq, "%s: %s", path, strerror(errno)); + return (CMD_RETURN_ERROR); + } + if (fwrite(pb->data, 1, pb->size, f) != pb->size) { + cmdq_error(cmdq, "%s: fwrite error", path); + fclose(f); + return (CMD_RETURN_ERROR); + } + fclose(f); + + return (CMD_RETURN_NORMAL); + +do_stdout: + evbuffer_add(c->stdout_data, pb->data, pb->size); + server_push_stdout(c); + return (CMD_RETURN_NORMAL); + +do_print: + if (pb->size > (INT_MAX / 4) - 1) { + cmdq_error(cmdq, "buffer too big"); + return (CMD_RETURN_ERROR); + } + msg = NULL; + msglen = 0; + + used = 0; + while (used != pb->size) { + start = pb->data + used; + end = memchr(start, '\n', pb->size - used); + if (end != NULL) + size = end - start; + else + size = pb->size - used; + + msglen = size * 4 + 1; + msg = xrealloc(msg, 1, msglen); + + strvisx(msg, start, size, VIS_OCTAL|VIS_TAB); + cmdq_print(cmdq, "%s", msg); + + used += size + (end != NULL); + } + + free(msg); return (CMD_RETURN_NORMAL); } diff --git a/cmd-select-layout.c b/cmd-select-layout.c index b2423e9c..ae1be4c4 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -25,7 +25,7 @@ */ void cmd_select_layout_key_binding(struct cmd *, int); -enum cmd_retval cmd_select_layout_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_select_layout_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_layout_entry = { "select-layout", "selectl", @@ -83,15 +83,16 @@ cmd_select_layout_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; const char *layoutname; int next, previous, layout; - if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); + server_unzoom_window(wl->window); next = self->entry == &cmd_next_layout_entry; if (args_has(self->args, 'n')) @@ -106,7 +107,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx) else layout = layout_set_previous(wl->window); server_redraw_window(wl->window); - ctx->info(ctx, "arranging in: %s", layout_set_name(layout)); + cmdq_info(cmdq, "arranging in: %s", layout_set_name(layout)); return (CMD_RETURN_NORMAL); } @@ -117,18 +118,18 @@ cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx) if (layout != -1) { layout = layout_set_select(wl->window, layout); server_redraw_window(wl->window); - ctx->info(ctx, "arranging in: %s", layout_set_name(layout)); + cmdq_info(cmdq, "arranging in: %s", layout_set_name(layout)); return (CMD_RETURN_NORMAL); } if (args->argc != 0) { layoutname = args->argv[0]; if (layout_parse(wl->window, layoutname) == -1) { - ctx->error(ctx, "can't set layout: %s", layoutname); + cmdq_error(cmdq, "can't set layout: %s", layoutname); return (CMD_RETURN_ERROR); } server_redraw_window(wl->window); - ctx->info(ctx, "arranging in: %s", layoutname); + cmdq_info(cmdq, "arranging in: %s", layoutname); } return (CMD_RETURN_NORMAL); } diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 8ebae5fb..b8a12671 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -25,7 +25,7 @@ */ void cmd_select_pane_key_binding(struct cmd *, int); -enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { "select-pane", "selectp", @@ -64,22 +64,23 @@ cmd_select_pane_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_select_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; struct window_pane *wp; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { - wl = cmd_find_window(ctx, args_get(args, 't'), NULL); + wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); if (wl->window->last == NULL) { - ctx->error(ctx, "no last pane"); + cmdq_error(cmdq, "no last pane"); return (CMD_RETURN_ERROR); } + server_unzoom_window(wl->window); window_set_active_pane(wl->window, wl->window->last); server_status_window(wl->window); server_redraw_window_borders(wl->window); @@ -87,11 +88,12 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_ctx *ctx) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) return (CMD_RETURN_ERROR); + server_unzoom_window(wp->window); if (!window_pane_visible(wp)) { - ctx->error(ctx, "pane not visible"); + cmdq_error(cmdq, "pane not visible"); return (CMD_RETURN_ERROR); } @@ -104,7 +106,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_ctx *ctx) else if (args_has(self->args, 'D')) wp = window_pane_find_down(wp); if (wp == NULL) { - ctx->error(ctx, "pane not found"); + cmdq_error(cmdq, "pane not found"); return (CMD_RETURN_ERROR); } diff --git a/cmd-select-window.c b/cmd-select-window.c index aecb5b5a..c15d5858 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -27,7 +27,7 @@ */ void cmd_select_window_key_binding(struct cmd *, int); -enum cmd_retval cmd_select_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_select_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_window_entry = { "select-window", "selectw", @@ -84,7 +84,7 @@ cmd_select_window_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_select_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; @@ -102,31 +102,31 @@ cmd_select_window_exec(struct cmd *self, struct cmd_ctx *ctx) last = 1; if (next || previous || last) { - s = cmd_find_session(ctx, args_get(args, 't'), 0); + s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); activity = args_has(self->args, 'a'); if (next) { if (session_next(s, activity) != 0) { - ctx->error(ctx, "no next window"); + cmdq_error(cmdq, "no next window"); return (CMD_RETURN_ERROR); } } else if (previous) { if (session_previous(s, activity) != 0) { - ctx->error(ctx, "no previous window"); + cmdq_error(cmdq, "no previous window"); return (CMD_RETURN_ERROR); } } else { if (session_last(s) != 0) { - ctx->error(ctx, "no last window"); + cmdq_error(cmdq, "no last window"); return (CMD_RETURN_ERROR); } } server_redraw_session(s); } else { - wl = cmd_find_window(ctx, args_get(args, 't'), &s); + wl = cmd_find_window(cmdq, args_get(args, 't'), &s); if (wl == NULL) return (CMD_RETURN_ERROR); @@ -136,7 +136,7 @@ cmd_select_window_exec(struct cmd *self, struct cmd_ctx *ctx) */ if (args_has(self->args, 'T') && wl == s->curw) { if (session_last(s) != 0) { - ctx->error(ctx, "no last window"); + cmdq_error(cmdq, "no last window"); return (-1); } server_redraw_session(s); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index e19ca3b0..37d4fd2b 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -27,7 +27,7 @@ * Send keys to client. */ -enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_send_keys_entry = { "send-keys", "send", @@ -39,8 +39,18 @@ const struct cmd_entry cmd_send_keys_entry = { cmd_send_keys_exec }; +const struct cmd_entry cmd_send_prefix_entry = { + "send-prefix", NULL, + "2t:", 0, 0, + "[-2] " CMD_TARGET_PANE_USAGE, + 0, + NULL, + NULL, + cmd_send_keys_exec +}; + enum cmd_retval -cmd_send_keys_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp; @@ -49,9 +59,18 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_ctx *ctx) const char *str; int i, key; - if (cmd_find_pane(ctx, args_get(args, 't'), &s, &wp) == NULL) + if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) return (CMD_RETURN_ERROR); + if (self->entry == &cmd_send_prefix_entry) { + if (args_has(args, '2')) + key = options_get_number(&s->options, "prefix2"); + else + key = options_get_number(&s->options, "prefix"); + window_pane_key(wp, s, key); + return (CMD_RETURN_NORMAL); + } + if (args_has(args, 'R')) { ictx = &wp->ictx; diff --git a/cmd-server-info.c b/cmd-server-info.c index 79310664..1e9bf29d 100644 --- a/cmd-server-info.c +++ b/cmd-server-info.c @@ -30,7 +30,7 @@ * Show various information about server. */ -enum cmd_retval cmd_server_info_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_server_info_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_server_info_entry = { "server-info", "info", @@ -42,9 +42,8 @@ const struct cmd_entry cmd_server_info_entry = { cmd_server_info_exec }; -/* ARGSUSED */ enum cmd_retval -cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx) +cmd_server_info_exec(unused struct cmd *self, struct cmd_q *cmdq) { struct tty_term *term; struct client *c; @@ -66,48 +65,47 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx) tim = ctime(&start_time); *strchr(tim, '\n') = '\0'; - ctx->print(ctx, - "tmux " VERSION ", pid %ld, started %s", (long) getpid(), tim); - ctx->print( - ctx, "socket path %s, debug level %d", socket_path, debug_level); + cmdq_print(cmdq, "pid %ld, started %s", (long) getpid(), tim); + cmdq_print(cmdq, "socket path %s, debug level %d", socket_path, + debug_level); if (uname(&un) >= 0) { - ctx->print(ctx, "system is %s %s %s %s", + cmdq_print(cmdq, "system is %s %s %s %s", un.sysname, un.release, un.version, un.machine); } if (cfg_file != NULL) - ctx->print(ctx, "configuration file is %s", cfg_file); + cmdq_print(cmdq, "configuration file is %s", cfg_file); else - ctx->print(ctx, "configuration file not specified"); - ctx->print(ctx, "protocol version is %d", PROTOCOL_VERSION); - ctx->print(ctx, "%s", ""); + cmdq_print(cmdq, "configuration file not specified"); + cmdq_print(cmdq, "protocol version is %d", PROTOCOL_VERSION); + cmdq_print(cmdq, "%s", ""); - ctx->print(ctx, "Clients:"); + cmdq_print(cmdq, "Clients:"); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; - ctx->print(ctx,"%2d: %s (%d, %d): %s [%ux%u %s bs=%hho " + cmdq_print(cmdq,"%2d: %s (%d, %d): %s [%ux%u %s bs=%hho " "class=%u] [flags=0x%x/0x%x, references=%u]", i, c->tty.path, c->ibuf.fd, c->tty.fd, c->session->name, c->tty.sx, c->tty.sy, c->tty.termname, c->tty.tio.c_cc[VERASE], c->tty.class, c->flags, c->tty.flags, c->references); } - ctx->print(ctx, "%s", ""); + cmdq_print(cmdq, "%s", ""); - ctx->print(ctx, "Sessions: [%zu]", sizeof (struct grid_cell)); + cmdq_print(cmdq, "Sessions: [%zu]", sizeof (struct grid_cell)); RB_FOREACH(s, sessions, &sessions) { t = s->creation_time.tv_sec; tim = ctime(&t); *strchr(tim, '\n') = '\0'; - ctx->print(ctx, "%2u: %s: %u windows (created %s) [%ux%u] " - "[flags=0x%x]", s->idx, s->name, + cmdq_print(cmdq, "%2u: %s: %u windows (created %s) [%ux%u] " + "[flags=0x%x]", s->id, s->name, winlink_count(&s->windows), tim, s->sx, s->sy, s->flags); RB_FOREACH(wl, winlinks, &s->windows) { w = wl->window; - ctx->print(ctx, "%4u: %s [%ux%u] [flags=0x%x, " + cmdq_print(cmdq, "%4u: %s [%ux%u] [flags=0x%x, " "references=%u, last layout=%d]", wl->idx, w->name, w->sx, w->sy, w->flags, w->references, w->lastlayout); @@ -123,7 +121,7 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx) size += gl->cellsize * sizeof *gl->celldata; } - ctx->print(ctx, + cmdq_print(cmdq, "%6u: %s %lu %d %u/%u, %zu bytes", j, wp->tty, (u_long) wp->pid, wp->fd, lines, gd->hsize + gd->sy, size); @@ -131,43 +129,43 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx) } } } - ctx->print(ctx, "%s", ""); + cmdq_print(cmdq, "%s", ""); - ctx->print(ctx, "Terminals:"); + cmdq_print(cmdq, "Terminals:"); LIST_FOREACH(term, &tty_terms, entry) { - ctx->print(ctx, "%s [references=%u, flags=0x%x]:", + cmdq_print(cmdq, "%s [references=%u, flags=0x%x]:", term->name, term->references, term->flags); for (i = 0; i < NTTYCODE; i++) { ent = &tty_term_codes[i]; code = &term->codes[ent->code]; switch (code->type) { case TTYCODE_NONE: - ctx->print(ctx, "%2u: %s: [missing]", + cmdq_print(cmdq, "%2u: %s: [missing]", ent->code, ent->name); break; case TTYCODE_STRING: strnvis(out, code->value.string, sizeof out, VIS_OCTAL|VIS_TAB|VIS_NL); - ctx->print(ctx, "%2u: %s: (string) %s", + cmdq_print(cmdq, "%2u: %s: (string) %s", ent->code, ent->name, out); break; case TTYCODE_NUMBER: - ctx->print(ctx, "%2u: %s: (number) %d", + cmdq_print(cmdq, "%2u: %s: (number) %d", ent->code, ent->name, code->value.number); break; case TTYCODE_FLAG: - ctx->print(ctx, "%2u: %s: (flag) %s", + cmdq_print(cmdq, "%2u: %s: (flag) %s", ent->code, ent->name, code->value.flag ? "true" : "false"); break; } } } - ctx->print(ctx, "%s", ""); + cmdq_print(cmdq, "%s", ""); - ctx->print(ctx, "Jobs:"); + cmdq_print(cmdq, "Jobs:"); LIST_FOREACH(job, &all_jobs, lentry) { - ctx->print(ctx, "%s [fd=%d, pid=%d, status=%d]", + cmdq_print(cmdq, "%s [fd=%d, pid=%d, status=%d]", job->cmd, job->fd, job->pid, job->status); } diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 1eeaead5..46d28ff2 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -27,7 +27,7 @@ * Add or set a paste buffer. */ -enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { "set-buffer", "setb", @@ -40,7 +40,7 @@ const struct cmd_entry cmd_set_buffer_entry = { }; enum cmd_retval -cmd_set_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; u_int limit; @@ -60,14 +60,14 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { - ctx->error(ctx, "buffer %s", cause); + cmdq_error(cmdq, "buffer %s", cause); free(cause); free(pdata); return (CMD_RETURN_ERROR); } if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { - ctx->error(ctx, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %d", buffer); free(pdata); return (CMD_RETURN_ERROR); } diff --git a/cmd-set-environment.c b/cmd-set-environment.c index c7a4fc51..0f0365aa 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -27,7 +27,7 @@ * Set an environment variable. */ -enum cmd_retval cmd_set_environment_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_set_environment_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_environment_entry = { "set-environment", "setenv", @@ -40,7 +40,7 @@ const struct cmd_entry cmd_set_environment_entry = { }; enum cmd_retval -cmd_set_environment_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; @@ -49,11 +49,11 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_ctx *ctx) name = args->argv[0]; if (*name == '\0') { - ctx->error(ctx, "empty variable name"); + cmdq_error(cmdq, "empty variable name"); return (CMD_RETURN_ERROR); } if (strchr(name, '=') != NULL) { - ctx->error(ctx, "variable name contains ="); + cmdq_error(cmdq, "variable name contains ="); return (CMD_RETURN_ERROR); } @@ -65,26 +65,26 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_ctx *ctx) if (args_has(self->args, 'g')) env = &global_environ; else { - if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); env = &s->environ; } if (args_has(self->args, 'u')) { if (value != NULL) { - ctx->error(ctx, "can't specify a value with -u"); + cmdq_error(cmdq, "can't specify a value with -u"); return (CMD_RETURN_ERROR); } environ_unset(env, name); } else if (args_has(self->args, 'r')) { if (value != NULL) { - ctx->error(ctx, "can't specify a value with -r"); + cmdq_error(cmdq, "can't specify a value with -r"); return (CMD_RETURN_ERROR); } environ_set(env, name, NULL); } else { if (value == NULL) { - ctx->error(ctx, "no value specified"); + cmdq_error(cmdq, "no value specified"); return (CMD_RETURN_ERROR); } environ_set(env, name, value); diff --git a/cmd-set-option.c b/cmd-set-option.c index ca99a977..a46460a8 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -27,41 +27,44 @@ * Set an option. */ -enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmd_q *); -int cmd_set_option_unset(struct cmd *, struct cmd_ctx *, +enum cmd_retval cmd_set_option_user(struct cmd *, struct cmd_q *, + const char *, const char *); + +int cmd_set_option_unset(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); -int cmd_set_option_set(struct cmd *, struct cmd_ctx *, +int cmd_set_option_set(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); -struct options_entry *cmd_set_option_string(struct cmd *, struct cmd_ctx *, +struct options_entry *cmd_set_option_string(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); -struct options_entry *cmd_set_option_number(struct cmd *, struct cmd_ctx *, +struct options_entry *cmd_set_option_number(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); -struct options_entry *cmd_set_option_key(struct cmd *, struct cmd_ctx *, +struct options_entry *cmd_set_option_key(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); -struct options_entry *cmd_set_option_colour(struct cmd *, struct cmd_ctx *, +struct options_entry *cmd_set_option_colour(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); -struct options_entry *cmd_set_option_attributes(struct cmd *, struct cmd_ctx *, +struct options_entry *cmd_set_option_attributes(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); -struct options_entry *cmd_set_option_flag(struct cmd *, struct cmd_ctx *, +struct options_entry *cmd_set_option_flag(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); -struct options_entry *cmd_set_option_choice(struct cmd *, struct cmd_ctx *, +struct options_entry *cmd_set_option_choice(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); const struct cmd_entry cmd_set_option_entry = { "set-option", "set", - "agqst:uw", 1, 2, - "[-agsquw] [-t target-session|target-window] option [value]", + "agoqst:uw", 1, 2, + "[-agosquw] [-t target-session|target-window] option [value]", 0, NULL, NULL, @@ -70,8 +73,8 @@ const struct cmd_entry cmd_set_option_entry = { const struct cmd_entry cmd_set_window_option_entry = { "set-window-option", "setw", - "agqt:u", 1, 2, - "[-agqu] " CMD_TARGET_WINDOW_USAGE " option [value]", + "agoqt:u", 1, 2, + "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", 0, NULL, NULL, @@ -79,7 +82,7 @@ const struct cmd_entry cmd_set_window_option_entry = { }; enum cmd_retval -cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; const struct options_table_entry *table, *oe; @@ -94,7 +97,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) /* Get the option name and value. */ optstr = args->argv[0]; if (*optstr == '\0') { - ctx->error(ctx, "invalid option"); + cmdq_error(cmdq, "invalid option"); return (CMD_RETURN_ERROR); } if (args->argc < 2) @@ -102,14 +105,18 @@ cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) else valstr = args->argv[1]; + /* Is this a user option? */ + if (*optstr == '@') + return (cmd_set_option_user(self, cmdq, optstr, valstr)); + /* Find the option entry, try each table. */ table = oe = NULL; if (options_table_find(optstr, &table, &oe) != 0) { - ctx->error(ctx, "ambiguous option: %s", optstr); + cmdq_error(cmdq, "ambiguous option: %s", optstr); return (CMD_RETURN_ERROR); } if (oe == NULL) { - ctx->error(ctx, "unknown option: %s", optstr); + cmdq_error(cmdq, "unknown option: %s", optstr); return (CMD_RETURN_ERROR); } @@ -120,7 +127,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) if (args_has(self->args, 'g')) oo = &global_w_options; else { - wl = cmd_find_window(ctx, args_get(args, 't'), NULL); + wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); oo = &wl->window->options; @@ -129,22 +136,27 @@ cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) if (args_has(self->args, 'g')) oo = &global_s_options; else { - s = cmd_find_session(ctx, args_get(args, 't'), 0); + s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); oo = &s->options; } } else { - ctx->error(ctx, "unknown table"); + cmdq_error(cmdq, "unknown table"); return (CMD_RETURN_ERROR); } /* Unset or set the option. */ if (args_has(args, 'u')) { - if (cmd_set_option_unset(self, ctx, oe, oo, valstr) != 0) + if (cmd_set_option_unset(self, cmdq, oe, oo, valstr) != 0) return (CMD_RETURN_ERROR); } else { - if (cmd_set_option_set(self, ctx, oe, oo, valstr) != 0) + if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) { + if (!args_has(args, 'q')) + cmdq_print(cmdq, "already set: %s", optstr); + return (CMD_RETURN_NORMAL); + } + if (cmd_set_option_set(self, cmdq, oe, oo, valstr) != 0) return (CMD_RETURN_ERROR); } @@ -171,31 +183,95 @@ cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) return (CMD_RETURN_NORMAL); } +/* Set user option. */ +enum cmd_retval +cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char* optstr, + const char *valstr) +{ + struct args *args = self->args; + struct session *s; + struct winlink *wl; + struct options *oo; + + if (args_has(args, 's')) + oo = &global_options; + else if (args_has(self->args, 'w') || + self->entry == &cmd_set_window_option_entry) { + if (args_has(self->args, 'g')) + oo = &global_w_options; + else { + wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); + if (wl == NULL) + return (CMD_RETURN_ERROR); + oo = &wl->window->options; + } + } else { + if (args_has(self->args, 'g')) + oo = &global_s_options; + else { + s = cmd_find_session(cmdq, args_get(args, 't'), 0); + if (s == NULL) + return (CMD_RETURN_ERROR); + oo = &s->options; + } + } + + if (args_has(args, 'u')) { + if (options_find1(oo, optstr) == NULL) { + cmdq_error(cmdq, "unknown option: %s", optstr); + return (CMD_RETURN_ERROR); + } + if (valstr != NULL) { + cmdq_error(cmdq, "value passed to unset option: %s", + optstr); + return (CMD_RETURN_ERROR); + } + options_remove(oo, optstr); + } else { + if (valstr == NULL) { + cmdq_error(cmdq, "empty value"); + return (CMD_RETURN_ERROR); + } + if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) { + if (!args_has(args, 'q')) + cmdq_print(cmdq, "already set: %s", optstr); + return (CMD_RETURN_NORMAL); + } + options_set_string(oo, optstr, "%s", valstr); + if (!args_has(args, 'q')) { + cmdq_info(cmdq, "set option: %s -> %s", optstr, + valstr); + } + } + return (CMD_RETURN_NORMAL); +} + + /* Unset an option. */ int -cmd_set_option_unset(struct cmd *self, struct cmd_ctx *ctx, +cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { struct args *args = self->args; if (args_has(args, 'g')) { - ctx->error(ctx, "can't unset global option: %s", oe->name); + cmdq_error(cmdq, "can't unset global option: %s", oe->name); return (-1); } if (value != NULL) { - ctx->error(ctx, "value passed to unset option: %s", oe->name); + cmdq_error(cmdq, "value passed to unset option: %s", oe->name); return (-1); } options_remove(oo, oe->name); if (!args_has(args, 'q')) - ctx->info(ctx, "unset option: %s", oe->name); + cmdq_info(cmdq, "unset option: %s", oe->name); return (0); } /* Set an option. */ int -cmd_set_option_set(struct cmd *self, struct cmd_ctx *ctx, +cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { struct args *args = self->args; @@ -203,46 +279,46 @@ cmd_set_option_set(struct cmd *self, struct cmd_ctx *ctx, const char *s; if (oe->type != OPTIONS_TABLE_FLAG && value == NULL) { - ctx->error(ctx, "empty value"); + cmdq_error(cmdq, "empty value"); return (-1); } o = NULL; switch (oe->type) { case OPTIONS_TABLE_STRING: - o = cmd_set_option_string(self, ctx, oe, oo, value); + o = cmd_set_option_string(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_NUMBER: - o = cmd_set_option_number(self, ctx, oe, oo, value); + o = cmd_set_option_number(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_KEY: - o = cmd_set_option_key(self, ctx, oe, oo, value); + o = cmd_set_option_key(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_COLOUR: - o = cmd_set_option_colour(self, ctx, oe, oo, value); + o = cmd_set_option_colour(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_ATTRIBUTES: - o = cmd_set_option_attributes(self, ctx, oe, oo, value); + o = cmd_set_option_attributes(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_FLAG: - o = cmd_set_option_flag(self, ctx, oe, oo, value); + o = cmd_set_option_flag(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_CHOICE: - o = cmd_set_option_choice(self, ctx, oe, oo, value); + o = cmd_set_option_choice(self, cmdq, oe, oo, value); break; } if (o == NULL) return (-1); - s = options_table_print_entry(oe, o); + s = options_table_print_entry(oe, o, 0); if (!args_has(args, 'q')) - ctx->info(ctx, "set option: %s -> %s", oe->name, s); + cmdq_info(cmdq, "set option: %s -> %s", oe->name, s); return (0); } /* Set a string option. */ struct options_entry * -cmd_set_option_string(struct cmd *self, unused struct cmd_ctx *ctx, +cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { struct args *args = self->args; @@ -263,7 +339,7 @@ cmd_set_option_string(struct cmd *self, unused struct cmd_ctx *ctx, /* Set a number option. */ struct options_entry * -cmd_set_option_number(unused struct cmd *self, struct cmd_ctx *ctx, +cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { long long ll; @@ -271,7 +347,7 @@ cmd_set_option_number(unused struct cmd *self, struct cmd_ctx *ctx, ll = strtonum(value, oe->minimum, oe->maximum, &errstr); if (errstr != NULL) { - ctx->error(ctx, "value is %s: %s", errstr, value); + cmdq_error(cmdq, "value is %s: %s", errstr, value); return (NULL); } @@ -280,13 +356,13 @@ cmd_set_option_number(unused struct cmd *self, struct cmd_ctx *ctx, /* Set a key option. */ struct options_entry * -cmd_set_option_key(unused struct cmd *self, struct cmd_ctx *ctx, +cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { int key; if ((key = key_string_lookup_string(value)) == KEYC_NONE) { - ctx->error(ctx, "bad key: %s", value); + cmdq_error(cmdq, "bad key: %s", value); return (NULL); } @@ -295,13 +371,13 @@ cmd_set_option_key(unused struct cmd *self, struct cmd_ctx *ctx, /* Set a colour option. */ struct options_entry * -cmd_set_option_colour(unused struct cmd *self, struct cmd_ctx *ctx, +cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { int colour; if ((colour = colour_fromstring(value)) == -1) { - ctx->error(ctx, "bad colour: %s", value); + cmdq_error(cmdq, "bad colour: %s", value); return (NULL); } @@ -310,13 +386,13 @@ cmd_set_option_colour(unused struct cmd *self, struct cmd_ctx *ctx, /* Set an attributes option. */ struct options_entry * -cmd_set_option_attributes(unused struct cmd *self, struct cmd_ctx *ctx, +cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { int attr; if ((attr = attributes_fromstring(value)) == -1) { - ctx->error(ctx, "bad attributes: %s", value); + cmdq_error(cmdq, "bad attributes: %s", value); return (NULL); } @@ -325,7 +401,7 @@ cmd_set_option_attributes(unused struct cmd *self, struct cmd_ctx *ctx, /* Set a flag option. */ struct options_entry * -cmd_set_option_flag(unused struct cmd *self, struct cmd_ctx *ctx, +cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { int flag; @@ -342,7 +418,7 @@ cmd_set_option_flag(unused struct cmd *self, struct cmd_ctx *ctx, strcasecmp(value, "no") == 0) flag = 0; else { - ctx->error(ctx, "bad value: %s", value); + cmdq_error(cmdq, "bad value: %s", value); return (NULL); } } @@ -352,8 +428,9 @@ cmd_set_option_flag(unused struct cmd *self, struct cmd_ctx *ctx, /* Set a choice option. */ struct options_entry * -cmd_set_option_choice(unused struct cmd *self, struct cmd_ctx *ctx, - const struct options_table_entry *oe, struct options *oo, const char *value) +cmd_set_option_choice(unused struct cmd *self, struct cmd_q *cmdq, + const struct options_table_entry *oe, struct options *oo, + const char *value) { const char **choicep; int n, choice = -1; @@ -365,13 +442,13 @@ cmd_set_option_choice(unused struct cmd *self, struct cmd_ctx *ctx, continue; if (choice != -1) { - ctx->error(ctx, "ambiguous value: %s", value); + cmdq_error(cmdq, "ambiguous value: %s", value); return (NULL); } choice = n - 1; } if (choice == -1) { - ctx->error(ctx, "unknown value: %s", value); + cmdq_error(cmdq, "unknown value: %s", value); return (NULL); } diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 679d5d4a..ffe98bcc 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -27,7 +27,7 @@ * Show environment. */ -enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_environment_entry = { "show-environment", "showenv", @@ -40,7 +40,7 @@ const struct cmd_entry cmd_show_environment_entry = { }; enum cmd_retval -cmd_show_environment_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; @@ -50,7 +50,7 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_ctx *ctx) if (args_has(self->args, 'g')) env = &global_environ; else { - if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); env = &s->environ; } @@ -58,21 +58,21 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_ctx *ctx) if (args->argc != 0) { envent = environ_find(env, args->argv[0]); if (envent == NULL) { - ctx->error(ctx, "unknown variable: %s", args->argv[0]); + cmdq_error(cmdq, "unknown variable: %s", args->argv[0]); return (CMD_RETURN_ERROR); } if (envent->value != NULL) - ctx->print(ctx, "%s=%s", envent->name, envent->value); + cmdq_print(cmdq, "%s=%s", envent->name, envent->value); else - ctx->print(ctx, "-%s", envent->name); + cmdq_print(cmdq, "-%s", envent->name); return (CMD_RETURN_NORMAL); } RB_FOREACH(envent, environ, env) { if (envent->value != NULL) - ctx->print(ctx, "%s=%s", envent->name, envent->value); + cmdq_print(cmdq, "%s=%s", envent->name, envent->value); else - ctx->print(ctx, "-%s", envent->name); + cmdq_print(cmdq, "-%s", envent->name); } return (CMD_RETURN_NORMAL); diff --git a/cmd-show-messages.c b/cmd-show-messages.c index d8c18519..bc2424ad 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -27,7 +27,7 @@ * Show client message log. */ -enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_messages_entry = { "show-messages", "showmsgs", @@ -40,7 +40,7 @@ const struct cmd_entry cmd_show_messages_entry = { }; enum cmd_retval -cmd_show_messages_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; @@ -48,7 +48,7 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_ctx *ctx) char *tim; u_int i; - if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) { @@ -57,7 +57,7 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_ctx *ctx) tim = ctime(&msg->msg_time); *strchr(tim, '\n') = '\0'; - ctx->print(ctx, "%s %s", tim, msg->msg); + cmdq_print(cmdq, "%s %s", tim, msg->msg); } return (CMD_RETURN_NORMAL); diff --git a/cmd-show-options.c b/cmd-show-options.c index d37b791e..e2f78e12 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -27,12 +27,17 @@ * Show options. */ -enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmd_q *); + +enum cmd_retval cmd_show_options_one(struct cmd *, struct cmd_q *, + struct options *, int); +enum cmd_retval cmd_show_options_all(struct cmd *, struct cmd_q *, + const struct options_table_entry *, struct options *); const struct cmd_entry cmd_show_options_entry = { "show-options", "show", - "gst:w", 0, 1, - "[-gsw] [-t target-session|target-window] [option]", + "gqst:vw", 0, 1, + "[-gqsvw] [-t target-session|target-window] [option]", 0, NULL, NULL, @@ -41,8 +46,8 @@ const struct cmd_entry cmd_show_options_entry = { const struct cmd_entry cmd_show_window_options_entry = { "show-window-options", "showw", - "gt:", 0, 1, - "[-g] " CMD_TARGET_WINDOW_USAGE " [option]", + "gvt:", 0, 1, + "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", 0, NULL, NULL, @@ -50,15 +55,14 @@ const struct cmd_entry cmd_show_window_options_entry = { }; enum cmd_retval -cmd_show_options_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - const struct options_table_entry *table, *oe; struct session *s; struct winlink *wl; + const struct options_table_entry *table; struct options *oo; - struct options_entry *o; - const char *optval; + int quiet; if (args_has(self->args, 's')) { oo = &global_options; @@ -69,7 +73,7 @@ cmd_show_options_exec(struct cmd *self, struct cmd_ctx *ctx) if (args_has(self->args, 'g')) oo = &global_w_options; else { - wl = cmd_find_window(ctx, args_get(args, 't'), NULL); + wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); oo = &wl->window->options; @@ -79,34 +83,90 @@ cmd_show_options_exec(struct cmd *self, struct cmd_ctx *ctx) if (args_has(self->args, 'g')) oo = &global_s_options; else { - s = cmd_find_session(ctx, args_get(args, 't'), 0); + s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); oo = &s->options; } } - if (args->argc != 0) { - table = oe = NULL; - if (options_table_find(args->argv[0], &table, &oe) != 0) { - ctx->error(ctx, "ambiguous option: %s", args->argv[0]); + quiet = args_has(self->args, 'q'); + if (args->argc == 0) + return (cmd_show_options_all(self, cmdq, table, oo)); + else + return (cmd_show_options_one(self, cmdq, oo, quiet)); +} + +enum cmd_retval +cmd_show_options_one(struct cmd *self, struct cmd_q *cmdq, + struct options *oo, int quiet) +{ + struct args *args = self->args; + const struct options_table_entry *table, *oe; + struct options_entry *o; + const char *optval; + + if (*args->argv[0] == '@') { + if ((o = options_find1(oo, args->argv[0])) == NULL) { + if (quiet) + return (CMD_RETURN_NORMAL); + cmdq_error(cmdq, "unknown option: %s", args->argv[0]); return (CMD_RETURN_ERROR); } - if (oe == NULL) { - ctx->error(ctx, "unknown option: %s", args->argv[0]); - return (CMD_RETURN_ERROR); + if (args_has(self->args, 'v')) + cmdq_print(cmdq, "%s", o->str); + else + cmdq_print(cmdq, "%s \"%s\"", o->name, o->str); + return (CMD_RETURN_NORMAL); + } + + table = oe = NULL; + if (options_table_find(args->argv[0], &table, &oe) != 0) { + cmdq_error(cmdq, "ambiguous option: %s", args->argv[0]); + return (CMD_RETURN_ERROR); + } + if (oe == NULL) { + if (quiet) + return (CMD_RETURN_NORMAL); + cmdq_error(cmdq, "unknown option: %s", args->argv[0]); + return (CMD_RETURN_ERROR); + } + if ((o = options_find1(oo, oe->name)) == NULL) + return (CMD_RETURN_NORMAL); + optval = options_table_print_entry(oe, o, args_has(self->args, 'v')); + if (args_has(self->args, 'v')) + cmdq_print(cmdq, "%s", optval); + else + cmdq_print(cmdq, "%s %s", oe->name, optval); + return (CMD_RETURN_NORMAL); +} + +enum cmd_retval +cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, + const struct options_table_entry *table, struct options *oo) +{ + const struct options_table_entry *oe; + struct options_entry *o; + const char *optval; + + RB_FOREACH(o, options_tree, &oo->tree) { + if (*o->name == '@') { + if (args_has(self->args, 'v')) + cmdq_print(cmdq, "%s", o->str); + else + cmdq_print(cmdq, "%s \"%s\"", o->name, o->str); } + } + + for (oe = table; oe->name != NULL; oe++) { if ((o = options_find1(oo, oe->name)) == NULL) - return (CMD_RETURN_NORMAL); - optval = options_table_print_entry(oe, o); - ctx->print(ctx, "%s %s", oe->name, optval); - } else { - for (oe = table; oe->name != NULL; oe++) { - if ((o = options_find1(oo, oe->name)) == NULL) - continue; - optval = options_table_print_entry(oe, o); - ctx->print(ctx, "%s %s", oe->name, optval); - } + continue; + optval = options_table_print_entry(oe, o, + args_has(self->args, 'v')); + if (args_has(self->args, 'v')) + cmdq_print(cmdq, "%s", optval); + else + cmdq_print(cmdq, "%s %s", oe->name, optval); } return (CMD_RETURN_NORMAL); diff --git a/cmd-source-file.c b/cmd-source-file.c index 220ec89c..827d4c00 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -26,7 +26,10 @@ * Sources a configuration file. */ -enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmd_q *); + +void cmd_source_file_show(struct cmd_q *); +void cmd_source_file_done(struct cmd_q *); const struct cmd_entry cmd_source_file_entry = { "source-file", "source", @@ -39,35 +42,67 @@ const struct cmd_entry cmd_source_file_entry = { }; enum cmd_retval -cmd_source_file_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - int retval; - u_int i; - char *cause; + struct args *args = self->args; + struct cmd_q *cmdq1; + char *cause; - retval = load_cfg(args->argv[0], ctx, &cfg_causes); + cmdq1 = cmdq_new(NULL); + cmdq1->emptyfn = cmd_source_file_done; + cmdq1->data = cmdq; - /* - * If the context for the cmdclient came from tmux's configuration - * file, then return the status of this command now, regardless of the - * error condition. Any errors from parsing a configuration file at - * startup will be handled for us by the server. - */ - if (cfg_references > 0 || - (ctx->curclient == NULL && ctx->cmdclient == NULL)) - return (retval); + switch (load_cfg(args->argv[0], cmdq1, &cause)) { + case -1: + if (cfg_references == 0) { + cmdq_free(cmdq1); + cmdq_error(cmdq, "%s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + ARRAY_ADD(&cfg_causes, cause); + /* FALLTHROUGH */ + case 0: + if (cfg_references == 0) + cmd_source_file_show(cmdq); + cmdq_free(cmdq1); + return (CMD_RETURN_NORMAL); + } + + cmdq->references++; + cfg_references++; + + cmdq_continue(cmdq1); + return (CMD_RETURN_WAIT); +} + +void +cmd_source_file_show(struct cmd_q *cmdq) +{ + u_int i; + char *cause; - /* - * We were called from the command-line in which case print the errors - * gathered here directly. - */ for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { cause = ARRAY_ITEM(&cfg_causes, i); - ctx->print(ctx, "%s", cause); + cmdq_print(cmdq, "%s", cause); free(cause); } ARRAY_FREE(&cfg_causes); - - return (retval); +} + +void +cmd_source_file_done(struct cmd_q *cmdq1) +{ + struct cmd_q *cmdq = cmdq1->data; + + cmdq_free(cmdq1); + + cfg_references--; + + if (cmdq_free(cmdq)) + return; + + if (cfg_references == 0) + cmd_source_file_show(cmdq); + cmdq_continue(cmdq); } diff --git a/cmd-split-window.c b/cmd-split-window.c index cac8095e..139f7e50 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -28,7 +28,7 @@ */ void cmd_split_window_key_binding(struct cmd *, int); -enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_split_window_entry = { "split-window", "splitw", @@ -50,7 +50,7 @@ cmd_split_window_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; @@ -58,8 +58,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) struct window *w; struct window_pane *wp, *new_wp = NULL; struct environ env; - const char *cmd, *cwd, *shell; - char *cause, *new_cause; + const char *cmd, *cwd, *shell, *prefix; + char *cause, *new_cause, *cmd1; u_int hlimit; int size, percentage; enum layout_type type; @@ -69,9 +69,10 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) struct format_tree *ft; char *cp; - if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; + server_unzoom_window(w); environ_init(&env); environ_copy(&global_environ, &env); @@ -82,7 +83,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; - cwd = cmd_get_default_path(ctx, args_get(args, 'c')); + cwd = cmd_get_default_path(cmdq, args_get(args, 'c')); type = LAYOUT_TOPBOTTOM; if (args_has(args, 'h')) @@ -121,9 +122,18 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) goto error; } new_wp = window_add_pane(w, hlimit); - if (window_pane_spawn( - new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0) + + if (*cmd != '\0') { + prefix = options_get_string(&w->options, "command-prefix"); + xasprintf(&cmd1, "%s%s", prefix, cmd); + } else + cmd1 = xstrdup(""); + if (window_pane_spawn(new_wp, cmd1, shell, cwd, &env, s->tio, + &cause) != 0) { + free(cmd1); goto error; + } + free(cmd1); layout_assign_pane(lc, new_wp); server_redraw_window(w); @@ -142,14 +152,14 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) template = SPLIT_WINDOW_TEMPLATE; ft = format_create(); - if ((c = cmd_find_client(ctx, NULL)) != NULL) - format_client(ft, c); + if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) + format_client(ft, c); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, new_wp); cp = format_expand(ft, template); - ctx->print(ctx, "%s", cp); + cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); @@ -161,7 +171,7 @@ error: environ_free(&env); if (new_wp != NULL) window_remove_pane(w, new_wp); - ctx->error(ctx, "create pane failed: %s", cause); + cmdq_error(cmdq, "create pane failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } diff --git a/cmd-start-server.c b/cmd-start-server.c index 7da13375..cba2403b 100644 --- a/cmd-start-server.c +++ b/cmd-start-server.c @@ -24,7 +24,7 @@ * Start the server and do nothing else. */ -enum cmd_retval cmd_start_server_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_start_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_start_server_entry = { "start-server", "start", @@ -36,9 +36,8 @@ const struct cmd_entry cmd_start_server_entry = { cmd_start_server_exec }; -/* ARGSUSED */ enum cmd_retval -cmd_start_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx) +cmd_start_server_exec(unused struct cmd *self, unused struct cmd_q *cmdq) { return (CMD_RETURN_NORMAL); } diff --git a/cmd-string.c b/cmd-string.c index 5bb37397..7e84eda6 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -59,7 +59,8 @@ cmd_string_ungetc(size_t *p) * string, or NULL for empty command. */ int -cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause) +cmd_string_parse(const char *s, struct cmd_list **cmdlist, const char *file, + u_int line, char **cause) { size_t p; int ch, i, argc, rval; @@ -131,7 +132,7 @@ cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause) if (argc == 0) goto out; - *cmdlist = cmd_list_parse(argc, argv, cause); + *cmdlist = cmd_list_parse(argc, argv, file, line, cause); if (*cmdlist == NULL) goto out; diff --git a/cmd-suspend-client.c b/cmd-suspend-client.c index 95278f98..101658b1 100644 --- a/cmd-suspend-client.c +++ b/cmd-suspend-client.c @@ -27,7 +27,7 @@ * Suspend client with SIGTSTP. */ -enum cmd_retval cmd_suspend_client_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_suspend_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_suspend_client_entry = { "suspend-client", "suspendc", @@ -40,12 +40,12 @@ const struct cmd_entry cmd_suspend_client_entry = { }; enum cmd_retval -cmd_suspend_client_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_suspend_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; - if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); tty_stop_tty(&c->tty); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 42fe2fcb..d484f4e2 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -27,7 +27,7 @@ */ void cmd_swap_pane_key_binding(struct cmd *, int); -enum cmd_retval cmd_swap_pane_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_swap_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_pane_entry = { "swap-pane", "swapp", @@ -50,7 +50,7 @@ cmd_swap_pane_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *src_wl, *dst_wl; @@ -59,10 +59,11 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx) struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; - dst_wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &dst_wp); + dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &dst_wp); if (dst_wl == NULL) return (CMD_RETURN_ERROR); dst_w = dst_wl->window; + server_unzoom_window(dst_w); if (!args_has(args, 's')) { src_w = dst_w; @@ -77,11 +78,12 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx) } else return (CMD_RETURN_NORMAL); } else { - src_wl = cmd_find_pane(ctx, args_get(args, 's'), NULL, &src_wp); + src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; } + server_unzoom_window(src_w); if (src_wp == dst_wp) return (CMD_RETURN_NORMAL); diff --git a/cmd-swap-window.c b/cmd-swap-window.c index f0c9ffe2..f9a2cb1b 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -26,7 +26,7 @@ * Swap one window with another. */ -enum cmd_retval cmd_swap_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_swap_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_window_entry = { "swap-window", "swapw", @@ -39,7 +39,7 @@ const struct cmd_entry cmd_swap_window_entry = { }; enum cmd_retval -cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; const char *target_src, *target_dst; @@ -49,17 +49,17 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx) struct window *w; target_src = args_get(args, 's'); - if ((wl_src = cmd_find_window(ctx, target_src, &src)) == NULL) + if ((wl_src = cmd_find_window(cmdq, target_src, &src)) == NULL) return (CMD_RETURN_ERROR); target_dst = args_get(args, 't'); - if ((wl_dst = cmd_find_window(ctx, target_dst, &dst)) == NULL) + if ((wl_dst = cmd_find_window(cmdq, target_dst, &dst)) == NULL) return (CMD_RETURN_ERROR); sg_src = session_group_find(src); sg_dst = session_group_find(dst); if (src != dst && sg_src != NULL && sg_dst != NULL && sg_src == sg_dst) { - ctx->error(ctx, "can't move window, sessions are grouped"); + cmdq_error(cmdq, "can't move window, sessions are grouped"); return (CMD_RETURN_ERROR); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 1ca0c41d..9adb2146 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -28,7 +28,7 @@ */ void cmd_switch_client_key_binding(struct cmd *, int); -enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { "switch-client", "switchc", @@ -58,45 +58,45 @@ cmd_switch_client_key_binding(struct cmd *self, int key) } enum cmd_retval -cmd_switch_client_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct session *s; - if ((c = cmd_find_client(ctx, args_get(args, 'c'))) == NULL) + if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'r')) { if (c->flags & CLIENT_READONLY) { c->flags &= ~CLIENT_READONLY; - ctx->info(ctx, "made client writable"); + cmdq_info(cmdq, "made client writable"); } else { c->flags |= CLIENT_READONLY; - ctx->info(ctx, "made client read-only"); + cmdq_info(cmdq, "made client read-only"); } } s = NULL; if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { - ctx->error(ctx, "can't find next session"); + cmdq_error(cmdq, "can't find next session"); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'p')) { if ((s = session_previous_session(c->session)) == NULL) { - ctx->error(ctx, "can't find previous session"); + cmdq_error(cmdq, "can't find previous session"); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'l')) { if (c->last_session != NULL && session_alive(c->last_session)) s = c->last_session; if (s == NULL) { - ctx->error(ctx, "can't find last session"); + cmdq_error(cmdq, "can't find last session"); return (CMD_RETURN_ERROR); } } else - s = cmd_find_session(ctx, args_get(args, 't'), 0); + s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 261ded40..dc037dde 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -27,8 +27,8 @@ */ enum cmd_retval cmd_unbind_key_check(struct args *); -enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_ctx *); -enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_ctx *, int); +enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_unbind_key_entry = { "unbind-key", "unbind", @@ -51,7 +51,7 @@ cmd_unbind_key_check(struct args *args) } enum cmd_retval -cmd_unbind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx) +cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct key_binding *bd; @@ -60,14 +60,14 @@ cmd_unbind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx) if (!args_has(args, 'a')) { key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE) { - ctx->error(ctx, "unknown key: %s", args->argv[0]); + cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } } else key = KEYC_NONE; if (args_has(args, 't')) - return (cmd_unbind_key_table(self, ctx, key)); + return (cmd_unbind_key_table(self, cmdq, key)); if (key == KEYC_NONE) { while (!RB_EMPTY(&key_bindings)) { @@ -84,7 +84,7 @@ cmd_unbind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx) } enum cmd_retval -cmd_unbind_key_table(struct cmd *self, struct cmd_ctx *ctx, int key) +cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) { struct args *args = self->args; const char *tablename; @@ -93,7 +93,7 @@ cmd_unbind_key_table(struct cmd *self, struct cmd_ctx *ctx, int key) tablename = args_get(args, 't'); if ((mtab = mode_key_findtable(tablename)) == NULL) { - ctx->error(ctx, "unknown key table: %s", tablename); + cmdq_error(cmdq, "unknown key table: %s", tablename); return (CMD_RETURN_ERROR); } diff --git a/cmd-unlink-window.c b/cmd-unlink-window.c index 19731ba5..39cdd8ed 100644 --- a/cmd-unlink-window.c +++ b/cmd-unlink-window.c @@ -24,7 +24,7 @@ * Unlink a window, unless it would be destroyed by doing so (only one link). */ -enum cmd_retval cmd_unlink_window_exec(struct cmd *, struct cmd_ctx *); +enum cmd_retval cmd_unlink_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_unlink_window_entry = { "unlink-window", "unlinkw", @@ -37,7 +37,7 @@ const struct cmd_entry cmd_unlink_window_entry = { }; enum cmd_retval -cmd_unlink_window_exec(struct cmd *self, struct cmd_ctx *ctx) +cmd_unlink_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; @@ -46,7 +46,7 @@ cmd_unlink_window_exec(struct cmd *self, struct cmd_ctx *ctx) struct session_group *sg; u_int references; - if ((wl = cmd_find_window(ctx, args_get(args, 't'), &s)) == NULL) + if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; @@ -59,7 +59,7 @@ cmd_unlink_window_exec(struct cmd *self, struct cmd_ctx *ctx) references = 1; if (!args_has(self->args, 'k') && w->references == references) { - ctx->error(ctx, "window is only linked to one session"); + cmdq_error(cmdq, "window is only linked to one session"); return (CMD_RETURN_ERROR); } diff --git a/cmd-wait-for.c b/cmd-wait-for.c new file mode 100644 index 00000000..3a8d8ea4 --- /dev/null +++ b/cmd-wait-for.c @@ -0,0 +1,197 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013 Nicholas Marriott + * Copyright (c) 2013 Thiago de Arruda + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Block or wake a client on a named wait channel. + */ + +enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); + +const struct cmd_entry cmd_wait_for_entry = { + "wait-for", "wait", + "LSU", 1, 1, + "[-LSU] channel", + 0, + NULL, + NULL, + cmd_wait_for_exec +}; + +struct wait_channel { + const char *name; + int locked; + + TAILQ_HEAD(, cmd_q) waiters; + TAILQ_HEAD(, cmd_q) lockers; + + RB_ENTRY(wait_channel) entry; +}; +RB_HEAD(wait_channels, wait_channel); +struct wait_channels wait_channels = RB_INITIALIZER(wait_channels); + +int wait_channel_cmp(struct wait_channel *, struct wait_channel *); +RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp); +RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp); + +int +wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2) +{ + return (strcmp(wc1->name, wc2->name)); +} + +enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *, + struct wait_channel *); +enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *, + struct wait_channel *); +enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *, + struct wait_channel *); +enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, + struct wait_channel *); + +enum cmd_retval +cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) +{ + struct args *args = self->args; + const char *name = args->argv[0]; + struct wait_channel *wc, wc0; + + wc0.name = name; + wc = RB_FIND(wait_channels, &wait_channels, &wc0); + + if (args_has(args, 'S')) + return (cmd_wait_for_signal(cmdq, name, wc)); + if (args_has(args, 'L')) + return (cmd_wait_for_lock(cmdq, name, wc)); + if (args_has(args, 'U')) + return (cmd_wait_for_unlock(cmdq, name, wc)); + return (cmd_wait_for_wait(cmdq, name, wc)); +} + +enum cmd_retval +cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + struct cmd_q *wq, *wq1; + + if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { + cmdq_error(cmdq, "no waiting clients on %s", name); + return (CMD_RETURN_ERROR); + } + + TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { + TAILQ_REMOVE(&wc->waiters, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } + + if (!wc->locked) { + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); + } + + return (CMD_RETURN_NORMAL); +} + +enum cmd_retval +cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + if (cmdq->client == NULL || cmdq->client->session != NULL) { + cmdq_error(cmdq, "not able to wait"); + return (CMD_RETURN_ERROR); + } + + if (wc == NULL) { + wc = xmalloc(sizeof *wc); + wc->name = xstrdup(name); + wc->locked = 0; + TAILQ_INIT(&wc->waiters); + TAILQ_INIT(&wc->lockers); + RB_INSERT(wait_channels, &wait_channels, wc); + } + + TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); + cmdq->references++; + + return (CMD_RETURN_WAIT); +} + +enum cmd_retval +cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + if (cmdq->client == NULL || cmdq->client->session != NULL) { + cmdq_error(cmdq, "not able to lock"); + return (CMD_RETURN_ERROR); + } + + if (wc == NULL) { + wc = xmalloc(sizeof *wc); + wc->name = xstrdup(name); + wc->locked = 0; + TAILQ_INIT(&wc->waiters); + TAILQ_INIT(&wc->lockers); + RB_INSERT(wait_channels, &wait_channels, wc); + } + + if (wc->locked) { + TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); + cmdq->references++; + return (CMD_RETURN_WAIT); + } + wc->locked = 1; + + return (CMD_RETURN_NORMAL); +} + +enum cmd_retval +cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + struct cmd_q *wq; + + 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); + } else { + wc->locked = 0; + if (TAILQ_EMPTY(&wc->waiters)) { + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); + } + } + + return (CMD_RETURN_NORMAL); +} + diff --git a/cmd.c b/cmd.c index 6869c12c..16a9eb98 100644 --- a/cmd.c +++ b/cmd.c @@ -112,6 +112,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_switch_client_entry, &cmd_unbind_key_entry, &cmd_unlink_window_entry, + &cmd_wait_for_entry, NULL }; @@ -121,13 +122,14 @@ struct session *cmd_choose_session(int); struct client *cmd_choose_client(struct clients *); struct client *cmd_lookup_client(const char *); struct session *cmd_lookup_session(const char *, int *); +struct session *cmd_lookup_session_id(const char *); struct winlink *cmd_lookup_window(struct session *, const char *, int *); int cmd_lookup_index(struct session *, const char *, int *); struct window_pane *cmd_lookup_paneid(const char *); struct winlink *cmd_lookup_winlink_windowid(struct session *, const char *); struct window *cmd_lookup_windowid(const char *); -struct session *cmd_window_session(struct cmd_ctx *, - struct window *, struct winlink **); +struct session *cmd_window_session(struct cmd_q *, struct window *, + struct winlink **); struct winlink *cmd_find_window_offset(const char *, struct session *, int *); int cmd_find_index_offset(const char *, struct session *, int *); struct window_pane *cmd_find_pane_offset(const char *, struct winlink *); @@ -205,7 +207,7 @@ cmd_free_argv(int argc, char **argv) } struct cmd * -cmd_parse(int argc, char **argv, char **cause) +cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) { const struct cmd_entry **entryp, *entry; struct cmd *cmd; @@ -255,9 +257,14 @@ cmd_parse(int argc, char **argv, char **cause) if (entry->check != NULL && entry->check(args) != 0) goto usage; - cmd = xmalloc(sizeof *cmd); + cmd = xcalloc(1, sizeof *cmd); cmd->entry = entry; cmd->args = args; + + if (file != NULL) + cmd->file = xstrdup(file); + cmd->line = line; + return (cmd); ambiguous: @@ -281,19 +288,6 @@ usage: return (NULL); } -enum cmd_retval -cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx) -{ - return (cmd->entry->exec(cmd, ctx)); -} - -void -cmd_free(struct cmd *cmd) -{ - args_free(cmd->args); - free(cmd); -} - size_t cmd_print(struct cmd *cmd, char *buf, size_t len) { @@ -319,31 +313,33 @@ cmd_print(struct cmd *cmd, char *buf, size_t len) * session from all sessions. */ struct session * -cmd_current_session(struct cmd_ctx *ctx, int prefer_unattached) +cmd_current_session(struct cmd_q *cmdq, int prefer_unattached) { - struct msg_command_data *data = ctx->msgdata; - struct client *c = ctx->cmdclient; + struct msg_command_data *data = cmdq->msgdata; + struct client *c = cmdq->client; struct session *s; struct sessionslist ss; struct winlink *wl; struct window_pane *wp; + const char *path; int found; - if (ctx->curclient != NULL && ctx->curclient->session != NULL) - return (ctx->curclient->session); + if (c != NULL && c->session != NULL) + return (c->session); /* - * If the name of the calling client's pty is know, build a list of the - * sessions that contain it and if any choose either the first or the - * newest. + * If the name of the calling client's pty is known, build a list of + * the sessions that contain it and if any choose either the first or + * the newest. */ - if (c != NULL && c->tty.path != NULL) { + path = c == NULL ? NULL : c->tty.path; + if (path != NULL) { ARRAY_INIT(&ss); RB_FOREACH(s, sessions, &sessions) { found = 0; RB_FOREACH(wl, winlinks, &s->windows) { TAILQ_FOREACH(wp, &wl->window->panes, entry) { - if (strcmp(wp->tty, c->tty.path) == 0) { + if (strcmp(wp->tty, path) == 0) { found = 1; break; } @@ -362,8 +358,8 @@ cmd_current_session(struct cmd_ctx *ctx, int prefer_unattached) } /* Use the session from the TMUX environment variable. */ - if (data != NULL && data->pid == getpid() && data->idx != -1) { - s = session_find_by_index(data->idx); + if (data != NULL && data->pid == getpid() && data->session_id != -1) { + s = session_find_by_id(data->session_id); if (s != NULL) return (s); } @@ -434,21 +430,21 @@ cmd_choose_session_list(struct sessionslist *ss) * then of all clients. */ struct client * -cmd_current_client(struct cmd_ctx *ctx) +cmd_current_client(struct cmd_q *cmdq) { struct session *s; struct client *c; struct clients cc; u_int i; - if (ctx->curclient != NULL) - return (ctx->curclient); + if (cmdq->client != NULL && cmdq->client->session != NULL) + return (cmdq->client); /* * No current client set. Find the current session and return the * newest of its clients. */ - s = cmd_current_session(ctx, 0); + s = cmd_current_session(cmdq, 0); if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { ARRAY_INIT(&cc); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { @@ -493,15 +489,19 @@ cmd_choose_client(struct clients *cc) /* Find the target client or report an error and return NULL. */ struct client * -cmd_find_client(struct cmd_ctx *ctx, const char *arg) +cmd_find_client(struct cmd_q *cmdq, const char *arg, int quiet) { struct client *c; char *tmparg; size_t arglen; /* A NULL argument means the current client. */ - if (arg == NULL) - return (cmd_current_client(ctx)); + if (arg == NULL) { + c = cmd_current_client(cmdq); + if (c == NULL && !quiet) + cmdq_error(cmdq, "no clients"); + return (c); + } tmparg = xstrdup(arg); /* Trim a single trailing colon if any. */ @@ -513,8 +513,8 @@ cmd_find_client(struct cmd_ctx *ctx, const char *arg) c = cmd_lookup_client(tmparg); /* If no client found, report an error. */ - if (c == NULL) - ctx->error(ctx, "client not found: %s", tmparg); + if (c == NULL && !quiet) + cmdq_error(cmdq, "client not found: %s", tmparg); free(tmparg); return (c); @@ -533,7 +533,7 @@ cmd_lookup_client(const char *name) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) + if (c == NULL || c->session == NULL || c->tty.path == NULL) continue; path = c->tty.path; @@ -551,6 +551,21 @@ cmd_lookup_client(const char *name) return (NULL); } +/* Find the target session or report an error and return NULL. */ +struct session * +cmd_lookup_session_id(const char *arg) +{ + char *endptr; + long id; + + if (arg[0] != '$') + return (NULL); + id = strtol(arg + 1, &endptr, 10); + if (arg[1] != '\0' && *endptr == '\0') + return (session_find_by_id(id)); + return (NULL); +} + /* Lookup a session by name. If no session is found, NULL is returned. */ struct session * cmd_lookup_session(const char *name, int *ambiguous) @@ -559,6 +574,10 @@ cmd_lookup_session(const char *name, int *ambiguous) *ambiguous = 0; + /* Look for $id first. */ + if ((s = cmd_lookup_session_id(name)) != NULL) + return (s); + /* * Look for matches. First look for exact matches - session names must * be unique so an exact match can't be ambigious and can just be @@ -716,14 +735,14 @@ cmd_lookup_windowid(const char *arg) /* Find session and winlink for window. */ struct session * -cmd_window_session(struct cmd_ctx *ctx, struct window *w, struct winlink **wlp) +cmd_window_session(struct cmd_q *cmdq, struct window *w, struct winlink **wlp) { struct session *s; struct sessionslist ss; struct winlink *wl; /* If this window is in the current session, return that winlink. */ - s = cmd_current_session(ctx, 0); + s = cmd_current_session(cmdq, 0); if (s != NULL) { wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { @@ -748,7 +767,7 @@ cmd_window_session(struct cmd_ctx *ctx, struct window *w, struct winlink **wlp) /* Find the target session or report an error and return NULL. */ struct session * -cmd_find_session(struct cmd_ctx *ctx, const char *arg, int prefer_unattached) +cmd_find_session(struct cmd_q *cmdq, const char *arg, int prefer_unattached) { struct session *s; struct window_pane *wp; @@ -760,13 +779,13 @@ cmd_find_session(struct cmd_ctx *ctx, const char *arg, int prefer_unattached) /* A NULL argument means the current session. */ if (arg == NULL) - return (cmd_current_session(ctx, prefer_unattached)); + return (cmd_current_session(cmdq, prefer_unattached)); /* Lookup as pane id or window id. */ if ((wp = cmd_lookup_paneid(arg)) != NULL) - return (cmd_window_session(ctx, wp->window, NULL)); + return (cmd_window_session(cmdq, wp->window, NULL)); if ((w = cmd_lookup_windowid(arg)) != NULL) - return (cmd_window_session(ctx, w, NULL)); + return (cmd_window_session(cmdq, w, NULL)); /* Trim a single trailing colon if any. */ tmparg = xstrdup(arg); @@ -777,7 +796,7 @@ cmd_find_session(struct cmd_ctx *ctx, const char *arg, int prefer_unattached) /* An empty session name is the current session. */ if (*tmparg == '\0') { free(tmparg); - return (cmd_current_session(ctx, prefer_unattached)); + return (cmd_current_session(cmdq, prefer_unattached)); } /* Find the session, if any. */ @@ -790,9 +809,9 @@ cmd_find_session(struct cmd_ctx *ctx, const char *arg, int prefer_unattached) /* If no session found, report an error. */ if (s == NULL) { if (ambiguous) - ctx->error(ctx, "more than one session: %s", tmparg); + cmdq_error(cmdq, "more than one session: %s", tmparg); else - ctx->error(ctx, "session not found: %s", tmparg); + cmdq_error(cmdq, "session not found: %s", tmparg); } free(tmparg); @@ -801,7 +820,7 @@ cmd_find_session(struct cmd_ctx *ctx, const char *arg, int prefer_unattached) /* Find the target session and window or report an error and return NULL. */ struct winlink * -cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp) +cmd_find_window(struct cmd_q *cmdq, const char *arg, struct session **sp) { struct session *s; struct winlink *wl; @@ -814,8 +833,8 @@ cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp) * Find the current session. There must always be a current session, if * it can't be found, report an error. */ - if ((s = cmd_current_session(ctx, 0)) == NULL) { - ctx->error(ctx, "can't establish current session"); + if ((s = cmd_current_session(cmdq, 0)) == NULL) { + cmdq_error(cmdq, "can't establish current session"); return (NULL); } @@ -828,7 +847,7 @@ cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp) /* Lookup as pane id. */ if ((wp = cmd_lookup_paneid(arg)) != NULL) { - s = cmd_window_session(ctx, wp->window, &wl); + s = cmd_window_session(cmdq, wp->window, &wl); if (sp != NULL) *sp = s; return (wl); @@ -909,17 +928,17 @@ lookup_session: no_session: if (ambiguous) - ctx->error(ctx, "multiple sessions: %s", arg); + cmdq_error(cmdq, "multiple sessions: %s", arg); else - ctx->error(ctx, "session not found: %s", arg); + cmdq_error(cmdq, "session not found: %s", arg); free(sessptr); return (NULL); not_found: if (ambiguous) - ctx->error(ctx, "multiple windows: %s", arg); + cmdq_error(cmdq, "multiple windows: %s", arg); else - ctx->error(ctx, "window not found: %s", arg); + cmdq_error(cmdq, "window not found: %s", arg); free(sessptr); return (NULL); } @@ -951,7 +970,7 @@ cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous) * example if it is going to be created). */ int -cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp) +cmd_find_index(struct cmd_q *cmdq, const char *arg, struct session **sp) { struct session *s; struct winlink *wl; @@ -963,8 +982,8 @@ cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp) * Find the current session. There must always be a current session, if * it can't be found, report an error. */ - if ((s = cmd_current_session(ctx, 0)) == NULL) { - ctx->error(ctx, "can't establish current session"); + if ((s = cmd_current_session(cmdq, 0)) == NULL) { + cmdq_error(cmdq, "can't establish current session"); return (-2); } @@ -1047,25 +1066,25 @@ lookup_session: no_session: if (ambiguous) - ctx->error(ctx, "multiple sessions: %s", arg); + cmdq_error(cmdq, "multiple sessions: %s", arg); else - ctx->error(ctx, "session not found: %s", arg); + cmdq_error(cmdq, "session not found: %s", arg); free(sessptr); return (-2); invalid_index: if (ambiguous) goto not_found; - ctx->error(ctx, "invalid index: %s", arg); + cmdq_error(cmdq, "invalid index: %s", arg); free(sessptr); return (-2); not_found: if (ambiguous) - ctx->error(ctx, "multiple windows: %s", arg); + cmdq_error(cmdq, "multiple windows: %s", arg); else - ctx->error(ctx, "window not found: %s", arg); + cmdq_error(cmdq, "window not found: %s", arg); free(sessptr); return (-2); } @@ -1102,7 +1121,7 @@ cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous) * such as mysession:mywindow.0. */ struct winlink * -cmd_find_pane(struct cmd_ctx *ctx, +cmd_find_pane(struct cmd_q *cmdq, const char *arg, struct session **sp, struct window_pane **wpp) { struct session *s; @@ -1112,8 +1131,8 @@ cmd_find_pane(struct cmd_ctx *ctx, u_int idx; /* Get the current session. */ - if ((s = cmd_current_session(ctx, 0)) == NULL) { - ctx->error(ctx, "can't establish current session"); + if ((s = cmd_current_session(cmdq, 0)) == NULL) { + cmdq_error(cmdq, "can't establish current session"); return (NULL); } if (sp != NULL) @@ -1127,7 +1146,7 @@ cmd_find_pane(struct cmd_ctx *ctx, /* Lookup as pane id. */ if ((*wpp = cmd_lookup_paneid(arg)) != NULL) { - s = cmd_window_session(ctx, (*wpp)->window, &wl); + s = cmd_window_session(cmdq, (*wpp)->window, &wl); if (sp != NULL) *sp = s; return (wl); @@ -1142,7 +1161,7 @@ cmd_find_pane(struct cmd_ctx *ctx, winptr[period - arg] = '\0'; if (*winptr == '\0') wl = s->curw; - else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL) + else if ((wl = cmd_find_window(cmdq, winptr, sp)) == NULL) goto error; /* Find the pane section and look it up. */ @@ -1166,7 +1185,7 @@ cmd_find_pane(struct cmd_ctx *ctx, lookup_string: /* Try pane string description. */ if ((*wpp = window_find_string(wl->window, paneptr)) == NULL) { - ctx->error(ctx, "can't find pane: %s", paneptr); + cmdq_error(cmdq, "can't find pane: %s", paneptr); goto error; } @@ -1191,7 +1210,7 @@ lookup_window: return (s->curw); /* Try as a window and use the active pane. */ - if ((wl = cmd_find_window(ctx, arg, sp)) != NULL) + if ((wl = cmd_find_window(cmdq, arg, sp)) != NULL) *wpp = wl->window->active; return (wl); @@ -1221,14 +1240,14 @@ cmd_find_pane_offset(const char *paneptr, struct winlink *wl) /* Replace the first %% or %idx in template by s. */ char * -cmd_template_replace(char *template, const char *s, int idx) +cmd_template_replace(const char *template, const char *s, int idx) { - char ch; - char *buf, *ptr; - int replaced; - size_t len; + char ch, *buf; + const char *ptr; + int replaced; + size_t len; - if (strstr(template, "%") == NULL) + if (strchr(template, '%') == NULL) return (xstrdup(template)); buf = xmalloc(1); @@ -1269,8 +1288,9 @@ cmd_template_replace(char *template, const char *s, int idx) * directory. */ const char * -cmd_get_default_path(struct cmd_ctx *ctx, const char *cwd) +cmd_get_default_path(struct cmd_q *cmdq, const char *cwd) { + struct client *c = cmdq->client; struct session *s; struct environ_entry *envent; const char *root; @@ -1280,7 +1300,7 @@ cmd_get_default_path(struct cmd_ctx *ctx, const char *cwd) size_t skip; static char path[MAXPATHLEN]; - if ((s = cmd_current_session(ctx, 0)) == NULL) + if ((s = cmd_current_session(cmdq, 0)) == NULL) return (NULL); if (cwd == NULL) @@ -1310,10 +1330,10 @@ cmd_get_default_path(struct cmd_ctx *ctx, const char *cwd) return (cwd); } else { /* Empty or relative path. */ - if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL) - root = ctx->cmdclient->cwd; - else if (ctx->curclient != NULL && s->curw != NULL) - root = osdep_get_cwd(s->curw->window->active->fd); + if (c != NULL && c->session == NULL && c->cwd != NULL) + root = c->cwd; + else if (s->curw != NULL) + root = get_proc_cwd(s->curw->window->active->fd); else return (s->cwd); skip = 0; diff --git a/control-notify.c b/control-notify.c index 87a25bb1..0931c23a 100644 --- a/control-notify.c +++ b/control-notify.c @@ -46,8 +46,12 @@ control_notify_input(struct client *c, struct window_pane *wp, if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) { message = evbuffer_new(); evbuffer_add_printf(message, "%%output %%%u ", wp->id); - for (i = 0; i < len; i++) - evbuffer_add_printf(message, "%02hhx", buf[i]); + for (i = 0; i < len; i++) { + if (buf[i] < ' ' || buf[i] == '\\') + evbuffer_add_printf(message, "\\%03o", buf[i]); + else + evbuffer_add_printf(message, "%c", buf[i]); + } control_write_buffer(c, message); evbuffer_free(message); } @@ -104,10 +108,7 @@ control_notify_window_unlinked(unused struct session *s, struct window *w) continue; cs = c->session; - if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) - control_write(c, "%%window-close %u", w->id); - else - control_write(c, "%%unlinked-window-close %u", w->id); + control_write(c, "%%window-close @%u", w->id); } } @@ -125,9 +126,9 @@ control_notify_window_linked(unused struct session *s, struct window *w) cs = c->session; if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) - control_write(c, "%%window-add %u", w->id); + control_write(c, "%%window-add @%u", w->id); else - control_write(c, "%%unlinked-window-add %u", w->id); + control_write(c, "%%unlinked-window-add @%u", w->id); } } @@ -144,13 +145,7 @@ control_notify_window_renamed(struct window *w) continue; s = c->session; - if (winlink_find_by_window_id(&s->windows, w->id) != NULL) { - control_write(c, "%%window-renamed %u %s", - w->id, w->name); - } else { - control_write(c, "%%unlinked-window-renamed %u %s", - w->id, w->name); - } + control_write(c, "%%window-renamed @%u %s", w->id, w->name); } } @@ -163,7 +158,7 @@ control_notify_attached_session_changed(struct client *c) return; s = c->session; - control_write(c, "%%session-changed %d %s", s->idx, s->name); + control_write(c, "%%session-changed $%u %s", s->id, s->name); } void @@ -174,10 +169,10 @@ control_notify_session_renamed(struct session *s) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session != s) + if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; - control_write(c, "%%session-renamed %s", s->name); + control_write(c, "%%session-renamed $%u %s", s->id, s->name); } } @@ -189,7 +184,7 @@ control_notify_session_created(unused struct session *s) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) + if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%sessions-changed"); @@ -204,7 +199,7 @@ control_notify_session_close(unused struct session *s) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) + if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%sessions-changed"); diff --git a/control.c b/control.c index b5ec6bdb..c888877e 100644 --- a/control.c +++ b/control.c @@ -25,46 +25,6 @@ #include "tmux.h" -void printflike2 control_msg_error(struct cmd_ctx *, const char *, ...); -void printflike2 control_msg_print(struct cmd_ctx *, const char *, ...); -void printflike2 control_msg_info(struct cmd_ctx *, const char *, ...); - -/* Command error callback. */ -void printflike2 -control_msg_error(struct cmd_ctx *ctx, const char *fmt, ...) -{ - struct client *c = ctx->curclient; - va_list ap; - - va_start(ap, fmt); - evbuffer_add_vprintf(c->stdout_data, fmt, ap); - va_end(ap); - - evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); -} - -/* Command print callback. */ -void printflike2 -control_msg_print(struct cmd_ctx *ctx, const char *fmt, ...) -{ - struct client *c = ctx->curclient; - va_list ap; - - va_start(ap, fmt); - evbuffer_add_vprintf(c->stdout_data, fmt, ap); - va_end(ap); - - evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); -} - -/* Command info callback. */ -void printflike2 -control_msg_info(unused struct cmd_ctx *ctx, unused const char *fmt, ...) -{ -} - /* Write a line. */ void printflike2 control_write(struct client *c, const char *fmt, ...) @@ -93,7 +53,6 @@ void control_callback(struct client *c, int closed, unused void *data) { char *line, *cause; - struct cmd_ctx ctx; struct cmd_list *cmdlist; if (closed) @@ -108,20 +67,17 @@ control_callback(struct client *c, int closed, unused void *data) break; } - ctx.msgdata = NULL; - ctx.cmdclient = NULL; - ctx.curclient = c; + if (cmd_string_parse(line, &cmdlist, NULL, 0, &cause) != 0) { + c->cmdq->time = time(NULL); + c->cmdq->number++; - ctx.error = control_msg_error; - ctx.print = control_msg_print; - ctx.info = control_msg_info; + cmdq_guard(c->cmdq, "begin"); + control_write(c, "parse error: %s", cause); + cmdq_guard(c->cmdq, "error"); - if (cmd_string_parse(line, &cmdlist, &cause) != 0) { - control_write(c, "%%error in line \"%s\": %s", line, - cause); free(cause); } else { - cmd_list_exec(cmdlist, &ctx); + cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); } diff --git a/format.c b/format.c index 5f57c72e..d70b7678 100644 --- a/format.c +++ b/format.c @@ -32,8 +32,9 @@ * string. */ -int format_replace(struct format_tree *, - const char *, size_t, char **, size_t *, size_t *); +int format_replace(struct format_tree *, const char *, size_t, char **, + size_t *, size_t *); +void format_window_pane_tabs(struct format_tree *, struct window_pane *); /* Format key-value replacement entry. */ RB_GENERATE(format_tree, format_entry, entry, format_cmp); @@ -251,10 +252,11 @@ format_expand(struct format_tree *ft, const char *fmt) continue; } } - while (len - off < 2) { + while (len - off < 3) { buf = xrealloc(buf, 2, len); len *= 2; } + buf[off++] = '#'; buf[off++] = ch; continue; } @@ -278,6 +280,7 @@ format_session(struct format_tree *ft, struct session *s) format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); format_add(ft, "session_width", "%u", s->sx); format_add(ft, "session_height", "%u", s->sy); + format_add(ft, "session_id", "$%u", s->id); sg = session_group_find(s); format_add(ft, "session_grouped", "%d", sg != NULL); @@ -300,8 +303,9 @@ format_session(struct format_tree *ft, struct session *s) void format_client(struct format_tree *ft, struct client *c) { - char *tim; - time_t t; + char *tim; + time_t t; + struct session *s; format_add(ft, "client_cwd", "%s", c->cwd); format_add(ft, "client_height", "%u", c->tty.sy); @@ -321,6 +325,8 @@ format_client(struct format_tree *ft, struct client *c) *strchr(tim, '\n') = '\0'; format_add(ft, "client_activity_string", "%s", tim); + format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX)); + if (c->tty.flags & TTY_UTF8) format_add(ft, "client_utf8", "%d", 1); else @@ -330,6 +336,13 @@ format_client(struct format_tree *ft, struct client *c) format_add(ft, "client_readonly", "%d", 1); else format_add(ft, "client_readonly", "%d", 0); + + s = c->session; + if (s != NULL) + format_add(ft, "client_session", "%s", s->name); + s = c->last_session; + if (s != NULL && session_alive(s)) + format_add(ft, "client_last_session", "%s", s->name); } /* Set default format keys for a winlink. */ @@ -356,6 +369,28 @@ format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) free(layout); } +/* Add window pane tabs. */ +void +format_window_pane_tabs(struct format_tree *ft, struct window_pane *wp) +{ + struct evbuffer *buffer; + u_int i; + + buffer = evbuffer_new(); + for (i = 0; i < wp->base.grid->sx; i++) { + if (!bit_test(wp->base.tabs, i)) + continue; + + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%d", i); + } + + format_add(ft, "pane_tabs", "%.*s", (int) EVBUFFER_LENGTH(buffer), + EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + /* Set default format keys for a window pane. */ void format_window_pane(struct format_tree *ft, struct window_pane *wp) @@ -363,9 +398,9 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) struct grid *gd = wp->base.grid; struct grid_line *gl; unsigned long long size; - u_int i; - u_int idx; + u_int i, idx; const char *cwd; + char *cmd; size = 0; for (i = 0; i < gd->hsize; i++) { @@ -373,31 +408,72 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) size += gl->cellsize * sizeof *gl->celldata; } size += gd->hsize * sizeof *gd->linedata; + format_add(ft, "history_size", "%u", gd->hsize); + format_add(ft, "history_limit", "%u", gd->hlimit); + format_add(ft, "history_bytes", "%llu", size); if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); + format_add(ft, "pane_index", "%u", idx); format_add(ft, "pane_width", "%u", wp->sx); format_add(ft, "pane_height", "%u", wp->sy); format_add(ft, "pane_title", "%s", wp->base.title); - format_add(ft, "pane_index", "%u", idx); - format_add(ft, "history_size", "%u", gd->hsize); - format_add(ft, "history_limit", "%u", gd->hlimit); - format_add(ft, "history_bytes", "%llu", size); format_add(ft, "pane_id", "%%%u", wp->id); format_add(ft, "pane_active", "%d", wp == wp->window->active); format_add(ft, "pane_dead", "%d", wp->fd == -1); + + format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); + + if (wp->tty != NULL) + format_add(ft, "pane_tty", "%s", wp->tty); + format_add(ft, "pane_pid", "%ld", (long) wp->pid); if (wp->cmd != NULL) format_add(ft, "pane_start_command", "%s", wp->cmd); if (wp->cwd != NULL) format_add(ft, "pane_start_path", "%s", wp->cwd); if ((cwd = osdep_get_cwd(wp->fd)) != NULL) format_add(ft, "pane_current_path", "%s", cwd); - format_add(ft, "pane_pid", "%ld", (long) wp->pid); - if (wp->tty != NULL) - format_add(ft, "pane_tty", "%s", wp->tty); + if ((cmd = get_proc_name(wp->fd, wp->tty)) != NULL) { + format_add(ft, "pane_current_command", "%s", cmd); + free(cmd); + } + + format_add(ft, "cursor_x", "%d", wp->base.cx); + format_add(ft, "cursor_y", "%d", wp->base.cy); + format_add(ft, "scroll_region_upper", "%d", wp->base.rupper); + format_add(ft, "scroll_region_lower", "%d", wp->base.rlower); + format_add(ft, "saved_cursor_x", "%d", wp->ictx.old_cx); + format_add(ft, "saved_cursor_y", "%d", wp->ictx.old_cy); + + format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); + format_add(ft, "alternate_saved_x", "%d", wp->saved_cx); + format_add(ft, "alternate_saved_y", "%d", wp->saved_cy); + + format_add(ft, "cursor_flag", "%d", + !!(wp->base.mode & MODE_CURSOR)); + format_add(ft, "insert_flag", "%d", + !!(wp->base.mode & MODE_INSERT)); + format_add(ft, "keypad_cursor_flag", "%d", + !!(wp->base.mode & MODE_KCURSOR)); + format_add(ft, "keypad_flag", "%d", + !!(wp->base.mode & MODE_KKEYPAD)); + format_add(ft, "wrap_flag", "%d", + !!(wp->base.mode & MODE_WRAP)); + + format_add(ft, "mouse_standard_flag", "%d", + !!(wp->base.mode & MODE_MOUSE_STANDARD)); + format_add(ft, "mouse_button_flag", "%d", + !!(wp->base.mode & MODE_MOUSE_BUTTON)); + format_add(ft, "mouse_any_flag", "%d", + !!(wp->base.mode & MODE_MOUSE_ANY)); + format_add(ft, "mouse_utf8_flag", "%d", + !!(wp->base.mode & MODE_MOUSE_UTF8)); + + format_window_pane_tabs(ft, wp); } +/* Set default format keys for paste buffer. */ void format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) { diff --git a/grid-view.c b/grid-view.c index b4355413..7ef443a3 100644 --- a/grid-view.c +++ b/grid-view.c @@ -234,5 +234,5 @@ grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) px = grid_view_x(gd, px); py = grid_view_y(gd, py); - return (grid_string_cells(gd, px, py, nx)); + return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0)); } diff --git a/grid.c b/grid.c index aabf66cb..2955e8ba 100644 --- a/grid.c +++ b/grid.c @@ -70,6 +70,15 @@ grid_check_y(struct grid *gd, u_int py) } #endif +void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); +void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, + u_int); +void grid_reflow_move(struct grid *, u_int *, struct grid_line *); +size_t grid_string_cells_fg(const struct grid_cell *, int *); +size_t grid_string_cells_bg(const struct grid_cell *, int *); +void grid_string_cells_code(const struct grid_cell *, + const struct grid_cell *, char *, size_t, int); + /* Create a new grid. */ struct grid * grid_create(u_int sx, u_int sy, u_int hlimit) @@ -225,6 +234,15 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx) gl->cellsize = sx; } +/* Peek at grid line. */ +const struct grid_line * +grid_peek_line(struct grid *gd, u_int py) +{ + if (grid_check_y(gd, py) != 0) + return (NULL); + return (&gd->linedata[py]); +} + /* Get cell for reading. */ const struct grid_cell * grid_peek_cell(struct grid *gd, u_int px, u_int py) @@ -387,18 +405,201 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) } } +/* Get ANSI foreground sequence. */ +size_t +grid_string_cells_fg(const struct grid_cell *gc, int *values) +{ + size_t n; + + n = 0; + if (gc->flags & GRID_FLAG_FG256) { + values[n++] = 38; + values[n++] = 5; + values[n++] = gc->fg; + } else { + switch (gc->fg) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + values[n++] = gc->fg + 30; + break; + case 8: + values[n++] = 39; + break; + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + values[n++] = gc->fg; + break; + } + } + return (n); +} + +/* Get ANSI background sequence. */ +size_t +grid_string_cells_bg(const struct grid_cell *gc, int *values) +{ + size_t n; + + n = 0; + if (gc->flags & GRID_FLAG_BG256) { + values[n++] = 48; + values[n++] = 5; + values[n++] = gc->bg; + } else { + switch (gc->bg) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + values[n++] = gc->bg + 40; + break; + case 8: + values[n++] = 49; + break; + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + values[n++] = gc->bg - 10; + break; + } + } + return (n); +} + +/* + * Returns ANSI code to set particular attributes (colour, bold and so on) + * given a current state. The output buffer must be able to hold at least 57 + * bytes. + */ +void +grid_string_cells_code(const struct grid_cell *lastgc, + const struct grid_cell *gc, char *buf, size_t len, int escape_c0) +{ + int oldc[16], newc[16], s[32]; + size_t noldc, nnewc, n, i; + u_int attr = gc->attr; + u_int lastattr = lastgc->attr; + char tmp[64]; + + struct { + u_int mask; + u_int code; + } attrs[] = { + { GRID_ATTR_BRIGHT, 1 }, + { GRID_ATTR_DIM, 2 }, + { GRID_ATTR_ITALICS, 3 }, + { GRID_ATTR_UNDERSCORE, 4 }, + { GRID_ATTR_BLINK, 5 }, + { GRID_ATTR_REVERSE, 7 }, + { GRID_ATTR_HIDDEN, 8 } + }; + n = 0; + + /* If any attribute is removed, begin with 0. */ + for (i = 0; i < nitems(attrs); i++) { + if (!(attr & attrs[i].mask) && (lastattr & attrs[i].mask)) { + s[n++] = 0; + lastattr &= GRID_ATTR_CHARSET; + break; + } + } + /* For each attribute that is newly set, add its code. */ + for (i = 0; i < nitems(attrs); i++) { + if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask)) + s[n++] = attrs[i].code; + } + + /* If the foreground c changed, append its parameters. */ + nnewc = grid_string_cells_fg(gc, newc); + noldc = grid_string_cells_fg(lastgc, oldc); + if (nnewc != noldc || + memcmp(newc,oldc, nnewc * sizeof newc[0]) != 0) { + for (i = 0; i < nnewc; i++) + s[n++] = newc[i]; + } + + /* If the background c changed, append its parameters. */ + nnewc = grid_string_cells_bg(gc, newc); + noldc = grid_string_cells_bg(lastgc, oldc); + if (nnewc != noldc || + memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) { + for (i = 0; i < nnewc; i++) + s[n++] = newc[i]; + } + + /* If there are any parameters, append an SGR code. */ + *buf = '\0'; + if (n > 0) { + if (escape_c0) + strlcat(buf, "\\033[", len); + else + strlcat(buf, "\033[", len); + for (i = 0; i < n; i++) { + if (i + 1 < n) + xsnprintf(tmp, sizeof tmp, "%d;", s[i]); + else + xsnprintf(tmp, sizeof tmp, "%d", s[i]); + strlcat(buf, tmp, len); + } + strlcat(buf, "m", len); + } + + /* Append shift in/shift out if needed. */ + if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) { + if (escape_c0) + strlcat(buf, "\\016", len); /* SO */ + else + strlcat(buf, "\016", len); /* SO */ + } + if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) { + if (escape_c0) + strlcat(buf, "\\017", len); /* SI */ + else + strlcat(buf, "\017", len); /* SI */ + } +} + /* Convert cells into a string. */ char * -grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) +grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, + struct grid_cell **lastgc, int with_codes, int escape_c0, int trim) { const struct grid_cell *gc; + static struct grid_cell lastgc1; struct utf8_data ud; - char *buf; - size_t len, off; + const char* data; + char *buf, code[128]; + size_t len, off, size, codelen; u_int xx; GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); + if (lastgc != NULL && *lastgc == NULL) { + memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); + *lastgc = &lastgc1; + } + len = 128; buf = xmalloc(len); off = 0; @@ -409,18 +610,40 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) continue; grid_cell_get(gc, &ud); - while (len < off + ud.size + 1) { + if (with_codes) { + grid_string_cells_code(*lastgc, gc, code, sizeof code, + escape_c0); + codelen = strlen(code); + memcpy(*lastgc, gc, sizeof *gc); + } else + codelen = 0; + + data = ud.data; + size = ud.size; + if (escape_c0 && size == 1 && *data == '\\') { + data = "\\\\"; + size = 2; + } + + while (len < off + size + codelen + 1) { buf = xrealloc(buf, 2, len); len *= 2; } - memcpy(buf + off, ud.data, ud.size); - off += ud.size; + if (codelen != 0) { + memcpy(buf + off, code, codelen); + off += codelen; + } + memcpy(buf + off, data, size); + off += size; } - while (off > 0 && buf[off - 1] == ' ') - off--; + if (trim) { + while (off > 0 && buf[off - 1] == ' ') + off--; + } buf[off] = '\0'; + return (buf); } @@ -461,43 +684,135 @@ grid_duplicate_lines( } } +/* Join line data. */ +void +grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, + u_int new_x) +{ + struct grid_line *dst_gl = &dst->linedata[(*py) - 1]; + u_int left, to_copy, ox, nx; + + /* How much is left on the old line? */ + left = new_x - dst_gl->cellsize; + + /* Work out how much to append. */ + to_copy = src_gl->cellsize; + if (to_copy > left) + to_copy = left; + ox = dst_gl->cellsize; + nx = ox + to_copy; + + /* Resize the destination line. */ + dst_gl->celldata = xrealloc(dst_gl->celldata, nx, + sizeof *dst_gl->celldata); + dst_gl->cellsize = nx; + + /* Append as much as possible. */ + memcpy(&dst_gl->celldata[ox], &src_gl->celldata[0], + to_copy * sizeof src_gl->celldata[0]); + + /* If there is any left in the source, split it. */ + if (src_gl->cellsize > to_copy) { + dst_gl->flags |= GRID_LINE_WRAPPED; + + src_gl->cellsize -= to_copy; + grid_reflow_split(dst, py, src_gl, new_x, to_copy); + } +} + +/* Split line data. */ +void +grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl, + u_int new_x, u_int offset) +{ + struct grid_line *dst_gl = NULL; + u_int to_copy; + + /* Loop and copy sections of the source line. */ + while (src_gl->cellsize > 0) { + /* Create new line. */ + if (*py >= dst->hsize + dst->sy) + grid_scroll_history(dst); + dst_gl = &dst->linedata[*py]; + (*py)++; + + /* How much should we copy? */ + to_copy = new_x; + if (to_copy > src_gl->cellsize) + to_copy = src_gl->cellsize; + + /* Expand destination line. */ + dst_gl->celldata = xmalloc(to_copy * sizeof *dst_gl->celldata); + dst_gl->cellsize = to_copy; + dst_gl->flags |= GRID_LINE_WRAPPED; + + /* Copy the data. */ + memcpy (&dst_gl->celldata[0], &src_gl->celldata[offset], + to_copy * sizeof dst_gl->celldata[0]); + + /* Move offset and reduce old line size. */ + offset += to_copy; + src_gl->cellsize -= to_copy; + } + + /* Last line is not wrapped. */ + if (dst_gl != NULL) + dst_gl->flags &= ~GRID_LINE_WRAPPED; +} + +/* Move line data. */ +void +grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl) +{ + struct grid_line *dst_gl; + + /* Create new line. */ + if (*py >= dst->hsize + dst->sy) + grid_scroll_history(dst); + dst_gl = &dst->linedata[*py]; + (*py)++; + + /* Copy the old line. */ + memcpy(dst_gl, src_gl, sizeof *dst_gl); + dst_gl->flags &= ~GRID_LINE_WRAPPED; + + /* Clear old line. */ + src_gl->celldata = NULL; +} + /* - * Reflow lines from src grid into dst grid based on width sx. Returns number - * of lines fewer in the visible area, or zero. + * Reflow lines from src grid into dst grid of width new_x. Returns number of + * lines fewer in the visible area. The source grid is destroyed. */ u_int -grid_reflow(struct grid *dst, const struct grid *src, u_int sx) +grid_reflow(struct grid *dst, struct grid *src, u_int new_x) { - u_int px, py, line, cell; + u_int py, sy, line; int previous_wrapped; - struct grid_line *gl; + struct grid_line *src_gl; - px = py = 0; - previous_wrapped = 1; - for (line = 0; line < src->sy + src->hsize; line++) { - gl = src->linedata + line; + py = 0; + sy = src->sy; + + previous_wrapped = 0; + for (line = 0; line < sy + src->hsize; line++) { + src_gl = src->linedata + line; if (!previous_wrapped) { - px = 0; - py++; - if (py >= dst->hsize + dst->sy) - grid_scroll_history(dst); + /* Wasn't wrapped. If smaller, move to destination. */ + if (src_gl->cellsize <= new_x) + grid_reflow_move(dst, &py, src_gl); + else + grid_reflow_split(dst, &py, src_gl, new_x, 0); + } else { + /* Previous was wrapped. Try to join. */ + grid_reflow_join(dst, &py, src_gl, new_x); } - for (cell = 0; cell < gl->cellsize; cell++) { - if (px == sx) { - dst->linedata[py].flags |= GRID_LINE_WRAPPED; - px = 0; - py++; - if (py >= dst->hsize + dst->sy) - grid_scroll_history(dst); - } - grid_set_cell(dst, px, py, gl->celldata + cell); - px++; - } - previous_wrapped = gl->flags & GRID_LINE_WRAPPED; + previous_wrapped = src_gl->flags & GRID_LINE_WRAPPED; } - py++; /* account for final line, which never wraps */ - if (py > src->sy) + grid_destroy(src); + + if (py > sy) return (0); - return (src->sy - py); + return (sy - py); } diff --git a/input-keys.c b/input-keys.c index 0953ce7e..faa7bd17 100644 --- a/input-keys.c +++ b/input-keys.c @@ -201,12 +201,26 @@ input_key(struct window_pane *wp, int key) void input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) { - char buf[10]; + char buf[40]; size_t len; struct paste_buffer *pb; if (wp->screen->mode & ALL_MOUSE_MODES) { - if (wp->screen->mode & MODE_MOUSE_UTF8) { + /* + * Use the SGR (1006) extension only if the application + * requested it and the underlying terminal also sent the event + * in this format (this is because an old style mouse release + * event cannot be converted into the new SGR format, since the + * released button is unknown). Otherwise pretend that tmux + * doesn't speak this extension, and fall back to the UTF-8 + * (1005) extension if the application requested, or to the + * legacy format. + */ + if (m->sgr && (wp->screen->mode & MODE_MOUSE_SGR)) { + len = xsnprintf(buf, sizeof buf, "\033[<%d;%d;%d%c", + m->sgr_xb, m->x + 1, m->y + 1, + m->sgr_rel ? 'm' : 'M'); + } else if (wp->screen->mode & MODE_MOUSE_UTF8) { len = xsnprintf(buf, sizeof buf, "\033[M"); len += utf8_split2(m->xb + 32, &buf[len]); len += utf8_split2(m->x + 33, &buf[len]); diff --git a/input.c b/input.c index 3b8d76b0..4aa02e90 100644 --- a/input.c +++ b/input.c @@ -1033,10 +1033,10 @@ input_esc_dispatch(struct input_ctx *ictx) screen_write_reverseindex(sctx); break; case INPUT_ESC_DECKPAM: - screen_write_kkeypadmode(sctx, 1); + screen_write_mode_set(sctx, MODE_KKEYPAD); break; case INPUT_ESC_DECKPNM: - screen_write_kkeypadmode(sctx, 0); + screen_write_mode_clear(sctx, MODE_KKEYPAD); break; case INPUT_ESC_DECSC: memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); @@ -1232,7 +1232,7 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_RM: switch (input_get(ictx, 0, 0, -1)) { case 4: /* IRM */ - screen_write_insertmode(&ictx->ctx, 0); + screen_write_mode_clear(&ictx->ctx, MODE_INSERT); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -1242,23 +1242,32 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_RM_PRIVATE: switch (input_get(ictx, 0, 0, -1)) { case 1: /* GATM */ - screen_write_kcursormode(&ictx->ctx, 0); + screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ screen_write_cursormove(&ictx->ctx, 0, 0); screen_write_clearscreen(&ictx->ctx); break; + case 7: /* DECAWM */ + screen_write_mode_clear(&ictx->ctx, MODE_WRAP); + break; case 25: /* TCEM */ - screen_write_cursormode(&ictx->ctx, 0); + screen_write_mode_clear(&ictx->ctx, MODE_CURSOR); break; case 1000: case 1001: case 1002: case 1003: - screen_write_mousemode_off(&ictx->ctx); + screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); + break; + case 1004: + screen_write_mode_clear(&ictx->ctx, MODE_FOCUSON); break; case 1005: - screen_write_utf8mousemode(&ictx->ctx, 0); + screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); + break; + case 1006: + screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR); break; case 47: case 1047: @@ -1268,7 +1277,7 @@ input_csi_dispatch(struct input_ctx *ictx) window_pane_alternate_off(wp, &ictx->cell, 1); break; case 2004: - screen_write_bracketpaste(&ictx->ctx, 0); + screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -1286,7 +1295,7 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_SM: switch (input_get(ictx, 0, 0, -1)) { case 4: /* IRM */ - screen_write_insertmode(&ictx->ctx, 1); + screen_write_mode_set(&ictx->ctx, MODE_INSERT); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -1296,28 +1305,41 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_SM_PRIVATE: switch (input_get(ictx, 0, 0, -1)) { case 1: /* GATM */ - screen_write_kcursormode(&ictx->ctx, 1); + screen_write_mode_set(&ictx->ctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ screen_write_cursormove(&ictx->ctx, 0, 0); screen_write_clearscreen(&ictx->ctx); break; + case 7: /* DECAWM */ + screen_write_mode_set(&ictx->ctx, MODE_WRAP); + break; case 25: /* TCEM */ - screen_write_cursormode(&ictx->ctx, 1); + screen_write_mode_set(&ictx->ctx, MODE_CURSOR); break; case 1000: - screen_write_mousemode_on( - &ictx->ctx, MODE_MOUSE_STANDARD); + screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_STANDARD); break; case 1002: - screen_write_mousemode_on( - &ictx->ctx, MODE_MOUSE_BUTTON); + screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_BUTTON); break; case 1003: - screen_write_mousemode_on(&ictx->ctx, MODE_MOUSE_ANY); + screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_ANY); + break; + case 1004: + if (s->mode & MODE_FOCUSON) + break; + screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); + wp->flags &= ~PANE_FOCUSED; /* force update if needed */ break; case 1005: - screen_write_utf8mousemode(&ictx->ctx, 1); + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); + break; + case 1006: + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR); break; case 47: case 1047: @@ -1327,7 +1349,7 @@ input_csi_dispatch(struct input_ctx *ictx) window_pane_alternate_on(wp, &ictx->cell, 1); break; case 2004: - screen_write_bracketpaste(&ictx->ctx, 1); + screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); diff --git a/job.c b/job.c index c648eae0..6a7286ae 100644 --- a/job.c +++ b/job.c @@ -32,13 +32,14 @@ */ void job_callback(struct bufferevent *, short, void *); +void job_write_callback(struct bufferevent *, void *); /* All jobs list. */ struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); /* Start a job running, if it isn't already. */ struct job * -job_run(const char *cmd, +job_run(const char *cmd, struct session *s, void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) { struct job *job; @@ -51,7 +52,9 @@ job_run(const char *cmd, environ_init(&env); environ_copy(&global_environ, &env); - server_fill_environ(NULL, &env); + if (s != NULL) + environ_copy(&s->environ, &env); + server_fill_environ(s, &env); switch (pid = fork()) { case -1: @@ -63,20 +66,20 @@ job_run(const char *cmd, environ_push(&env); environ_free(&env); + if (dup2(out[1], STDIN_FILENO) == -1) + fatal("dup2 failed"); if (dup2(out[1], STDOUT_FILENO) == -1) fatal("dup2 failed"); - if (out[1] != STDOUT_FILENO) + if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) close(out[1]); close(out[0]); nullfd = open(_PATH_DEVNULL, O_RDWR, 0); if (nullfd < 0) fatal("open failed"); - if (dup2(nullfd, STDIN_FILENO) == -1) - fatal("dup2 failed"); if (dup2(nullfd, STDERR_FILENO) == -1) fatal("dup2 failed"); - if (nullfd != STDIN_FILENO && nullfd != STDERR_FILENO) + if (nullfd != STDERR_FILENO) close(nullfd); closefrom(STDERR_FILENO + 1); @@ -103,7 +106,8 @@ job_run(const char *cmd, job->fd = out[0]; setblocking(job->fd, 0); - job->event = bufferevent_new(job->fd, NULL, NULL, job_callback, job); + job->event = bufferevent_new(job->fd, NULL, job_write_callback, + job_callback, job); bufferevent_enable(job->event, EV_READ); log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); @@ -132,8 +136,23 @@ job_free(struct job *job) free(job); } +/* Called when output buffer falls below low watermark (default is 0). */ +void +job_write_callback(unused struct bufferevent *bufev, void *data) +{ + struct job *job = data; + size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); + + log_debug("job write %p: %s, pid %ld, output left %lu", job, job->cmd, + (long) job->pid, (unsigned long) len); + + if (len == 0) { + shutdown(job->fd, SHUT_WR); + bufferevent_disable(job->event, EV_WRITE); + } +} + /* Job buffer error callback. */ -/* ARGSUSED */ void job_callback(unused struct bufferevent *bufev, unused short events, void *data) { diff --git a/key-bindings.c b/key-bindings.c index cf5237d4..86048ea6 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -150,6 +150,7 @@ key_bindings_init(void) { 't', 0, &cmd_clock_mode_entry }, { 'w', 0, &cmd_choose_window_entry }, { 'x', 0, &cmd_confirm_before_entry }, + { 'z', 0, &cmd_resize_pane_entry }, { '{', 0, &cmd_swap_pane_entry }, { '}', 0, &cmd_swap_pane_entry }, { '~', 0, &cmd_show_messages_entry }, @@ -182,11 +183,11 @@ key_bindings_init(void) RB_INIT(&key_bindings); for (i = 0; i < nitems(table); i++) { - cmdlist = xmalloc(sizeof *cmdlist); - TAILQ_INIT(&cmdlist->list); + cmdlist = xcalloc(1, sizeof *cmdlist); cmdlist->references = 1; + TAILQ_INIT(&cmdlist->list); - cmd = xmalloc(sizeof *cmd); + cmd = xcalloc(1, sizeof *cmd); cmd->entry = table[i].entry; if (cmd->entry->key_binding != NULL) cmd->entry->key_binding(cmd, table[i].key); @@ -199,91 +200,21 @@ key_bindings_init(void) } } -void printflike2 -key_bindings_error(struct cmd_ctx *ctx, const char *fmt, ...) -{ - va_list ap; - char *msg; - - if (ctx->curclient->session == NULL) - return; - - va_start(ap, fmt); - xvasprintf(&msg, fmt, ap); - va_end(ap); - - *msg = toupper((u_char) *msg); - status_message_set(ctx->curclient, "%s", msg); - free(msg); -} - -void printflike2 -key_bindings_print(struct cmd_ctx *ctx, const char *fmt, ...) -{ - struct winlink *wl; - va_list ap; - - if (ctx->curclient->session == NULL) - return; - - wl = ctx->curclient->session->curw; - if (wl->window->active->mode != &window_copy_mode) { - window_pane_reset_mode(wl->window->active); - window_pane_set_mode(wl->window->active, &window_copy_mode); - window_copy_init_for_output(wl->window->active); - } - - va_start(ap, fmt); - window_copy_vadd(wl->window->active, fmt, ap); - va_end(ap); -} - -void printflike2 -key_bindings_info(struct cmd_ctx *ctx, const char *fmt, ...) -{ - va_list ap; - char *msg; - - if (ctx->curclient->session == NULL) - return; - - if (options_get_number(&global_options, "quiet")) - return; - - va_start(ap, fmt); - xvasprintf(&msg, fmt, ap); - va_end(ap); - - *msg = toupper((u_char) *msg); - status_message_set(ctx->curclient, "%s", msg); - free(msg); -} - void key_bindings_dispatch(struct key_binding *bd, struct client *c) { - struct cmd_ctx ctx; struct cmd *cmd; int readonly; - ctx.msgdata = NULL; - ctx.curclient = c; - - ctx.error = key_bindings_error; - ctx.print = key_bindings_print; - ctx.info = key_bindings_info; - - ctx.cmdclient = NULL; - readonly = 1; TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { if (!(cmd->entry->flags & CMD_READONLY)) readonly = 0; } - if (!readonly && c->flags & CLIENT_READONLY) { - key_bindings_info(&ctx, "Client is read-only"); + if (!readonly && (c->flags & CLIENT_READONLY)) { + cmdq_info(c->cmdq, "client is read-only"); return; } - cmd_list_exec(bd->cmdlist, &ctx); + cmdq_run(c->cmdq, bd->cmdlist); } diff --git a/key-string.c b/key-string.c index df177399..797eedd5 100644 --- a/key-string.c +++ b/key-string.c @@ -136,9 +136,10 @@ key_string_get_modifiers(const char **string) int key_string_lookup_string(const char *string) { - int key, modifiers; - u_short u; - int size; + static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; + int key, modifiers; + u_short u; + int size; /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { @@ -170,7 +171,7 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (key < KEYC_BASE && (modifiers & KEYC_CTRL)) { + if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && !strchr(other, key)) { if (key >= 97 && key <= 122) key -= 96; else if (key >= 64 && key <= 95) @@ -193,7 +194,7 @@ key_string_lookup_key(int key) { static char out[24]; char tmp[8]; - u_int i; + u_int i; *out = '\0'; diff --git a/layout-custom.c b/layout-custom.c index c076232f..e32d9d9d 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -63,7 +63,7 @@ layout_dump(struct window *w) if (layout_append(w->layout_root, layout, sizeof layout) != 0) return (NULL); - xasprintf(&out, "%4x,%s", layout_checksum(layout), layout); + xasprintf(&out, "%04x,%s", layout_checksum(layout), layout); return (out); } @@ -206,11 +206,11 @@ layout_construct(struct layout_cell *lcparent, const char **layout) { struct layout_cell *lc, *lcchild; u_int sx, sy, xoff, yoff; + const char *saved; if (!isdigit((u_char) **layout)) return (NULL); - if (sscanf(*layout, "%ux%u,%u,%u,%*u", &sx, &sy, &xoff, &yoff) != 4 && - sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4) + if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4) return (NULL); while (isdigit((u_char) **layout)) @@ -231,9 +231,12 @@ layout_construct(struct layout_cell *lcparent, const char **layout) while (isdigit((u_char) **layout)) (*layout)++; if (**layout == ',') { + saved = *layout; (*layout)++; while (isdigit((u_char) **layout)) (*layout)++; + if (**layout == 'x') + *layout = saved; } lc = layout_create_cell(lcparent); diff --git a/layout.c b/layout.c index 397d90e5..b74bd789 100644 --- a/layout.c +++ b/layout.c @@ -374,13 +374,13 @@ layout_destroy_cell(struct layout_cell *lc, struct layout_cell **lcroot) } void -layout_init(struct window *w) +layout_init(struct window *w, struct window_pane *wp) { struct layout_cell *lc; lc = w->layout_root = layout_create_cell(NULL); layout_set_size(lc, w->sx, w->sy, 0, 0); - layout_make_leaf(lc, TAILQ_FIRST(&w->panes)); + layout_make_leaf(lc, wp); layout_fix_panes(w, w->sx, w->sy); } @@ -443,6 +443,39 @@ layout_resize(struct window *w, u_int sx, u_int sy) layout_fix_panes(w, sx, sy); } +/* Resize a pane to an absolute size. */ +void +layout_resize_pane_to(struct window_pane *wp, enum layout_type type, + u_int new_size) +{ + struct layout_cell *lc, *lcparent; + int change, size; + + lc = wp->layout_cell; + + /* Find next parent of the same type. */ + lcparent = lc->parent; + while (lcparent != NULL && lcparent->type != type) { + lc = lcparent; + lcparent = lc->parent; + } + if (lcparent == NULL) + return; + + /* Work out the size adjustment. */ + if (type == LAYOUT_LEFTRIGHT) + size = lc->sx; + else + size = lc->sy; + if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) + change = size - new_size; + else + change = new_size - size; + + /* Resize the pane. */ + layout_resize_pane(wp, type, change); +} + /* Resize a single pane within the layout. */ void layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) @@ -486,6 +519,7 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) notify_window_layout_changed(wp->window); } +/* Resize pane based on mouse events. */ void layout_resize_pane_mouse(struct client *c) { @@ -534,6 +568,7 @@ layout_resize_pane_mouse(struct client *c) m->flags &= ~MOUSE_RESIZE_PANE; } +/* Helper function to grow pane. */ int layout_resize_pane_grow( struct layout_cell *lc, enum layout_type type, int needed) @@ -574,6 +609,7 @@ layout_resize_pane_grow( return (size); } +/* Helper function to shrink pane. */ int layout_resize_pane_shrink( struct layout_cell *lc, enum layout_type type, int needed) diff --git a/mode-key.c b/mode-key.c index 7dea26d8..94115ebb 100644 --- a/mode-key.c +++ b/mode-key.c @@ -99,6 +99,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { { MODEKEYCOPY_BOTTOMLINE, "bottom-line" }, { MODEKEYCOPY_CANCEL, "cancel" }, { MODEKEYCOPY_CLEARSELECTION, "clear-selection" }, + { MODEKEYCOPY_COPYPIPE, "copy-pipe" }, { MODEKEYCOPY_COPYLINE, "copy-line" }, { MODEKEYCOPY_COPYENDOFLINE, "copy-end-of-line" }, { MODEKEYCOPY_COPYSELECTION, "copy-selection" }, @@ -413,7 +414,6 @@ const struct mode_key_entry mode_key_emacs_copy[] = { { '\026' /* C-v */, 0, MODEKEYCOPY_NEXTPAGE }, { '\027' /* C-w */, 0, MODEKEYCOPY_COPYSELECTION }, { '\033' /* Escape */, 0, MODEKEYCOPY_CANCEL }, - { 'N', 0, MODEKEYCOPY_SEARCHREVERSE }, { 'b' | KEYC_ESCAPE, 0, MODEKEYCOPY_PREVIOUSWORD }, { 'f', 0, MODEKEYCOPY_JUMP }, { 'f' | KEYC_ESCAPE, 0, MODEKEYCOPY_NEXTWORDEND }, @@ -514,6 +514,7 @@ mode_key_init_trees(void) mbind->key = ment->key; mbind->mode = ment->mode; mbind->cmd = ment->cmd; + mbind->arg = NULL; RB_INSERT(mode_key_tree, mtab->tree, mbind); } } @@ -527,7 +528,7 @@ mode_key_init(struct mode_key_data *mdata, struct mode_key_tree *mtree) } enum mode_key_cmd -mode_key_lookup(struct mode_key_data *mdata, int key) +mode_key_lookup(struct mode_key_data *mdata, int key, const char **arg) { struct mode_key_binding *mbind, mtmp; @@ -547,6 +548,8 @@ mode_key_lookup(struct mode_key_data *mdata, int key) mdata->mode = 1 - mdata->mode; /* FALLTHROUGH */ default: + if (arg != NULL) + *arg = mbind->arg; return (mbind->cmd); } } diff --git a/names.c b/names.c index 72f1ad17..bcb6aafe 100644 --- a/names.c +++ b/names.c @@ -26,8 +26,8 @@ #include "tmux.h" -void window_name_callback(unused int, unused short, void *); -char *parse_window_name(const char *); +void window_name_callback(int, short, void *); +char *parse_window_name(struct window *, const char *); void queue_window_name(struct window *w) @@ -43,7 +43,6 @@ queue_window_name(struct window *w) evtimer_add(&w->name_timer, &tv); } -/* ARGSUSED */ void window_name_callback(unused int fd, unused short events, void *data) { @@ -74,9 +73,9 @@ window_name_callback(unused int fd, unused short events, void *data) */ if (w->active->cmd != NULL && *w->active->cmd == '\0' && name != NULL && name[0] == '-' && name[1] != '\0') - wname = parse_window_name(name + 1); + wname = parse_window_name(w, name + 1); else - wname = parse_window_name(name); + wname = parse_window_name(w, name); free(name); } @@ -99,18 +98,22 @@ default_window_name(struct window *w) if (w->active->screen != &w->active->base) return (xstrdup("[tmux]")); if (w->active->cmd != NULL && *w->active->cmd != '\0') - return (parse_window_name(w->active->cmd)); - return (parse_window_name(w->active->shell)); + return (parse_window_name(w, w->active->cmd)); + return (parse_window_name(w, w->active->shell)); } char * -parse_window_name(const char *in) +parse_window_name(struct window *w, const char *in) { - char *copy, *name, *ptr; + char *copy, *name, *ptr, *prefix; + size_t prefixlen; + + prefix = options_get_string(&w->options, "command-prefix"); + prefixlen = strlen(prefix); name = copy = xstrdup(in); - if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) - name = name + (sizeof "exec ") - 1; + if (strncmp(name, prefix, prefixlen) == 0) + name = name + prefixlen; while (*name == ' ') name++; diff --git a/options-table.c b/options-table.c index 83ec97f2..e5f6c777 100644 --- a/options-table.c +++ b/options-table.c @@ -476,7 +476,6 @@ const struct options_table_entry window_options_table[] = { .default_num = 1 }, - { .name = "c0-change-trigger", .type = OPTIONS_TABLE_NUMBER, .default_num = 250, @@ -502,6 +501,11 @@ const struct options_table_entry window_options_table[] = { .default_num = 1 }, + { .name = "command-prefix", + .type = OPTIONS_TABLE_STRING, + .default_str = "exec " + }, + { .name = "force-height", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, @@ -745,8 +749,8 @@ options_table_populate_tree( /* Print an option using its type from the table. */ const char * -options_table_print_entry( - const struct options_table_entry *oe, struct options_entry *o) +options_table_print_entry(const struct options_table_entry *oe, + struct options_entry *o, int no_quotes) { static char out[BUFSIZ]; const char *s; @@ -754,13 +758,17 @@ options_table_print_entry( *out = '\0'; switch (oe->type) { case OPTIONS_TABLE_STRING: - xsnprintf(out, sizeof out, "\"%s\"", o->str); + if (no_quotes) + xsnprintf(out, sizeof out, "%s", o->str); + else + xsnprintf(out, sizeof out, "\"%s\"", o->str); break; case OPTIONS_TABLE_NUMBER: xsnprintf(out, sizeof out, "%lld", o->num); break; case OPTIONS_TABLE_KEY: - xsnprintf(out, sizeof out, "%s", key_string_lookup_key(o->num)); + xsnprintf(out, sizeof out, "%s", + key_string_lookup_key(o->num)); break; case OPTIONS_TABLE_COLOUR: s = colour_tostring(o->num); diff --git a/resize.c b/resize.c index 317110e3..5c365dfe 100644 --- a/resize.c +++ b/resize.c @@ -49,10 +49,12 @@ recalculate_sizes(void) struct client *c; struct window *w; struct window_pane *wp; - u_int i, j, ssx, ssy, has, limit; - int flag; + u_int i, j, ssx, ssy, has, limit; + int flag, has_status, is_zoomed; RB_FOREACH(s, sessions, &sessions) { + has_status = options_get_number(&s->options, "status"); + ssx = ssy = UINT_MAX; for (j = 0; j < ARRAY_LENGTH(&clients); j++) { c = ARRAY_ITEM(&clients, j); @@ -61,7 +63,11 @@ recalculate_sizes(void) if (c->session == s) { if (c->tty.sx < ssx) ssx = c->tty.sx; - if (c->tty.sy < ssy) + if (has_status && + !(c->flags & CLIENT_CONTROL) && + c->tty.sy > 1 && c->tty.sy - 1 < ssy) + ssy = c->tty.sy - 1; + else if (c->tty.sy < ssy) ssy = c->tty.sy; } } @@ -71,17 +77,14 @@ recalculate_sizes(void) } s->flags &= ~SESSION_UNATTACHED; - if (options_get_number(&s->options, "status")) { - if (ssy == 0) - ssy = 1; - else - ssy--; - } + if (has_status && ssy == 0) + ssy = 1; + if (s->sx == ssx && s->sy == ssy) continue; - log_debug( - "session size %u,%u (was %u,%u)", ssx, ssy, s->sx, s->sy); + log_debug("session size %u,%u (was %u,%u)", ssx, ssy, s->sx, + s->sy); s->sx = ssx; s->sy = ssy; @@ -120,12 +123,16 @@ recalculate_sizes(void) if (w->sx == ssx && w->sy == ssy) continue; + log_debug("window size %u,%u (was %u,%u)", ssx, ssy, w->sx, + w->sy); - log_debug( - "window size %u,%u (was %u,%u)", ssx, ssy, w->sx, w->sy); - + is_zoomed = w->flags & WINDOW_ZOOMED; + if (is_zoomed) + window_unzoom(w); layout_resize(w, ssx, ssy); window_resize(w, ssx, ssy); + if (is_zoomed && window_pane_visible(w->active)) + window_zoom(w->active); /* * If the current pane is now not visible, move to the next diff --git a/screen-redraw.c b/screen-redraw.c index 899f741b..a7bf81ff 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -24,7 +24,11 @@ int screen_redraw_cell_border1(struct window_pane *, u_int, u_int); int screen_redraw_cell_border(struct client *, u_int, u_int); -int screen_redraw_check_cell(struct client *, u_int, u_int); +int screen_redraw_check_cell(struct client *, u_int, u_int, + struct window_pane **); +int screen_redraw_check_active(u_int, u_int, int, struct window *, + struct window_pane *); + void screen_redraw_draw_number(struct client *, struct window_pane *); #define CELL_INSIDE 0 @@ -93,7 +97,8 @@ screen_redraw_cell_border(struct client *c, u_int px, u_int py) /* Check if cell inside a pane. */ int -screen_redraw_check_cell(struct client *c, u_int px, u_int py) +screen_redraw_check_cell(struct client *c, u_int px, u_int py, + struct window_pane **wpp) { struct window *w = c->session->curw->window; struct window_pane *wp; @@ -105,6 +110,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py) TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; + *wpp = wp; /* If outside the pane and its border, skip it. */ if ((wp->xoff != 0 && px < wp->xoff - 1) || @@ -162,9 +168,52 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py) } } + *wpp = NULL; return (CELL_OUTSIDE); } +/* Check active pane indicator. */ +int +screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, + struct window_pane *wp) +{ + /* Is this off the active pane border? */ + if (screen_redraw_cell_border1(w->active, px, py) != 1) + return (0); + + /* If there are more than two panes, that's enough. */ + if (window_count_panes(w) != 2) + return (1); + + /* Else if the cell is not a border cell, forget it. */ + if (wp == NULL || (type == CELL_OUTSIDE || type == CELL_INSIDE)) + return (1); + + /* Check if the pane covers the whole width. */ + if (wp->xoff == 0 && wp->sx == w->sx) { + /* This can either be the top pane or the bottom pane. */ + if (wp->yoff == 0) { /* top pane */ + if (wp == w->active) + return (px <= wp->sx / 2); + return (px > wp->sx / 2); + } + return (0); + } + + /* Check if the pane covers the whole height. */ + if (wp->yoff == 0 && wp->sy == w->sy) { + /* This can either be the left pane or the right pane. */ + if (wp->xoff == 0) { /* left pane */ + if (wp == w->active) + return (py <= wp->sy / 2); + return (py > wp->sy / 2); + } + return (0); + } + + return (type); +} + /* Redraw entire screen. */ void screen_redraw_screen(struct client *c, int status_only, int borders_only) @@ -223,10 +272,10 @@ screen_redraw_screen(struct client *c, int status_only, int borders_only) break; } for (i = 0; i < tty->sx; i++) { - type = screen_redraw_check_cell(c, i, j); + type = screen_redraw_check_cell(c, i, j, &wp); if (type == CELL_INSIDE) continue; - if (screen_redraw_cell_border1(w->active, i, j) == 1) + if (screen_redraw_check_active(i, j, type, w, wp)) tty_attributes(tty, &active_gc); else tty_attributes(tty, &other_gc); @@ -273,6 +322,9 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) { u_int i, yoff; + if (!window_pane_visible(wp)) + return; + yoff = wp->yoff; if (status_at_line(c) == 0) yoff++; diff --git a/screen-write.c b/screen-write.c index 04d2b385..3e836938 100644 --- a/screen-write.c +++ b/screen-write.c @@ -41,7 +41,6 @@ screen_write_start( } /* Finish writing. */ -/* ARGSUSED */ void screen_write_stop(unused struct screen_write_ctx *ctx) { @@ -52,14 +51,13 @@ screen_write_stop(unused struct screen_write_ctx *ctx) void screen_write_reset(struct screen_write_ctx *ctx) { - screen_reset_tabs(ctx->s); + struct screen *s = ctx->s; - screen_write_scrollregion(ctx, 0, screen_size_y(ctx->s) - 1); + screen_reset_tabs(s); + screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); - screen_write_insertmode(ctx, 0); - screen_write_kcursormode(ctx, 0); - screen_write_kkeypadmode(ctx, 0); - screen_write_mousemode_off(ctx); + s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD); + s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); screen_write_clearscreen(ctx); screen_write_cursormove(ctx, 0, 0); @@ -454,6 +452,24 @@ screen_write_initctx( memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell); } +/* Set a mode. */ +void +screen_write_mode_set(struct screen_write_ctx *ctx, int mode) +{ + struct screen *s = ctx->s; + + s->mode |= mode; +} + +/* Clear a mode. */ +void +screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) +{ + struct screen *s = ctx->s; + + s->mode &= ~mode; +} + /* Cursor up by ny. */ void screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) @@ -805,18 +821,6 @@ screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py) s->cy = py; } -/* Set cursor mode. */ -void -screen_write_cursormode(struct screen_write_ctx *ctx, int state) -{ - struct screen *s = ctx->s; - - if (state) - s->mode |= MODE_CURSOR; - else - s->mode &= ~MODE_CURSOR; -} - /* Reverse index (up with scroll). */ void screen_write_reverseindex(struct screen_write_ctx *ctx) @@ -856,61 +860,6 @@ screen_write_scrollregion( s->rlower = rlower; } -/* Set insert mode. */ -void -screen_write_insertmode(struct screen_write_ctx *ctx, int state) -{ - struct screen *s = ctx->s; - - if (state) - s->mode |= MODE_INSERT; - else - s->mode &= ~MODE_INSERT; -} - -/* Set UTF-8 mouse mode. */ -void -screen_write_utf8mousemode(struct screen_write_ctx *ctx, int state) -{ - struct screen *s = ctx->s; - - if (state) - s->mode |= MODE_MOUSE_UTF8; - else - s->mode &= ~MODE_MOUSE_UTF8; -} - -/* Set mouse mode off. */ -void -screen_write_mousemode_off(struct screen_write_ctx *ctx) -{ - struct screen *s = ctx->s; - - s->mode &= ~ALL_MOUSE_MODES; -} - -/* Set mouse mode on. */ -void -screen_write_mousemode_on(struct screen_write_ctx *ctx, int mode) -{ - struct screen *s = ctx->s; - - s->mode &= ~ALL_MOUSE_MODES; - s->mode |= mode; -} - -/* Set bracketed paste mode. */ -void -screen_write_bracketpaste(struct screen_write_ctx *ctx, int state) -{ - struct screen *s = ctx->s; - - if (state) - s->mode |= MODE_BRACKETPASTE; - else - s->mode &= ~MODE_BRACKETPASTE; -} - /* Line feed. */ void screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) @@ -945,30 +894,6 @@ screen_write_carriagereturn(struct screen_write_ctx *ctx) s->cx = 0; } -/* Set keypad cursor keys mode. */ -void -screen_write_kcursormode(struct screen_write_ctx *ctx, int state) -{ - struct screen *s = ctx->s; - - if (state) - s->mode |= MODE_KCURSOR; - else - s->mode &= ~MODE_KCURSOR; -} - -/* Set keypad number keys mode. */ -void -screen_write_kkeypadmode(struct screen_write_ctx *ctx, int state) -{ - struct screen *s = ctx->s; - - if (state) - s->mode |= MODE_KKEYPAD; - else - s->mode &= ~MODE_KKEYPAD; -} - /* Clear to end of screen from cursor. */ void screen_write_clearendofscreen(struct screen_write_ctx *ctx) @@ -1054,10 +979,10 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) struct screen *s = ctx->s; struct grid *gd = s->grid; struct tty_ctx ttyctx; - u_int width, xx; + u_int width, xx, last; struct grid_cell tmp_gc, *tmp_gcp; struct utf8_data ud; - int insert = 0; + int insert; /* Ignore padding. */ if (gc->flags & GRID_FLAG_PADDING) @@ -1095,7 +1020,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) xx = screen_size_x(s) - s->cx - width; grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx); insert = 1; - } + } else + insert = 0; /* Check this will fit on the current line and wrap if not. */ if ((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) { @@ -1103,9 +1029,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) s->cx = 0; /* carriage return */ } - /* Sanity checks. */ - if (((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) - || s->cy > screen_size_y(s) - 1) + /* Sanity check cursor position. */ + if (s->cx > screen_size_x(s) - width || s->cy > screen_size_y(s) - 1) return; /* Handle overwriting of UTF-8 characters. */ @@ -1124,8 +1049,15 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) /* Set the cell. */ grid_view_set_cell(gd, s->cx, s->cy, gc); - /* Move the cursor. */ - s->cx += width; + /* + * Move the cursor. If not wrapping, stick at the last character and + * replace it. + */ + last = !(s->mode & MODE_WRAP); + if (s->cx <= screen_size_x(s) - last - width) + s->cx += width; + else + s->cx = screen_size_x(s) - last; /* Draw to the screen if necessary. */ if (insert) { diff --git a/screen.c b/screen.c index fe0b7389..e92c6aa7 100644 --- a/screen.c +++ b/screen.c @@ -363,15 +363,10 @@ screen_check_selection(struct screen *s, u_int px, u_int py) /* Reflow wrapped lines. */ void -screen_reflow(struct screen *s, u_int sx) +screen_reflow(struct screen *s, u_int new_x) { - struct grid *old, *new; + struct grid *old = s->grid; - old = s->grid; - new = grid_create(old->sx, old->sy, old->hlimit); - - s->cy -= grid_reflow(new, old, sx); - s->grid = new; - - grid_destroy(old); + s->grid = grid_create(old->sx, old->sy, old->hlimit); + s->cy -= grid_reflow(s->grid, old, new_x); } diff --git a/server-client.c b/server-client.c index 796390f7..377f9ae9 100644 --- a/server-client.c +++ b/server-client.c @@ -17,9 +17,11 @@ */ #include +#include #include #include +#include #include #include #include @@ -27,6 +29,8 @@ #include "tmux.h" +void server_client_check_focus(struct window_pane *); +void server_client_check_resize(struct window_pane *); void server_client_check_mouse(struct client *, struct window_pane *); void server_client_repeat_timer(int, short, void *); void server_client_check_exit(struct client *); @@ -41,10 +45,6 @@ void server_client_msg_identify( struct client *, struct msg_identify_data *, int); void server_client_msg_shell(struct client *); -void printflike2 server_client_msg_error(struct cmd_ctx *, const char *, ...); -void printflike2 server_client_msg_print(struct cmd_ctx *, const char *, ...); -void printflike2 server_client_msg_info(struct cmd_ctx *, const char *, ...); - /* Create a new client. */ void server_client_create(int fd) @@ -63,6 +63,9 @@ server_client_create(int fd) fatal("gettimeofday failed"); memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); + c->cmdq = cmdq_new(c); + c->cmdq->client_exit = 1; + c->stdin_data = evbuffer_new (); c->stdout_data = evbuffer_new (); c->stderr_data = evbuffer_new (); @@ -93,6 +96,8 @@ server_client_create(int fd) c->tty.mouse.event = MOUSE_EVENT_UP; c->tty.mouse.flags = 0; + c->flags |= CLIENT_FOCUSED; + evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { @@ -149,7 +154,8 @@ server_client_lost(struct client *c) evbuffer_free (c->stdin_data); evbuffer_free (c->stdout_data); - evbuffer_free (c->stderr_data); + if (c->stderr_data != c->stdout_data) + evbuffer_free (c->stderr_data); status_free_jobs(&c->status_new); status_free_jobs(&c->status_old); @@ -175,6 +181,10 @@ server_client_lost(struct client *c) free(c->prompt_buffer); free(c->cwd); + c->cmdq->dead = 1; + cmdq_free(c->cmdq); + c->cmdq = NULL; + environ_free(&c->environ); close(c->ibuf.fd); @@ -355,6 +365,7 @@ server_client_handle_key(struct client *c, int key) /* Check the client is good to accept input. */ if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; + if (c->session == NULL) return; s = c->session; @@ -374,6 +385,7 @@ server_client_handle_key(struct client *c, int key) if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { if (c->flags & CLIENT_READONLY) return; + window_unzoom(w); wp = window_pane_at_index(w, key - '0'); if (wp != NULL && window_pane_visible(wp)) window_set_active_pane(w, wp); @@ -417,6 +429,7 @@ server_client_handle_key(struct client *c, int key) if (!(c->flags & CLIENT_PREFIX)) { if (isprefix) { c->flags |= CLIENT_PREFIX; + server_status_client(c); return; } @@ -431,6 +444,7 @@ server_client_handle_key(struct client *c, int key) /* Prefix key already pressed. Reset prefix and lookup key. */ c->flags &= ~CLIENT_PREFIX; + server_status_client(c); if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { /* If repeating, treat this as a key, else ignore. */ if (c->flags & CLIENT_REPEAT) { @@ -491,7 +505,7 @@ server_client_loop(void) /* * Any windows will have been redrawn as part of clients, so clear - * their flags now. + * their flags now. Also check pane focus and resize. */ for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); @@ -499,11 +513,92 @@ server_client_loop(void) continue; w->flags &= ~WINDOW_REDRAW; - TAILQ_FOREACH(wp, &w->panes, entry) + TAILQ_FOREACH(wp, &w->panes, entry) { + server_client_check_focus(wp); + server_client_check_resize(wp); wp->flags &= ~PANE_REDRAW; + } } } +/* Check if pane should be resized. */ +void +server_client_check_resize(struct window_pane *wp) +{ + struct winsize ws; + + if (wp->fd == -1 || !(wp->flags & PANE_RESIZE)) + return; + + memset(&ws, 0, sizeof ws); + ws.ws_col = wp->sx; + ws.ws_row = wp->sy; + + if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) { +#ifdef __sun + /* + * Some versions of Solaris apparently can return an error when + * resizing; don't know why this happens, can't reproduce on + * other platforms and ignoring it doesn't seem to cause any + * issues. + */ + if (errno != EINVAL) +#endif + fatal("ioctl failed"); + } + + wp->flags &= ~PANE_RESIZE; +} + +/* Check whether pane should be focused. */ +void +server_client_check_focus(struct window_pane *wp) +{ + u_int i; + struct client *c; + + /* If we don't care about focus, forget it. */ + if (!(wp->base.mode & MODE_FOCUSON)) + return; + + /* If we're not the active pane in our window, we're not focused. */ + if (wp->window->active != wp) + goto not_focused; + + /* If we're in a mode, we're not focused. */ + if (wp->screen != &wp->base) + goto not_focused; + + /* + * If our window is the current window in any focused clients with an + * attached session, we're focused. + */ + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + + if (!(c->flags & CLIENT_FOCUSED)) + continue; + if (c->session->flags & SESSION_UNATTACHED) + continue; + + if (c->session->curw->window == wp->window) + goto focused; + } + +not_focused: + if (wp->flags & PANE_FOCUSED) + bufferevent_write(wp->event, "\033[O", 3); + wp->flags &= ~PANE_FOCUSED; + return; + +focused: + if (!(wp->flags & PANE_FOCUSED)) + bufferevent_write(wp->event, "\033[I", 3); + wp->flags |= PANE_FOCUSED; +} + /* * Update cursor position and mode settings. The scroll region and attributes * are cleared when idle (waiting for an event) as this is the most likely time @@ -526,6 +621,9 @@ server_client_reset_state(struct client *c) if (c->flags & CLIENT_SUSPENDED) return; + if (c->flags & CLIENT_CONTROL) + return; + tty_region(&c->tty, 0, c->tty.sy - 1); status = options_get_number(oo, "status"); @@ -580,14 +678,16 @@ server_client_reset_state(struct client *c) } /* Repeat time callback. */ -/* ARGSUSED */ void server_client_repeat_timer(unused int fd, unused short events, void *data) { struct client *c = data; - if (c->flags & CLIENT_REPEAT) + if (c->flags & CLIENT_REPEAT) { + if (c->flags & CLIENT_PREFIX) + server_status_client(c); c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); + } } /* Check if client should be exited. */ @@ -620,7 +720,7 @@ server_client_check_redraw(struct client *c) struct window_pane *wp; int flags, redraw; - if (c->flags & CLIENT_SUSPENDED) + if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; flags = c->tty.flags & TTY_FREEZE; @@ -750,6 +850,8 @@ server_client_msg_dispatch(struct client *c) if (datalen != 0) fatalx("bad MSG_RESIZE size"); + if (c->flags & CLIENT_CONTROL) + break; if (tty_resize(&c->tty)) { recalculate_sizes(); server_redraw_client(c); @@ -804,74 +906,18 @@ server_client_msg_dispatch(struct client *c) } } -/* Callback to send error message to client. */ -void printflike2 -server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - evbuffer_add_vprintf(ctx->cmdclient->stderr_data, fmt, ap); - va_end(ap); - - evbuffer_add(ctx->cmdclient->stderr_data, "\n", 1); - server_push_stderr(ctx->cmdclient); - ctx->cmdclient->retcode = 1; -} - -/* Callback to send print message to client. */ -void printflike2 -server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - evbuffer_add_vprintf(ctx->cmdclient->stdout_data, fmt, ap); - va_end(ap); - - evbuffer_add(ctx->cmdclient->stdout_data, "\n", 1); - server_push_stdout(ctx->cmdclient); -} - -/* Callback to send print message to client, if not quiet. */ -void printflike2 -server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...) -{ - va_list ap; - - if (options_get_number(&global_options, "quiet")) - return; - - va_start(ap, fmt); - evbuffer_add_vprintf(ctx->cmdclient->stdout_data, fmt, ap); - va_end(ap); - - evbuffer_add(ctx->cmdclient->stdout_data, "\n", 1); - server_push_stdout(ctx->cmdclient); -} - /* Handle command message. */ void server_client_msg_command(struct client *c, struct msg_command_data *data) { - struct cmd_ctx ctx; struct cmd_list *cmdlist = NULL; int argc; char **argv, *cause; - ctx.error = server_client_msg_error; - ctx.print = server_client_msg_print; - ctx.info = server_client_msg_info; - - ctx.msgdata = data; - ctx.curclient = NULL; - - ctx.cmdclient = c; - argc = data->argc; data->argv[(sizeof data->argv) - 1] = '\0'; if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) { - server_client_msg_error(&ctx, "command too long"); + cmdq_error(c->cmdq, "command too long"); goto error; } @@ -881,29 +927,21 @@ server_client_msg_command(struct client *c, struct msg_command_data *data) *argv = xstrdup("new-session"); } - if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) { - server_client_msg_error(&ctx, "%s", cause); + 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); - switch (cmd_list_exec(cmdlist, &ctx)) - { - case CMD_RETURN_ERROR: - case CMD_RETURN_NORMAL: - c->flags |= CLIENT_EXIT; - break; - case CMD_RETURN_ATTACH: - case CMD_RETURN_YIELD: - break; - } + cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); return; error: if (cmdlist != NULL) cmd_list_free(cmdlist); + c->flags |= CLIENT_EXIT; } @@ -919,7 +957,11 @@ server_client_msg_identify( if (data->flags & IDENTIFY_CONTROL) { c->stdin_callback = control_callback; - c->flags |= (CLIENT_CONTROL|CLIENT_SUSPENDED); + evbuffer_free(c->stderr_data); + c->stderr_data = c->stdout_data; + c->flags |= CLIENT_CONTROL; + if (data->flags & IDENTIFY_TERMIOS) + evbuffer_add_printf(c->stdout_data, "\033P1000p"); server_write_client(c, MSG_STDIN, NULL, 0); c->tty.fd = -1; @@ -944,7 +986,8 @@ server_client_msg_identify( tty_resize(&c->tty); - c->flags |= CLIENT_TERMINAL; + if (!(data->flags & IDENTIFY_CONTROL)) + c->flags |= CLIENT_TERMINAL; } /* Handle shell message. */ diff --git a/server-fn.c b/server-fn.c index b21d94a1..566925f0 100644 --- a/server-fn.c +++ b/server-fn.c @@ -39,7 +39,7 @@ server_fill_environ(struct session *s, struct environ *env) term = options_get_string(&s->options, "default-terminal"); environ_set(env, "TERM", term); - idx = s->idx; + idx = s->id; } else idx = -1; pid = getpid(); @@ -194,7 +194,7 @@ server_status_window(struct window *w) /* * This is slightly different. We want to redraw the status line of any - * clients containing this window rather than any where it is the + * clients containing this window rather than anywhere it is the * current window. */ @@ -239,6 +239,9 @@ server_lock_client(struct client *c) size_t cmdlen; struct msg_lock_data lockdata; + if (c->flags & CLIENT_CONTROL) + return; + if (c->flags & CLIENT_SUSPENDED) return; @@ -374,6 +377,7 @@ server_destroy_pane(struct window_pane *wp) return; } + server_unzoom_window(w); layout_close_pane(wp); window_remove_pane(w, wp); @@ -491,7 +495,6 @@ server_clear_identify(struct client *c) } } -/* ARGSUSED */ void server_callback_identify(unused int fd, unused short events, void *data) { @@ -543,6 +546,10 @@ server_push_stderr(struct client *c) struct msg_stderr_data data; size_t size; + if (c->stderr_data == c->stdout_data) { + server_push_stdout(c); + return; + } size = EVBUFFER_LENGTH(c->stderr_data); if (size == 0) return; @@ -561,7 +568,7 @@ int server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, void *), void *cb_data, char **cause) { - if (c == NULL) { + if (c == NULL || c->session != NULL) { *cause = xstrdup("no client with stdin"); return (-1); } @@ -586,3 +593,11 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, return (0); } + +void +server_unzoom_window(struct window *w) +{ + window_unzoom(w); + server_redraw_window(w); + server_status_window(w); +} diff --git a/server-window.c b/server-window.c index fce6439b..4f5a5504 100644 --- a/server-window.c +++ b/server-window.c @@ -85,7 +85,7 @@ server_window_check_bell(struct session *s, struct winlink *wl) return (0); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) + if (c == NULL || c->session != s || (c->flags & CLIENT_CONTROL)) continue; if (!visual) { tty_bell(&c->tty); @@ -242,7 +242,7 @@ ring_bell(struct session *s) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session == s) + if (c != NULL && c->session == s && !(c->flags & CLIENT_CONTROL)) tty_bell(&c->tty); } } diff --git a/server.c b/server.c index b5139b4f..4bfa9185 100644 --- a/server.c +++ b/server.c @@ -105,8 +105,9 @@ server_create_socket(void) int server_start(int lockfd, char *lockfile) { - int pair[2]; - struct timeval tv; + int pair[2]; + struct timeval tv; + char *cause; /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) @@ -163,23 +164,28 @@ server_start(int lockfd, char *lockfile) free(lockfile); close(lockfd); - if (access(SYSTEM_CFG, R_OK) == 0) - load_cfg(SYSTEM_CFG, NULL, &cfg_causes); - else if (errno != ENOENT) { - cfg_add_cause( - &cfg_causes, "%s: %s", SYSTEM_CFG, strerror(errno)); + cfg_cmd_q = cmdq_new(NULL); + cfg_cmd_q->emptyfn = cfg_default_done; + cfg_finished = 0; + cfg_references = 1; + ARRAY_INIT(&cfg_causes); + + if (access(SYSTEM_CFG, R_OK) == 0) { + if (load_cfg(SYSTEM_CFG, cfg_cmd_q, &cause) == -1) { + xasprintf(&cause, "%s: %s", SYSTEM_CFG, cause); + ARRAY_ADD(&cfg_causes, cause); + } + } else if (errno != ENOENT) { + xasprintf(&cause, "%s: %s", SYSTEM_CFG, strerror(errno)); + ARRAY_ADD(&cfg_causes, cause); } - if (cfg_file != NULL) - load_cfg(cfg_file, NULL, &cfg_causes); - - /* - * If there is a session already, put the current window and pane into - * more mode. - */ - if (!RB_EMPTY(&sessions) && !ARRAY_EMPTY(&cfg_causes)) - show_cfg_causes(RB_MIN(sessions, &sessions)); - - cfg_finished = 1; + if (cfg_file != NULL) { + if (load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) { + xasprintf(&cause, "%s: %s", cfg_file, cause); + ARRAY_ADD(&cfg_causes, cause); + } + } + cmdq_continue(cfg_cmd_q); server_add_accept(0); @@ -317,7 +323,6 @@ server_update_socket(void) } /* Callback for server socket. */ -/* ARGSUSED */ void server_accept_callback(int fd, short events, unused void *data) { @@ -371,7 +376,6 @@ server_add_accept(int timeout) } /* Signal handler. */ -/* ARGSUSED */ void server_signal_callback(int sig, unused short events, unused void *data) { @@ -467,7 +471,6 @@ server_child_stopped(pid_t pid, int status) } /* Handle once-per-second timer events. */ -/* ARGSUSED */ void server_second_callback(unused int fd, unused short events, unused void *arg) { diff --git a/session.c b/session.c index 1f4fb30c..74eb06a5 100644 --- a/session.c +++ b/session.c @@ -29,7 +29,7 @@ /* Global session list. */ struct sessions sessions; struct sessions dead_sessions; -u_int next_session; +u_int next_session_id; struct session_groups session_groups; struct winlink *session_next_alert(struct winlink *); @@ -69,14 +69,14 @@ session_find(const char *name) return (RB_FIND(sessions, &sessions, &s)); } -/* Find session by index. */ +/* Find session by id. */ struct session * -session_find_by_index(u_int idx) +session_find_by_id(u_int id) { struct session *s; RB_FOREACH(s, sessions, &sessions) { - if (s->idx == idx) + if (s->id == id) return (s); } return (NULL); @@ -120,13 +120,13 @@ session_create(const char *name, const char *cmd, const char *cwd, if (name != NULL) { s->name = xstrdup(name); - s->idx = next_session++; + s->id = next_session_id++; } else { s->name = NULL; do { - s->idx = next_session++; + s->id = next_session_id++; free (s->name); - xasprintf(&s->name, "%u", s->idx); + xasprintf(&s->name, "%u", s->id); } while (RB_FIND(sessions, &sessions, s) != NULL); } RB_INSERT(sessions, &sessions, s); @@ -345,13 +345,7 @@ session_next(struct session *s, int alert) if (alert && ((wl = session_next_alert(wl)) == NULL)) return (-1); } - if (wl == s->curw) - return (1); - winlink_stack_remove(&s->lastw, wl); - winlink_stack_push(&s->lastw, s->curw); - s->curw = wl; - winlink_clear_flags(wl); - return (0); + return (session_set_current(s, wl)); } struct winlink * @@ -382,13 +376,7 @@ session_previous(struct session *s, int alert) if (alert && (wl = session_previous_alert(wl)) == NULL) return (-1); } - if (wl == s->curw) - return (1); - winlink_stack_remove(&s->lastw, wl); - winlink_stack_push(&s->lastw, s->curw); - s->curw = wl; - winlink_clear_flags(wl); - return (0); + return (session_set_current(s, wl)); } /* Move session to specific window. */ @@ -398,15 +386,7 @@ session_select(struct session *s, int idx) struct winlink *wl; wl = winlink_find_by_index(&s->windows, idx); - if (wl == NULL) - return (-1); - if (wl == s->curw) - return (1); - winlink_stack_remove(&s->lastw, wl); - winlink_stack_push(&s->lastw, s->curw); - s->curw = wl; - winlink_clear_flags(wl); - return (0); + return (session_set_current(s, wl)); } /* Move session to last used window. */ @@ -421,6 +401,18 @@ session_last(struct session *s) if (wl == s->curw) return (1); + return (session_set_current(s, wl)); +} + +/* Set current winlink to wl .*/ +int +session_set_current(struct session *s, struct winlink *wl) +{ + if (wl == NULL) + return (-1); + if (wl == s->curw) + return (1); + winlink_stack_remove(&s->lastw, wl); winlink_stack_push(&s->lastw, s->curw); s->curw = wl; diff --git a/status.c b/status.c index 88ab68d1..f120c38a 100644 --- a/status.c +++ b/status.c @@ -393,13 +393,6 @@ status_replace1(struct client *c, struct session *s, struct winlink *wl, long limit; u_int idx; - if (s == NULL) - s = c->session; - if (wl == NULL) - wl = s->curw; - if (wp == NULL) - wp = wl->window->active; - errno = 0; limit = strtol(*iptr, &endptr, 10); if ((limit == 0 && errno != EINVAL) || @@ -444,8 +437,7 @@ status_replace1(struct client *c, struct session *s, struct winlink *wl, case 'P': if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); - xsnprintf( - tmp, sizeof tmp, "%u", idx); + xsnprintf(tmp, sizeof tmp, "%u", idx); ptr = tmp; goto do_replace; case 'S': @@ -468,6 +460,9 @@ status_replace1(struct client *c, struct session *s, struct winlink *wl, */ ch = ']'; goto skip_to; + case '{': + ptr = (char *) "#{"; + goto do_replace; case '#': *(*optr)++ = '#'; break; @@ -507,13 +502,21 @@ char * status_replace(struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp, const char *fmt, time_t t, int jobsflag) { - static char out[BUFSIZ]; - char in[BUFSIZ], ch, *iptr, *optr; - size_t len; + static char out[BUFSIZ]; + char in[BUFSIZ], ch, *iptr, *optr, *expanded; + size_t len; + struct format_tree *ft; if (fmt == NULL) return (xstrdup("")); + if (s == NULL) + s = c->session; + if (wl == NULL) + wl = s->curw; + if (wp == NULL) + wp = wl->window->active; + len = strftime(in, sizeof in, fmt, localtime(&t)); in[len] = '\0'; @@ -534,7 +537,14 @@ status_replace(struct client *c, struct session *s, struct winlink *wl, } *optr = '\0'; - return (xstrdup(out)); + ft = format_create(); + format_client(ft, c); + format_session(ft, s); + format_winlink(ft, s, wl); + format_window_pane(ft, wp); + expanded = format_expand(ft, out); + format_free(ft); + return (expanded); } /* Figure out job name and get its result, starting it off if necessary. */ @@ -584,7 +594,7 @@ status_find_job(struct client *c, char **iptr) /* If not found at all, start the job and add to the tree. */ if (so == NULL) { - job_run(cmd, status_job_callback, status_job_free, c); + job_run(cmd, NULL, status_job_callback, status_job_free, c); c->references++; so = xmalloc(sizeof *so); @@ -666,7 +676,7 @@ status_job_callback(struct job *job) memcpy(buf, EVBUFFER_DATA(job->event->input), len); buf[len] = '\0'; } else - buf = xstrdup(line); + buf = line; so->out = buf; server_status_client(c); @@ -819,7 +829,6 @@ status_message_clear(struct client *c) } /* Clear status line message after timer expires. */ -/* ARGSUSED */ void status_message_callback(unused int fd, unused short event, void *data) { @@ -1033,7 +1042,7 @@ status_prompt_key(struct client *c, int key) size_t size, n, off, idx; size = strlen(c->prompt_buffer); - switch (mode_key_lookup(&c->prompt_mdata, key)) { + switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) { case MODEKEYEDIT_CURSORLEFT: if (c->prompt_index > 0) { c->prompt_index--; diff --git a/tmux.1 b/tmux.1 index 3e85be76..a639476f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 28lquvV +.Op Fl 28lCquv .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name @@ -102,6 +102,11 @@ to assume the terminal supports 256 colours. Like .Fl 2 , but indicates that the terminal supports 88 colours. +.It Fl C +Start in control mode. +Given twice +.Xo ( Fl CC ) Xc +disables echo. .It Fl c Ar shell-command Execute .Ar shell-command @@ -369,9 +374,9 @@ Clients may be listed with the command. .Pp .Ar target-session -is either the name of a session (as listed by the +is the session id prefixed with a $, the name of a session (as listed by the .Ic list-sessions -command) or the name of a client with the same syntax as +command), or the name of a client with the same syntax as .Ar target-client , in which case the session attached to the client is used. When looking for the session name, @@ -670,7 +675,8 @@ command. Lock all clients attached to .Ar target-session . .It Xo Ic new-session -.Op Fl d +.Op Fl AdDP +.Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name .Op Fl t Ar target-session @@ -701,6 +707,21 @@ If run from a terminal, any .Xr termios 4 special characters are saved and used for new windows in the new session. .Pp +The +.Fl A +flag makes +.Ic new-session +behave like +.Ic attach-session +if +.Ar session-name +already exists; in the case, +.Fl D +behaves like +.Fl d +to +.Ic attach-session . +.Pp If .Fl t is given, the new session is @@ -720,6 +741,14 @@ or are invalid if .Fl t is used. +.Pp +The +.Fl P +option prints information about the new session after it has been created. +By default, it uses the format +.Ql #{session_name}: +but a different format may be specified with +.Fl F . .It Xo Ic refresh-client .Op Fl S .Op Fl t Ar target-client @@ -854,7 +883,7 @@ The following keys are supported as appropriate for the mode: .It Li "Start of line" Ta "0" Ta "C-a" .It Li "Start selection" Ta "Space" Ta "C-Space" .It Li "Top of history" Ta "g" Ta "M->" -.It Li "Transpose chars" Ta "" Ta "C-t" +.It Li "Transpose characters" Ta "" Ta "C-t" .El .Pp The next and previous word keys use space and the @@ -916,6 +945,17 @@ command and keys modified or removed with .Ic bind-key and .Ic unbind-key . +One command accepts an argument, +.Ic copy-pipe , +which copies the selection and pipes it to a command. +For example the following will bind +.Ql C-q +to copy the selection into +.Pa /tmp +as well as the paste buffer: +.Bd -literal -offset indent +bind-key -temacs-copy C-q copy-pipe "cat >/tmp/out" +.Ed .Pp The paste buffer key pastes the first line from the top paste buffer on the stack. @@ -1040,14 +1080,36 @@ By default, it uses the format but a different format may be specified with .Fl F . .It Xo Ic capture-pane +.Op Fl aepPq .Op Fl b Ar buffer-index .Op Fl E Ar end-line .Op Fl S Ar start-line .Op Fl t Ar target-pane .Xc .D1 (alias: Ic capturep ) -Capture the contents of a pane to the specified buffer, or a new buffer if none -is specified. +Capture the contents of a pane. +If +.Fl p +is given, the output goes to stdout, otherwise to the buffer specified with +.Fl b +or a new buffer if omitted. +If +.Fl a +is given, the alternate screen is used, and the history is not accessible. +If no alternate screen exists, an error will be returned unless +.Fl q +is given. +If +.Fl e +is given, the output includes escape sequences for text and background +attributes. +.Fl C +also escapes non-printable characters as octal \exxx. +.Fl J +joins wrapped lines and preserves trailing spaces at each line's end. +.Fl P +captures only any output that the pane has received that is the beginning of an +as-yet incomplete escape sequence. .Pp .Fl S and @@ -1078,8 +1140,7 @@ For the meaning of the flag, see the .Sx FORMATS section. -This command works only from inside -.Nm . +This command works only if at least one client is attached. .It Xo .Ic choose-list .Op Fl l Ar items @@ -1105,8 +1166,7 @@ also accepts format specifiers. For the meaning of this see the .Sx FORMATS section. -This command works only from inside -.Nm . +This command works only if at least one client is attached. .It Xo .Ic choose-session .Op Fl F Ar format @@ -1128,13 +1188,10 @@ For the meaning of the flag, see the .Sx FORMATS section. -This command works only from inside -.Nm . +This command works only if at least one client is attached. .It Xo .Ic choose-tree -.Op Fl s -.Op Fl w -.Op Fl u +.Op Fl suw .Op Fl b Ar session-template .Op Fl c Ar window-template .Op Fl S Ar format @@ -1159,25 +1216,30 @@ is given, will show sessions. If .Fl w is given, will show windows. -If +.Pp +By default, the tree is collapsed and sessions must be expanded to windows +with the right arrow key. +The .Fl u -is given, the tree is uncollapsed by default. +option will start with all sessions expanded instead. +.Pp If .Fl b is given, will override the default session command. Note that .Ql %% -can be used, and will be replaced with the session name. +can be used and will be replaced with the session name. The default option if not specified is "switch-client -t '%%'". If .Fl c is given, will override the default window command. -Note that +Like +.Fl b , .Ql %% -can be used, and will be replaced with the session name and window index. -This command will run -.Ar session-template -before it. +can be used and will be replaced with the session name and window index. +When a window is chosen from the list, the session command is run before the +window command. +.Pp If .Fl S is given will display the specified format instead of the default session @@ -1193,8 +1255,8 @@ and options, see the .Sx FORMATS section. -This command only works from inside -.Nm . +.Pp +This command works only if at least one client is attached. .It Xo .Ic choose-window .Op Fl F Ar format @@ -1216,8 +1278,7 @@ For the meaning of the flag, see the .Sx FORMATS section. -This command works only from inside -.Nm . +This command works only if at least one client is attached. .It Ic display-panes Op Fl t Ar target-client .D1 (alias: Ic displayp) Display a visible indicator of each pane shown by @@ -1261,8 +1322,7 @@ For the meaning of the flag, see the .Sx FORMATS section. -This command only works from inside -.Nm . +This command works only if at least one client is attached. .It Xo Ic join-pane .Op Fl bdhv .Oo Fl l @@ -1419,9 +1479,9 @@ option. .It Xo Ic new-window .Op Fl adkP .Op Fl c Ar start-directory +.Op Fl F Ar format .Op Fl n Ar window-name .Op Fl t Ar target-window -.Op Fl F Ar format .Op Ar shell-command .Xc .D1 (alias: Ic neww ) @@ -1549,22 +1609,35 @@ Rename the current window, or the window at if specified, to .Ar new-name . .It Xo Ic resize-pane -.Op Fl DLRU +.Op Fl DLRUZ .Op Fl t Ar target-pane +.Op Fl x Ar width +.Op Fl y Ar height .Op Ar adjustment .Xc .D1 (alias: Ic resizep ) -Resize a pane, upward with -.Fl U -(the default), downward with +Resize a pane, up, down, left or right by +.Ar adjustment +with +.Fl U , .Fl D , -to the left with .Fl L -and to the right with -.Fl R . +or +.Fl R , +or +to an absolute size +with +.Fl x +or +.Fl y . The .Ar adjustment is given in lines or cells (the default is 1). +.Pp +With +.Fl Z , +the active pane is toggled between zoomed (occupying the whole of the window) +and unzoomed (its normal position in the layout). .It Xo Ic respawn-pane .Op Fl k .Op Fl t Ar target-pane @@ -1949,10 +2022,23 @@ All window options are documented with the .Ic set-window-option command. .Pp +.Nm +also supports user options which are prefixed with a +.Ql \&@ . +User options may have any name, so long as they are prefixed with +.Ql \&@ , +and be set to any string. +For example +.Bd -literal -offset indent +$ tmux setw -q @foo "abc123" +$ tmux showw -v @foo +abc123 +.Ed +.Pp Commands which set options are as follows: .Bl -tag -width Ds .It Xo Ic set-option -.Op Fl agqsuw +.Op Fl agoqsuw .Op Fl t Ar target-session | Ar target-window .Ar option Ar value .Xc @@ -1981,6 +2067,10 @@ options. It is not possible to unset a global option. .Pp The +.Fl o +flag prevents setting an option that is already set. +.Pp +The .Fl q flag suppresses the informational message (as if the .Ic quiet @@ -2664,6 +2754,13 @@ Set clock colour. .Xc Set clock hour format. .Pp +.It Ic command-prefix Ar string +String prefixed to commands (apart from a plain shell as set by the +.Ic default-shell +option). +The default is +.Ql "exec\ " . +.Pp .It Ic force-height Ar height .It Ic force-width Ar width Prevent @@ -2875,7 +2972,7 @@ If this option is set, searches will wrap around the end of the pane contents. The default is on. .El .It Xo Ic show-options -.Op Fl gsw +.Op Fl gqsvw .Op Fl t Ar target-session | Ar target-window .Op Ar option .Xc @@ -2891,8 +2988,15 @@ otherwise the session options for Global session or window options are listed if .Fl g is used. +.Fl v +shows only the option value, not the name. +If +.Fl q +is set, no error will be returned if +.Ar option +is unset. .It Xo Ic show-window-options -.Op Fl g +.Op Fl gv .Op Fl t Ar target-window .Op Ar option .Xc @@ -2902,6 +3006,8 @@ List the window options or a single option for or the global window options if .Fl g is used. +.Fl v +shows only the option value, not the name. .El .Sh FORMATS Certain commands accept the @@ -2937,6 +3043,9 @@ if it is unattached. The following variables are available, where appropriate: .Bl -column "session_created_string" "Replaced with" -offset indent .It Sy "Variable name" Ta Sy "Replaced with" +.It Li "alternate_on" Ta "If pane is in alternate screen" +.It Li "alternate_saved_x" Ta "Saved cursor X in alternate screen" +.It Li "alternate_saved_y" Ta "Saved cursor Y in alternate screen" .It Li "buffer_sample" Ta "First 50 characters from the specified buffer" .It Li "buffer_size" Ta "Size of the specified buffer in bytes" .It Li "client_activity" Ta "Integer time client last had activity" @@ -2945,34 +3054,55 @@ The following variables are available, where appropriate: .It Li "client_created_string" Ta "String time client created" .It Li "client_cwd" Ta "Working directory of client" .It Li "client_height" Ta "Height of client" +.It Li "client_last_session" Ta "Name of the client's last session" +.It Li "client_prefix" Ta "1 if prefix key has been pressed" .It Li "client_readonly" Ta "1 if client is readonly" +.It Li "client_session" Ta "Name of the client's session" .It Li "client_termname" Ta "Terminal name of client" .It Li "client_tty" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "1 if client supports utf8" .It Li "client_width" Ta "Width of client" -.It Li "host" Ta "Hostname of local host" +.It Li "cursor_flag" Ta "Pane cursor flag" +.It Li "cursor_x" Ta "Cursor X position in pane" +.It Li "cursor_y" Ta "Cursor Y position in pane" .It Li "history_bytes" Ta "Number of bytes in window history" .It Li "history_limit" Ta "Maximum window history lines" .It Li "history_size" Ta "Size of history in bytes" +.It Li "host" Ta "Hostname of local host" +.It Li "insert_flag" Ta "Pane insert flag" +.It Li "keypad_cursor_flag" Ta "Pane keypad cursor flag" +.It Li "keypad_flag" Ta "Pane keypad flag" .It Li "line" Ta "Line number in the list" +.It Li "mouse_any_flag" Ta "Pane mouse any flag" +.It Li "mouse_button_flag" Ta "Pane mouse button flag" +.It Li "mouse_standard_flag" Ta "Pane mouse standard flag" +.It Li "mouse_utf8_flag" Ta "Pane mouse UTF-8 flag" .It Li "pane_active" Ta "1 if active pane" +.It Li "pane_current_command" Ta "Current command if available" .It Li "pane_current_path" Ta "Current path if available" .It Li "pane_dead" Ta "1 if pane is dead" .It Li "pane_height" Ta "Height of pane" .It Li "pane_id" Ta "Unique pane ID" +.It Li "pane_in_mode" Ta "If pane is in a mode" .It Li "pane_index" Ta "Index of pane" .It Li "pane_pid" Ta "PID of first process in pane" .It Li "pane_start_command" Ta "Command pane started with" .It Li "pane_start_path" Ta "Path pane started with" +.It Li "pane_tabs" Ta "Pane tab positions" .It Li "pane_title" Ta "Title of pane" .It Li "pane_tty" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "Width of pane" +.It Li "saved_cursor_x" Ta "Saved cursor X in pane" +.It Li "saved_cursor_y" Ta "Saved cursor Y in pane" +.It Li "scroll_region_lower" Ta "Bottom of scroll region in pane" +.It Li "scroll_region_upper" Ta "Top of scroll region in pane" .It Li "session_attached" Ta "1 if session attached" .It Li "session_created" Ta "Integer time session created" .It Li "session_created_string" Ta "String time session created" .It Li "session_group" Ta "Number of session group" .It Li "session_grouped" Ta "1 if session in a group" .It Li "session_height" Ta "Height of session" +.It Li "session_id" Ta "Unique session ID" .It Li "session_name" Ta "Name of session" .It Li "session_width" Ta "Width of session" .It Li "session_windows" Ta "Number of windows in session" @@ -2986,6 +3116,7 @@ The following variables are available, where appropriate: .It Li "window_name" Ta "Name of window" .It Li "window_panes" Ta "Number of panes in window" .It Li "window_width" Ta "Width of window" +.It Li "wrap_flag" Ta "Pane wrap flag" .El .Sh NAMES AND TITLES .Nm @@ -3138,6 +3269,7 @@ The flag is one of the following symbols appended to the window name: .It Li "!" Ta "A bell has occurred in the window." .It Li "+" Ta "Window is monitored for content and it has appeared." .It Li "~" Ta "The window has been silent for the monitor-silence interval." +.It Li "Z" Ta "The window's active pane is zoomed." .El .Pp The # symbol relates to the @@ -3310,8 +3442,7 @@ For the meaning of the flag, see the .Sx FORMATS section. -This command works only from inside -.Nm . +This command works only if at least one client is attached. .It Ic clear-history Op Fl t Ar target-pane .D1 (alias: Ic clearhist ) Remove and free the history for the specified pane. @@ -3390,7 +3521,12 @@ Miscellaneous commands are as follows: .Bl -tag -width Ds .It Ic clock-mode Op Fl t Ar target-pane Display a large clock. -.It Ic if-shell Ar shell-command command Op Ar command +.It Xo Ic if-shell +.Op Fl b +.Op Fl t Ar target-pane +.Ar shell-command command +.Op Ar command +.Xc .D1 (alias: Ic if ) Execute the first .Ar command @@ -3399,12 +3535,21 @@ if returns success or the second .Ar command otherwise. +Before being executed, shell-command is expanded using the rules specified in the +.Sx FORMATS +section, including those relevant to +.Ar target-pane . +With +.Fl b , +.Ar shell-command +is run in the background. .It Ic lock-server .D1 (alias: Ic lock ) Lock each client individually by running the command specified by the .Ic lock-command option. .It Xo Ic run-shell +.Fl b .Op Fl t Ar target-pane .Ar shell-command .Xc @@ -3412,6 +3557,13 @@ option. Execute .Ar shell-command in the background without creating a window. +Before being executed, shell-command is expanded using the rules specified in +the +.Sx FORMATS +section. +With +.Fl b , +the command is run in the background. After it finishes, any output to stdout is displayed in copy mode (in the pane specified by .Fl t @@ -3420,6 +3572,23 @@ If the command doesn't return success, the exit status is also displayed. .It Ic server-info .D1 (alias: Ic info ) Show server information and terminal details. +.It Xo Ic wait-for +.Fl LSU +.Ar channel +.Xc +.D1 (alias: Ic wait ) +When used without options, prevents the client from exiting until woken using +.Ic wait-for +.Fl S +with the same channel. +When +.Fl L +is used, the channel is locked and any clients that try to lock the same +channel are made to wait until the channel is unlocked with +.Ic wait-for +.Fl U . +This command only works from outside +.Nm . .El .Sh TERMINFO EXTENSIONS .Nm @@ -3459,6 +3628,91 @@ option above and the .Xr xterm 1 man page. .El +.Sh CONTROL MODE +.Nm +offers a textual interface called +.Em control mode . +This allows applications to communicate with +.Nm +using a simple text-only protocol. +.Pp +In control mode, a client sends +.Nm +commands or command sequences terminated by newlines on standard input. +Each command will produce one block of output on standard output. +An output block consists of a +.Em %begin +line followed by the output (which may be empty). +The output block ends with a +.Em %end +or +.Em %error . +.Em %begin +and matching +.Em %end +or +.Em %error +have two arguments: an integer time (as seconds from epoch) and command number. +For example: +.Bd -literal -offset indent +%begin 1363006971 2 +0: ksh* (1 panes) [80x24] [layout b25f,80x24,0,0,2] @2 (active) +%end 1363006971 2 +.Ed +.Pp +In control mode, +.Nm +outputs notifications. +A notification will never occur inside an output block. +.Pp +The following notifications are defined: +.Bl -tag -width Ds +.It Ic %exit Op Ar reason +The +.Nm +client is exiting immediately, either because it is not attached to any session +or an error occurred. +If present, +.Ar reason +describes why the client exited. +.It Ic %layout-change Ar window-id Ar window-layout +The layout of a window with ID +.Ar window-id +changed. +The new layout is +.Ar window-layout . +.It Ic %output Ar pane-id Ar value +A window pane produced output. +.Ar value +escapes non-printable characters and backslash as octal \\xxx. +.It Ic %session-changed Ar session-id Ar name +The client is now attached to the session with ID +.Ar session-id , +which is named +.Ar name . +.It Ic %session-renamed Ar name +The current session was renamed to +.Ar name . +.It Ic %sessions-changed +A session was created or destroyed. +.It Ic %unlinked-window-add Ar window-id +The window with ID +.Ar window-id +was created but is not linked to the current session. +.It Ic %window-add Ar window-id +The window with ID +.Ar window-id +was linked to the current session. +.It Ic %window-close Ar window-id +The window with ID +.Ar window-id +closed. +.It Ic %window-renamed Ar window-id Ar name +The window with ID +.Ar window-id +was renamed to +.Ar name . +.El .Sh FILES .Bl -tag -width "/etc/tmux.confXXX" -compact .It Pa ~/.tmux.conf diff --git a/tmux.c b/tmux.c index 4b58abad..8ea91ebe 100644 --- a/tmux.c +++ b/tmux.c @@ -48,7 +48,7 @@ char socket_path[MAXPATHLEN]; int login_shell; char *environ_path; pid_t environ_pid = -1; -int environ_idx = -1; +int environ_session_id = -1; __dead void usage(void); void parseenvironment(void); @@ -147,16 +147,16 @@ parseenvironment(void) { char *env, path[256]; long pid; - int idx; + int id; if ((env = getenv("TMUX")) == NULL) return; - if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &idx) != 3) + if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &id) != 3) return; environ_path = xstrdup(path); environ_pid = pid; - environ_idx = idx; + environ_session_id = id; } char * @@ -336,8 +336,6 @@ main(int argc, char **argv) options_init(&global_w_options, NULL); options_table_populate_tree(window_options_table, &global_w_options); - ARRAY_INIT(&cfg_causes); - /* Enable UTF-8 if the first client is on UTF-8 terminal. */ if (flags & IDENTIFY_UTF8) { options_set_number(&global_s_options, "status-utf8", 1); diff --git a/tmux.h b/tmux.h index 1dd11ade..9c91d6a4 100644 --- a/tmux.h +++ b/tmux.h @@ -101,7 +101,7 @@ extern char **environ; /* Default templates for choose-tree. */ #define CHOOSE_TREE_SESSION_TEMPLATE \ - "#{session_name}: #{session_windows} windows " \ + "#{session_name}: #{session_windows} windows" \ "#{?session_grouped, (group ,}" \ "#{session_group}#{?session_grouped,),}" \ "#{?session_attached, (attached),}" @@ -155,6 +155,7 @@ extern char **environ; /* Default templates for break-pane, new-window and split-window. */ #define BREAK_PANE_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" +#define NEW_SESSION_TEMPLATE "#{session_name}:" #define NEW_WINDOW_TEMPLATE BREAK_PANE_TEMPLATE #define SPLIT_WINDOW_TEMPLATE BREAK_PANE_TEMPLATE @@ -237,6 +238,9 @@ enum key_code { KEYC_KP_ENTER, KEYC_KP_ZERO, KEYC_KP_PERIOD, + + KEYC_FOCUS_IN, + KEYC_FOCUS_OUT, }; /* Termcap codes. */ @@ -425,6 +429,9 @@ struct tty_term_code_entry { const char *name; }; +/* List of error causes. */ +ARRAY_DECL(causelist, char *); + /* Message codes. */ enum msgtype { MSG_COMMAND, @@ -456,8 +463,8 @@ enum msgtype { * Don't forget to bump PROTOCOL_VERSION if any of these change! */ struct msg_command_data { - pid_t pid; /* PID from $TMUX or -1 */ - int idx; /* index from $TMUX or -1 */ + pid_t pid; /* from $TMUX or -1 */ + int session_id; /* from $TMUX or -1 */ int argc; char argv[COMMAND_LENGTH]; @@ -562,6 +569,7 @@ enum mode_key_cmd { MODEKEYCOPY_BOTTOMLINE, MODEKEYCOPY_CANCEL, MODEKEYCOPY_CLEARSELECTION, + MODEKEYCOPY_COPYPIPE, MODEKEYCOPY_COPYLINE, MODEKEYCOPY_COPYENDOFLINE, MODEKEYCOPY_COPYSELECTION, @@ -628,12 +636,13 @@ struct mode_key_data { /* Binding between a key and a command. */ struct mode_key_binding { - int key; + int key; - int mode; - enum mode_key_cmd cmd; + int mode; + enum mode_key_cmd cmd; + const char *arg; - RB_ENTRY(mode_key_binding) entry; + RB_ENTRY(mode_key_binding) entry; }; RB_HEAD(mode_key_tree, mode_key_binding); @@ -661,7 +670,9 @@ struct mode_key_table { #define MODE_MOUSE_BUTTON 0x40 #define MODE_MOUSE_ANY 0x80 #define MODE_MOUSE_UTF8 0x100 -#define MODE_BRACKETPASTE 0x200 +#define MODE_MOUSE_SGR 0x200 +#define MODE_BRACKETPASTE 0x400 +#define MODE_FOCUSON 0x800 #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ANY) @@ -879,18 +890,24 @@ struct window_mode { /* Structures for choose mode. */ struct window_choose_data { - struct client *client; - struct session *session; /* Session of current client. */ - struct session *tree_session; /* Session of items in tree. */ - struct format_tree *ft; - struct winlink *wl; - char *ft_template; - char *command; + struct client *start_client; + struct session *start_session; + u_int idx; int type; +#define TREE_OTHER 0x0 #define TREE_WINDOW 0x1 #define TREE_SESSION 0x2 + + struct session *tree_session; /* session of items in tree */ + + struct winlink *wl; int pane_id; + + char *ft_template; + struct format_tree *ft; + + char *command; }; struct window_choose_mode_item { @@ -906,7 +923,9 @@ struct window_pane { u_int id; struct window *window; + struct layout_cell *layout_cell; + struct layout_cell *saved_layout_cell; u_int sx; u_int sy; @@ -917,6 +936,8 @@ struct window_pane { int flags; #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 +#define PANE_FOCUSED 0x4 +#define PANE_RESIZE 0x8 char *cmd; char *shell; @@ -969,6 +990,7 @@ struct window { int lastlayout; struct layout_cell *layout_root; + struct layout_cell *saved_layout_root; u_int sx; u_int sy; @@ -978,6 +1000,7 @@ struct window { #define WINDOW_ACTIVITY 0x2 #define WINDOW_REDRAW 0x4 #define WINDOW_SILENCE 0x8 +#define WINDOW_ZOOMED 0x10 struct options options; @@ -1061,7 +1084,7 @@ struct session_group { TAILQ_HEAD(session_groups, session_group); struct session { - u_int idx; + u_int id; char *name; char *cwd; @@ -1143,6 +1166,9 @@ LIST_HEAD(tty_terms, tty_term); * - bits 3, 4 and 5 are for keys * - bit 6 is set for dragging * - bit 7 for buttons 4 and 5 + * + * With the SGR 1006 extension the released button becomes known. Store these + * in separate fields and store the value converted to the old format in xb. */ struct mouse_event { u_int xb; @@ -1155,6 +1181,10 @@ struct mouse_event { u_int ly; u_int sy; + u_int sgr; /* whether the input arrived in SGR format */ + u_int sgr_xb; /* only for SGR: the unmangled button */ + u_int sgr_rel; /* only for SGR: if it is a release event */ + u_int button; u_int clicks; @@ -1196,7 +1226,7 @@ struct tty { #define TTY_NOCURSOR 0x1 #define TTY_FREEZE 0x2 -#define TTY_ESCAPE 0x4 +#define TTY_TIMER 0x4 #define TTY_UTF8 0x8 #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 @@ -1297,6 +1327,7 @@ struct client { #define CLIENT_READONLY 0x800 #define CLIENT_REDRAWWINDOW 0x1000 #define CLIENT_CONTROL 0x2000 +#define CLIENT_FOCUSED 0x4000 int flags; struct event identify_timer; @@ -1323,6 +1354,7 @@ struct client { int wlmouse; + struct cmd_q *cmdq; int references; }; ARRAY_DECL(clients, struct client *); @@ -1336,39 +1368,14 @@ struct args { char **argv; }; -/* Key/command line command. */ -struct cmd_ctx { - /* - * curclient is the client where this command was executed if inside - * tmux. This is NULL if the command came from the command-line. - * - * cmdclient is the client which sent the MSG_COMMAND to the server, if - * any. This is NULL unless the command came from the command-line. - * - * cmdclient and curclient may both be NULL if the command is in the - * configuration file. - */ - struct client *curclient; - struct client *cmdclient; - - struct msg_command_data *msgdata; - - /* gcc2 doesn't understand attributes on function pointers... */ -#if defined(__GNUC__) && __GNUC__ >= 3 - void printflike2 (*print)(struct cmd_ctx *, const char *, ...); - void printflike2 (*info)(struct cmd_ctx *, const char *, ...); - void printflike2 (*error)(struct cmd_ctx *, const char *, ...); -#else - void (*print)(struct cmd_ctx *, const char *, ...); - void (*info)(struct cmd_ctx *, const char *, ...); - void (*error)(struct cmd_ctx *, const char *, ...); -#endif -}; - +/* Command and list of commands. */ struct cmd { const struct cmd_entry *entry; struct args *args; + char *file; + u_int line; + TAILQ_ENTRY(cmd) qentry; }; struct cmd_list { @@ -1376,13 +1383,45 @@ struct cmd_list { TAILQ_HEAD(, cmd) list; }; +/* Command return values. */ enum cmd_retval { CMD_RETURN_ERROR = -1, CMD_RETURN_NORMAL = 0, - CMD_RETURN_YIELD, - CMD_RETURN_ATTACH + CMD_RETURN_WAIT, + CMD_RETURN_STOP }; +/* Command queue entry. */ +struct cmd_q_item { + struct cmd_list *cmdlist; + TAILQ_ENTRY(cmd_q_item) qentry; +}; +TAILQ_HEAD(cmd_q_items, cmd_q_item); + +/* Command queue. */ +struct cmd_q { + int references; + int dead; + + struct client *client; + int client_exit; + + struct cmd_q_items queue; + struct cmd_q_item *item; + struct cmd *cmd; + + time_t time; + u_int number; + + void (*emptyfn)(struct cmd_q *); + void *data; + + struct msg_command_data *msgdata; + + TAILQ_ENTRY(cmd_q) waitentry; +}; + +/* Command definition. */ struct cmd_entry { const char *name; const char *alias; @@ -1401,7 +1440,7 @@ struct cmd_entry { void (*key_binding)(struct cmd *, int); int (*check)(struct args *); - enum cmd_retval (*exec)(struct cmd *, struct cmd_ctx *); + enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; /* Key binding. */ @@ -1450,9 +1489,6 @@ struct format_entry { }; RB_HEAD(format_tree, format_entry); -/* List of configuration causes. */ -ARRAY_DECL(causelist, char *); - /* Common command usages. */ #define CMD_TARGET_PANE_USAGE "[-t target-pane]" #define CMD_TARGET_WINDOW_USAGE "[-t target-window]" @@ -1478,7 +1514,7 @@ extern char socket_path[MAXPATHLEN]; extern int login_shell; extern char *environ_path; extern pid_t environ_pid; -extern int environ_idx; +extern int environ_session_id; void logfile(const char *); const char *getshell(void); int checkshell(const char *); @@ -1488,12 +1524,13 @@ void setblocking(int, int); __dead void shell_exec(const char *, const char *); /* cfg.c */ -extern int cfg_finished; -extern int cfg_references; +extern struct cmd_q *cfg_cmd_q; +extern int cfg_finished; +extern int cfg_references; extern struct causelist cfg_causes; -void printflike2 cfg_add_cause(struct causelist *, const char *, ...); -enum cmd_retval load_cfg(const char *, struct cmd_ctx *, struct causelist *); -void show_cfg_causes(struct session *); +int load_cfg(const char *, struct cmd_q *, char **); +void cfg_default_done(struct cmd_q *); +void cfg_show_causes(struct session *); /* format.c */ int format_cmp(struct format_entry *, struct format_entry *); @@ -1528,7 +1565,7 @@ enum mode_key_cmd mode_key_fromstring(const struct mode_key_cmdstr *, const struct mode_key_table *mode_key_findtable(const char *); void mode_key_init_trees(void); void mode_key_init(struct mode_key_data *, struct mode_key_tree *); -enum mode_key_cmd mode_key_lookup(struct mode_key_data *, int); +enum mode_key_cmd mode_key_lookup(struct mode_key_data *, int, const char **); /* notify.c */ void notify_enable(void); @@ -1562,18 +1599,17 @@ long long options_get_number(struct options *, const char *); extern const struct options_table_entry server_options_table[]; extern const struct options_table_entry session_options_table[]; extern const struct options_table_entry window_options_table[]; -void options_table_populate_tree( - const struct options_table_entry *, struct options *); -const char *options_table_print_entry( - const struct options_table_entry *, struct options_entry *); -int options_table_find( - const char *, const struct options_table_entry **, +void options_table_populate_tree(const struct options_table_entry *, + struct options *); +const char *options_table_print_entry(const struct options_table_entry *, + struct options_entry *, int); +int options_table_find(const char *, const struct options_table_entry **, const struct options_table_entry **); /* job.c */ extern struct joblist all_jobs; -struct job *job_run( - const char *, void (*)(struct job *), void (*)(void *), void *); +struct job *job_run(const char *, struct session *, + void (*)(struct job *), void (*)(void *), void *); void job_free(struct job *); void job_died(struct job *, int); @@ -1665,7 +1701,7 @@ int tty_term_flag(struct tty_term *, enum tty_code_code); const char *tty_acs_get(struct tty *, u_char); /* tty-keys.c */ -void tty_keys_init(struct tty *); +void tty_keys_build(struct tty *); void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); @@ -1701,22 +1737,20 @@ int cmd_pack_argv(int, char **, char *, size_t); int cmd_unpack_argv(char *, size_t, int, char ***); char **cmd_copy_argv(int, char *const *); void cmd_free_argv(int, char **); -struct cmd *cmd_parse(int, char **, char **); -enum cmd_retval cmd_exec(struct cmd *, struct cmd_ctx *); -void cmd_free(struct cmd *); +struct cmd *cmd_parse(int, char **, const char *, u_int, char **); size_t cmd_print(struct cmd *, char *, size_t); -struct session *cmd_current_session(struct cmd_ctx *, int); -struct client *cmd_current_client(struct cmd_ctx *); -struct client *cmd_find_client(struct cmd_ctx *, const char *); -struct session *cmd_find_session(struct cmd_ctx *, const char *, int); -struct winlink *cmd_find_window( - struct cmd_ctx *, const char *, struct session **); -int cmd_find_index( - struct cmd_ctx *, const char *, struct session **); -struct winlink *cmd_find_pane(struct cmd_ctx *, - const char *, struct session **, struct window_pane **); -char *cmd_template_replace(char *, const char *, int); -const char *cmd_get_default_path(struct cmd_ctx *, const char *); +struct session *cmd_current_session(struct cmd_q *, int); +struct client *cmd_current_client(struct cmd_q *); +struct client *cmd_find_client(struct cmd_q *, const char *, int); +struct session *cmd_find_session(struct cmd_q *, const char *, int); +struct winlink *cmd_find_window(struct cmd_q *, const char *, + struct session **); +int cmd_find_index(struct cmd_q *, const char *, + struct session **); +struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, + struct window_pane **); +char *cmd_template_replace(const char *, const char *, int); +const char *cmd_get_default_path(struct cmd_q *, const char *); extern const struct cmd_entry *cmd_table[]; extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_bind_key_entry; @@ -1804,15 +1838,31 @@ extern const struct cmd_entry cmd_switch_client_entry; extern const struct cmd_entry cmd_unbind_key_entry; extern const struct cmd_entry cmd_unlink_window_entry; extern const struct cmd_entry cmd_up_pane_entry; +extern const struct cmd_entry cmd_wait_for_entry; + +/* cmd-attach-session.c */ +enum cmd_retval cmd_attach_session(struct cmd_q *, const char*, int, int); /* cmd-list.c */ -struct cmd_list *cmd_list_parse(int, char **, char **); -enum cmd_retval cmd_list_exec(struct cmd_list *, struct cmd_ctx *); +struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); void cmd_list_free(struct cmd_list *); size_t cmd_list_print(struct cmd_list *, char *, size_t); +/* cmd-queue.c */ +struct cmd_q *cmdq_new(struct client *); +int cmdq_free(struct cmd_q *); +void printflike2 cmdq_print(struct cmd_q *, const char *, ...); +void printflike2 cmdq_info(struct cmd_q *, const char *, ...); +void printflike2 cmdq_error(struct cmd_q *, const char *, ...); +int cmdq_guard(struct cmd_q *, const char *); +void cmdq_run(struct cmd_q *, struct cmd_list *); +void cmdq_append(struct cmd_q *, struct cmd_list *); +int cmdq_continue(struct cmd_q *); +void cmdq_flush(struct cmd_q *); + /* cmd-string.c */ -int cmd_string_parse(const char *, struct cmd_list **, char **); +int cmd_string_parse(const char *, struct cmd_list **, const char *, + u_int, char **); /* client.c */ int client_main(int, char **, int); @@ -1827,9 +1877,6 @@ void key_bindings_remove(int); void key_bindings_clean(void); void key_bindings_init(void); void key_bindings_dispatch(struct key_binding *, struct client *); -void printflike2 key_bindings_error(struct cmd_ctx *, const char *, ...); -void printflike2 key_bindings_print(struct cmd_ctx *, const char *, ...); -void printflike2 key_bindings_info(struct cmd_ctx *, const char *, ...); /* key-string.c */ int key_string_lookup_string(const char *); @@ -1890,6 +1937,7 @@ void server_push_stdout(struct client *); void server_push_stderr(struct client *); int server_set_stdin_callback(struct client *, void (*)(struct client *, int, void *), void *, char **); +void server_unzoom_window(struct window *); /* status.c */ int status_out_cmp(struct status_out *, struct status_out *); @@ -1951,16 +1999,18 @@ void grid_scroll_history(struct grid *); void grid_scroll_history_region(struct grid *, u_int, u_int); void grid_expand_line(struct grid *, u_int, u_int); const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int); +const struct grid_line *grid_peek_line(struct grid *, u_int); struct grid_cell *grid_get_cell(struct grid *, u_int, u_int); void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); void grid_clear(struct grid *, u_int, u_int, u_int, u_int); void grid_clear_lines(struct grid *, u_int, u_int); void grid_move_lines(struct grid *, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int); -char *grid_string_cells(struct grid *, u_int, u_int, u_int); +char *grid_string_cells(struct grid *, u_int, u_int, u_int, + struct grid_cell **, int, int, int); void grid_duplicate_lines( struct grid *, u_int, struct grid *, u_int, u_int); -u_int grid_reflow(struct grid *, const struct grid *, u_int); +u_int grid_reflow(struct grid *, struct grid *, u_int); /* grid-cell.c */ u_int grid_cell_width(const struct grid_cell *); @@ -2007,6 +2057,8 @@ void screen_write_putc( void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int, u_int, u_int, u_int); void screen_write_backspace(struct screen_write_ctx *); +void screen_write_mode_set(struct screen_write_ctx *, int); +void screen_write_mode_clear(struct screen_write_ctx *, int); void screen_write_cursorup(struct screen_write_ctx *, u_int); void screen_write_cursordown(struct screen_write_ctx *, u_int); void screen_write_cursorright(struct screen_write_ctx *, u_int); @@ -2021,18 +2073,11 @@ void screen_write_clearline(struct screen_write_ctx *); void screen_write_clearendofline(struct screen_write_ctx *); void screen_write_clearstartofline(struct screen_write_ctx *); void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int); -void screen_write_cursormode(struct screen_write_ctx *, int); void screen_write_reverseindex(struct screen_write_ctx *); void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); -void screen_write_insertmode(struct screen_write_ctx *, int); -void screen_write_utf8mousemode(struct screen_write_ctx *, int); -void screen_write_mousemode_on(struct screen_write_ctx *, int); -void screen_write_mousemode_off(struct screen_write_ctx *); void screen_write_linefeed(struct screen_write_ctx *, int); void screen_write_linefeedscreen(struct screen_write_ctx *, int); void screen_write_carriagereturn(struct screen_write_ctx *); -void screen_write_kcursormode(struct screen_write_ctx *, int); -void screen_write_kkeypadmode(struct screen_write_ctx *, int); void screen_write_clearendofscreen(struct screen_write_ctx *); void screen_write_clearstartofscreen(struct screen_write_ctx *); void screen_write_clearscreen(struct screen_write_ctx *); @@ -2040,7 +2085,6 @@ void screen_write_clearhistory(struct screen_write_ctx *); void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *); void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); -void screen_write_bracketpaste(struct screen_write_ctx *, int); /* screen-redraw.c */ void screen_redraw_screen(struct client *, int, int); @@ -2097,6 +2141,8 @@ struct window_pane *window_find_string(struct window *, const char *); void window_set_active_pane(struct window *, struct window_pane *); struct window_pane *window_add_pane(struct window *, u_int); void window_resize(struct window *, u_int, u_int); +int window_zoom(struct window_pane *); +int window_unzoom(struct window *); void window_remove_pane(struct window *, struct window_pane *); struct window_pane *window_pane_at_index(struct window *, u_int); struct window_pane *window_pane_next_by_number(struct window *, @@ -2153,12 +2199,14 @@ void layout_fix_panes(struct window *, u_int, u_int); u_int layout_resize_check(struct layout_cell *, enum layout_type); void layout_resize_adjust( struct layout_cell *, enum layout_type, int); -void layout_init(struct window *); +void layout_init(struct window *, struct window_pane *); void layout_free(struct window *); void layout_resize(struct window *, u_int, u_int); -void layout_resize_pane( - struct window_pane *, enum layout_type, int); -void layout_resize_pane_mouse(struct client *c); +void layout_resize_pane(struct window_pane *, enum layout_type, + int); +void layout_resize_pane_to(struct window_pane *, enum layout_type, + u_int); +void layout_resize_pane_mouse(struct client *); void layout_assign_pane(struct layout_cell *, struct window_pane *); struct layout_cell *layout_split_pane( struct window_pane *, enum layout_type, int, int); @@ -2192,19 +2240,20 @@ extern const struct window_mode window_choose_mode; void window_choose_add(struct window_pane *, struct window_choose_data *); void window_choose_ready(struct window_pane *, - u_int, void (*)(struct window_choose_data *), - void (*)(struct window_choose_data *)); -struct window_choose_data *window_choose_data_create(struct cmd_ctx *); -void window_choose_ctx(struct window_choose_data *); + u_int, void (*)(struct window_choose_data *)); +struct window_choose_data *window_choose_data_create (int, + struct client *, struct session *); +void window_choose_data_free(struct window_choose_data *); +void window_choose_data_run(struct window_choose_data *); struct window_choose_data *window_choose_add_window(struct window_pane *, - struct cmd_ctx *, struct session *, struct winlink *, - const char *, char *, u_int); + struct client *, struct session *, struct winlink *, + const char *, const char *, u_int); struct window_choose_data *window_choose_add_session(struct window_pane *, - struct cmd_ctx *, struct session *, const char *, - char *, u_int); + struct client *, struct session *, const char *, + const char *, u_int); struct window_choose_data *window_choose_add_item(struct window_pane *, - struct cmd_ctx *, struct winlink *, const char *, - char *, u_int); + struct client *, struct winlink *, const char *, + const char *, u_int); void window_choose_expand_all(struct window_pane *); /* names.c */ @@ -2240,7 +2289,7 @@ int session_cmp(struct session *, struct session *); RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); struct session *session_find(const char *); -struct session *session_find_by_index(u_int); +struct session *session_find_by_id(u_int); struct session *session_create(const char *, const char *, const char *, struct environ *, struct termios *, int, u_int, u_int, char **); @@ -2259,6 +2308,7 @@ int session_next(struct session *, int); int session_previous(struct session *, int); int session_select(struct session *, int); int session_last(struct session *); +int session_set_current(struct session *, struct winlink *); struct session_group *session_group_find(struct session *); u_int session_group_index(struct session_group *); void session_group_add(struct session *, struct session *); diff --git a/tty-keys.c b/tty-keys.c index 681f31b6..d86d22cd 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -28,9 +28,9 @@ #include "tmux.h" /* - * Handle keys input from the outside terminal. tty_keys[] is a base table of - * supported keys which are looked up in terminfo(5) and translated into a - * ternary tree (a binary tree of binary trees). + * Handle keys input from the outside terminal. tty_default_*_keys[] are a base + * table of supported keys which are looked up in terminfo(5) and translated + * into a ternary tree. */ void tty_keys_add1(struct tty_key **, const char *, int); @@ -43,249 +43,255 @@ void tty_keys_callback(int, short, void *); int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); int tty_keys_device(struct tty *, const char *, size_t, size_t *); -struct tty_key_ent { - enum tty_code_code code; +/* Default raw keys. */ +struct tty_default_key_raw { const char *string; - int key; - int flags; -#define TTYKEY_RAW 0x1 }; - -/* - * Default key tables. Those flagged with TTYKEY_RAW are inserted directly, - * otherwise they are looked up in terminfo(5). - */ -const struct tty_key_ent tty_keys[] = { +const struct tty_default_key_raw tty_default_raw_keys[] = { /* * Numeric keypad. Just use the vt100 escape sequences here and always * put the terminal into keypad_xmit mode. Translation of numbers * mode/applications mode is done in input-keys.c. */ - { 0, "\033Oo", KEYC_KP_SLASH, TTYKEY_RAW }, - { 0, "\033Oj", KEYC_KP_STAR, TTYKEY_RAW }, - { 0, "\033Om", KEYC_KP_MINUS, TTYKEY_RAW }, - { 0, "\033Ow", KEYC_KP_SEVEN, TTYKEY_RAW }, - { 0, "\033Ox", KEYC_KP_EIGHT, TTYKEY_RAW }, - { 0, "\033Oy", KEYC_KP_NINE, TTYKEY_RAW }, - { 0, "\033Ok", KEYC_KP_PLUS, TTYKEY_RAW }, - { 0, "\033Ot", KEYC_KP_FOUR, TTYKEY_RAW }, - { 0, "\033Ou", KEYC_KP_FIVE, TTYKEY_RAW }, - { 0, "\033Ov", KEYC_KP_SIX, TTYKEY_RAW }, - { 0, "\033Oq", KEYC_KP_ONE, TTYKEY_RAW }, - { 0, "\033Or", KEYC_KP_TWO, TTYKEY_RAW }, - { 0, "\033Os", KEYC_KP_THREE, TTYKEY_RAW }, - { 0, "\033OM", KEYC_KP_ENTER, TTYKEY_RAW }, - { 0, "\033Op", KEYC_KP_ZERO, TTYKEY_RAW }, - { 0, "\033On", KEYC_KP_PERIOD, TTYKEY_RAW }, + { "\033Oo", KEYC_KP_SLASH }, + { "\033Oj", KEYC_KP_STAR }, + { "\033Om", KEYC_KP_MINUS }, + { "\033Ow", KEYC_KP_SEVEN }, + { "\033Ox", KEYC_KP_EIGHT }, + { "\033Oy", KEYC_KP_NINE }, + { "\033Ok", KEYC_KP_PLUS }, + { "\033Ot", KEYC_KP_FOUR }, + { "\033Ou", KEYC_KP_FIVE }, + { "\033Ov", KEYC_KP_SIX }, + { "\033Oq", KEYC_KP_ONE }, + { "\033Or", KEYC_KP_TWO }, + { "\033Os", KEYC_KP_THREE }, + { "\033OM", KEYC_KP_ENTER }, + { "\033Op", KEYC_KP_ZERO }, + { "\033On", KEYC_KP_PERIOD }, /* Arrow keys. */ - { 0, "\033OA", KEYC_UP, TTYKEY_RAW }, - { 0, "\033OB", KEYC_DOWN, TTYKEY_RAW }, - { 0, "\033OC", KEYC_RIGHT, TTYKEY_RAW }, - { 0, "\033OD", KEYC_LEFT, TTYKEY_RAW }, + { "\033OA", KEYC_UP }, + { "\033OB", KEYC_DOWN }, + { "\033OC", KEYC_RIGHT }, + { "\033OD", KEYC_LEFT }, - { 0, "\033[A", KEYC_UP, TTYKEY_RAW }, - { 0, "\033[B", KEYC_DOWN, TTYKEY_RAW }, - { 0, "\033[C", KEYC_RIGHT, TTYKEY_RAW }, - { 0, "\033[D", KEYC_LEFT, TTYKEY_RAW }, + { "\033[A", KEYC_UP }, + { "\033[B", KEYC_DOWN }, + { "\033[C", KEYC_RIGHT }, + { "\033[D", KEYC_LEFT }, + + /* Other (xterm) "cursor" keys. */ + { "\033OH", KEYC_HOME }, + { "\033OF", KEYC_END }, + + { "\033[H", KEYC_HOME }, + { "\033[F", KEYC_END }, /* rxvt-style arrow + modifier keys. */ - { 0, "\033Oa", KEYC_UP|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033Ob", KEYC_DOWN|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033Oc", KEYC_RIGHT|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033Od", KEYC_LEFT|KEYC_CTRL, TTYKEY_RAW }, + { "\033Oa", KEYC_UP|KEYC_CTRL }, + { "\033Ob", KEYC_DOWN|KEYC_CTRL }, + { "\033Oc", KEYC_RIGHT|KEYC_CTRL }, + { "\033Od", KEYC_LEFT|KEYC_CTRL }, - { 0, "\033[a", KEYC_UP|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[b", KEYC_DOWN|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[c", KEYC_RIGHT|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[d", KEYC_LEFT|KEYC_SHIFT, TTYKEY_RAW }, + { "\033[a", KEYC_UP|KEYC_SHIFT }, + { "\033[b", KEYC_DOWN|KEYC_SHIFT }, + { "\033[c", KEYC_RIGHT|KEYC_SHIFT }, + { "\033[d", KEYC_LEFT|KEYC_SHIFT }, - /* - * rxvt-style function + modifier keys: - * Ctrl = ^, Shift = $, Ctrl+Shift = @ - */ - { 0, "\033[11^", KEYC_F1|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[12^", KEYC_F2|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[13^", KEYC_F3|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[14^", KEYC_F4|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[15^", KEYC_F5|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[17^", KEYC_F6|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[18^", KEYC_F7|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[19^", KEYC_F8|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[20^", KEYC_F9|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[21^", KEYC_F10|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[23^", KEYC_F11|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[24^", KEYC_F12|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[25^", KEYC_F13|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[26^", KEYC_F14|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[28^", KEYC_F15|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[29^", KEYC_F16|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[31^", KEYC_F17|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[32^", KEYC_F18|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[33^", KEYC_F19|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[34^", KEYC_F20|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[2^", KEYC_IC|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[3^", KEYC_DC|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[7^", KEYC_HOME|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[8^", KEYC_END|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[6^", KEYC_NPAGE|KEYC_CTRL, TTYKEY_RAW }, - { 0, "\033[5^", KEYC_PPAGE|KEYC_CTRL, TTYKEY_RAW }, + /* rxvt-style function + modifier keys (C = ^, S = $, C-S = @). */ + { "\033[11^", KEYC_F1|KEYC_CTRL }, + { "\033[12^", KEYC_F2|KEYC_CTRL }, + { "\033[13^", KEYC_F3|KEYC_CTRL }, + { "\033[14^", KEYC_F4|KEYC_CTRL }, + { "\033[15^", KEYC_F5|KEYC_CTRL }, + { "\033[17^", KEYC_F6|KEYC_CTRL }, + { "\033[18^", KEYC_F7|KEYC_CTRL }, + { "\033[19^", KEYC_F8|KEYC_CTRL }, + { "\033[20^", KEYC_F9|KEYC_CTRL }, + { "\033[21^", KEYC_F10|KEYC_CTRL }, + { "\033[23^", KEYC_F11|KEYC_CTRL }, + { "\033[24^", KEYC_F12|KEYC_CTRL }, + { "\033[25^", KEYC_F13|KEYC_CTRL }, + { "\033[26^", KEYC_F14|KEYC_CTRL }, + { "\033[28^", KEYC_F15|KEYC_CTRL }, + { "\033[29^", KEYC_F16|KEYC_CTRL }, + { "\033[31^", KEYC_F17|KEYC_CTRL }, + { "\033[32^", KEYC_F18|KEYC_CTRL }, + { "\033[33^", KEYC_F19|KEYC_CTRL }, + { "\033[34^", KEYC_F20|KEYC_CTRL }, + { "\033[2^", KEYC_IC|KEYC_CTRL }, + { "\033[3^", KEYC_DC|KEYC_CTRL }, + { "\033[7^", KEYC_HOME|KEYC_CTRL }, + { "\033[8^", KEYC_END|KEYC_CTRL }, + { "\033[6^", KEYC_NPAGE|KEYC_CTRL }, + { "\033[5^", KEYC_PPAGE|KEYC_CTRL }, - { 0, "\033[11$", KEYC_F1|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[12$", KEYC_F2|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[13$", KEYC_F3|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[14$", KEYC_F4|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[15$", KEYC_F5|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[17$", KEYC_F6|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[18$", KEYC_F7|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[19$", KEYC_F8|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[20$", KEYC_F9|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[21$", KEYC_F10|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[23$", KEYC_F11|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[24$", KEYC_F12|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[25$", KEYC_F13|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[26$", KEYC_F14|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[28$", KEYC_F15|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[29$", KEYC_F16|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[31$", KEYC_F17|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[32$", KEYC_F18|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[33$", KEYC_F19|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[34$", KEYC_F20|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[2$", KEYC_IC|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[3$", KEYC_DC|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[7$", KEYC_HOME|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[8$", KEYC_END|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[6$", KEYC_NPAGE|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[5$", KEYC_PPAGE|KEYC_SHIFT, TTYKEY_RAW }, + { "\033[11$", KEYC_F1|KEYC_SHIFT }, + { "\033[12$", KEYC_F2|KEYC_SHIFT }, + { "\033[13$", KEYC_F3|KEYC_SHIFT }, + { "\033[14$", KEYC_F4|KEYC_SHIFT }, + { "\033[15$", KEYC_F5|KEYC_SHIFT }, + { "\033[17$", KEYC_F6|KEYC_SHIFT }, + { "\033[18$", KEYC_F7|KEYC_SHIFT }, + { "\033[19$", KEYC_F8|KEYC_SHIFT }, + { "\033[20$", KEYC_F9|KEYC_SHIFT }, + { "\033[21$", KEYC_F10|KEYC_SHIFT }, + { "\033[23$", KEYC_F11|KEYC_SHIFT }, + { "\033[24$", KEYC_F12|KEYC_SHIFT }, + { "\033[25$", KEYC_F13|KEYC_SHIFT }, + { "\033[26$", KEYC_F14|KEYC_SHIFT }, + { "\033[28$", KEYC_F15|KEYC_SHIFT }, + { "\033[29$", KEYC_F16|KEYC_SHIFT }, + { "\033[31$", KEYC_F17|KEYC_SHIFT }, + { "\033[32$", KEYC_F18|KEYC_SHIFT }, + { "\033[33$", KEYC_F19|KEYC_SHIFT }, + { "\033[34$", KEYC_F20|KEYC_SHIFT }, + { "\033[2$", KEYC_IC|KEYC_SHIFT }, + { "\033[3$", KEYC_DC|KEYC_SHIFT }, + { "\033[7$", KEYC_HOME|KEYC_SHIFT }, + { "\033[8$", KEYC_END|KEYC_SHIFT }, + { "\033[6$", KEYC_NPAGE|KEYC_SHIFT }, + { "\033[5$", KEYC_PPAGE|KEYC_SHIFT }, - { 0, "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[13@", KEYC_F3|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[14@", KEYC_F4|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[15@", KEYC_F5|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[17@", KEYC_F6|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[18@", KEYC_F7|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[19@", KEYC_F8|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[20@", KEYC_F9|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[25@", KEYC_F13|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[26@", KEYC_F14|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[28@", KEYC_F15|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[29@", KEYC_F16|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[31@", KEYC_F17|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[32@", KEYC_F18|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[33@", KEYC_F19|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[34@", KEYC_F20|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[2@", KEYC_IC|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[3@", KEYC_DC|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[7@", KEYC_HOME|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[8@", KEYC_END|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, - { 0, "\033[6@", KEYC_NPAGE|KEYC_CTRL|KEYC_SHIFT,TTYKEY_RAW }, - { 0, "\033[5@", KEYC_PPAGE|KEYC_CTRL|KEYC_SHIFT,TTYKEY_RAW }, + { "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT }, + { "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT }, + { "\033[13@", KEYC_F3|KEYC_CTRL|KEYC_SHIFT }, + { "\033[14@", KEYC_F4|KEYC_CTRL|KEYC_SHIFT }, + { "\033[15@", KEYC_F5|KEYC_CTRL|KEYC_SHIFT }, + { "\033[17@", KEYC_F6|KEYC_CTRL|KEYC_SHIFT }, + { "\033[18@", KEYC_F7|KEYC_CTRL|KEYC_SHIFT }, + { "\033[19@", KEYC_F8|KEYC_CTRL|KEYC_SHIFT }, + { "\033[20@", KEYC_F9|KEYC_CTRL|KEYC_SHIFT }, + { "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT }, + { "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT }, + { "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT }, + { "\033[25@", KEYC_F13|KEYC_CTRL|KEYC_SHIFT }, + { "\033[26@", KEYC_F14|KEYC_CTRL|KEYC_SHIFT }, + { "\033[28@", KEYC_F15|KEYC_CTRL|KEYC_SHIFT }, + { "\033[29@", KEYC_F16|KEYC_CTRL|KEYC_SHIFT }, + { "\033[31@", KEYC_F17|KEYC_CTRL|KEYC_SHIFT }, + { "\033[32@", KEYC_F18|KEYC_CTRL|KEYC_SHIFT }, + { "\033[33@", KEYC_F19|KEYC_CTRL|KEYC_SHIFT }, + { "\033[34@", KEYC_F20|KEYC_CTRL|KEYC_SHIFT }, + { "\033[2@", KEYC_IC|KEYC_CTRL|KEYC_SHIFT }, + { "\033[3@", KEYC_DC|KEYC_CTRL|KEYC_SHIFT }, + { "\033[7@", KEYC_HOME|KEYC_CTRL|KEYC_SHIFT }, + { "\033[8@", KEYC_END|KEYC_CTRL|KEYC_SHIFT }, + { "\033[6@", KEYC_NPAGE|KEYC_CTRL|KEYC_SHIFT }, + { "\033[5@", KEYC_PPAGE|KEYC_CTRL|KEYC_SHIFT }, - /* terminfo lookups below this line so they can override raw keys. */ - - /* Function keys. */ - { TTYC_KF1, NULL, KEYC_F1, 0 }, - { TTYC_KF2, NULL, KEYC_F2, 0 }, - { TTYC_KF3, NULL, KEYC_F3, 0 }, - { TTYC_KF4, NULL, KEYC_F4, 0 }, - { TTYC_KF5, NULL, KEYC_F5, 0 }, - { TTYC_KF6, NULL, KEYC_F6, 0 }, - { TTYC_KF7, NULL, KEYC_F7, 0 }, - { TTYC_KF8, NULL, KEYC_F8, 0 }, - { TTYC_KF9, NULL, KEYC_F9, 0 }, - { TTYC_KF10, NULL, KEYC_F10, 0 }, - { TTYC_KF11, NULL, KEYC_F11, 0 }, - { TTYC_KF12, NULL, KEYC_F12, 0 }, - { TTYC_KF13, NULL, KEYC_F13, 0 }, - { TTYC_KF14, NULL, KEYC_F14, 0 }, - { TTYC_KF15, NULL, KEYC_F15, 0 }, - { TTYC_KF16, NULL, KEYC_F16, 0 }, - { TTYC_KF17, NULL, KEYC_F17, 0 }, - { TTYC_KF18, NULL, KEYC_F18, 0 }, - { TTYC_KF19, NULL, KEYC_F19, 0 }, - { TTYC_KF20, NULL, KEYC_F20, 0 }, - { TTYC_KICH1, NULL, KEYC_IC, 0 }, - { TTYC_KDCH1, NULL, KEYC_DC, 0 }, - { TTYC_KHOME, NULL, KEYC_HOME, 0 }, - { TTYC_KEND, NULL, KEYC_END, 0 }, - { TTYC_KNP, NULL, KEYC_NPAGE, 0 }, - { TTYC_KPP, NULL, KEYC_PPAGE, 0 }, - { TTYC_KCBT, NULL, KEYC_BTAB, 0 }, - - /* Arrow keys from terminfo. */ - { TTYC_KCUU1, NULL, KEYC_UP, 0 }, - { TTYC_KCUD1, NULL, KEYC_DOWN, 0 }, - { TTYC_KCUB1, NULL, KEYC_LEFT, 0 }, - { TTYC_KCUF1, NULL, KEYC_RIGHT, 0 }, - - /* Key and modifier capabilities. */ - { TTYC_KDC2, NULL, KEYC_DC|KEYC_SHIFT, 0 }, - { TTYC_KDC3, NULL, KEYC_DC|KEYC_ESCAPE, 0 }, - { TTYC_KDC4, NULL, KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE, 0 }, - { TTYC_KDC5, NULL, KEYC_DC|KEYC_CTRL, 0 }, - { TTYC_KDC6, NULL, KEYC_DC|KEYC_SHIFT|KEYC_CTRL, 0 }, - { TTYC_KDC7, NULL, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL, 0 }, - { TTYC_KDN2, NULL, KEYC_DOWN|KEYC_SHIFT, 0 }, - { TTYC_KDN3, NULL, KEYC_DOWN|KEYC_ESCAPE, 0 }, - { TTYC_KDN4, NULL, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE, 0 }, - { TTYC_KDN5, NULL, KEYC_DOWN|KEYC_CTRL, 0 }, - { TTYC_KDN6, NULL, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL, 0 }, - { TTYC_KDN7, NULL, KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL, 0 }, - { TTYC_KEND2, NULL, KEYC_END|KEYC_SHIFT, 0 }, - { TTYC_KEND3, NULL, KEYC_END|KEYC_ESCAPE, 0 }, - { TTYC_KEND4, NULL, KEYC_END|KEYC_SHIFT|KEYC_ESCAPE, 0 }, - { TTYC_KEND5, NULL, KEYC_END|KEYC_CTRL, 0 }, - { TTYC_KEND6, NULL, KEYC_END|KEYC_SHIFT|KEYC_CTRL, 0 }, - { TTYC_KEND7, NULL, KEYC_END|KEYC_ESCAPE|KEYC_CTRL, 0 }, - { TTYC_KHOM2, NULL, KEYC_HOME|KEYC_SHIFT, 0 }, - { TTYC_KHOM3, NULL, KEYC_HOME|KEYC_ESCAPE, 0 }, - { TTYC_KHOM4, NULL, KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE, 0 }, - { TTYC_KHOM5, NULL, KEYC_HOME|KEYC_CTRL, 0 }, - { TTYC_KHOM6, NULL, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL, 0 }, - { TTYC_KHOM7, NULL, KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL, 0 }, - { TTYC_KIC2, NULL, KEYC_IC|KEYC_SHIFT, 0 }, - { TTYC_KIC3, NULL, KEYC_IC|KEYC_ESCAPE, 0 }, - { TTYC_KIC4, NULL, KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE, 0 }, - { TTYC_KIC5, NULL, KEYC_IC|KEYC_CTRL, 0 }, - { TTYC_KIC6, NULL, KEYC_IC|KEYC_SHIFT|KEYC_CTRL, 0 }, - { TTYC_KIC7, NULL, KEYC_IC|KEYC_ESCAPE|KEYC_CTRL, 0 }, - { TTYC_KLFT2, NULL, KEYC_LEFT|KEYC_SHIFT, 0 }, - { TTYC_KLFT3, NULL, KEYC_LEFT|KEYC_ESCAPE, 0 }, - { TTYC_KLFT4, NULL, KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE, 0 }, - { TTYC_KLFT5, NULL, KEYC_LEFT|KEYC_CTRL, 0 }, - { TTYC_KLFT6, NULL, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL, 0 }, - { TTYC_KLFT7, NULL, KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL, 0 }, - { TTYC_KNXT2, NULL, KEYC_NPAGE|KEYC_SHIFT, 0 }, - { TTYC_KNXT3, NULL, KEYC_NPAGE|KEYC_ESCAPE, 0 }, - { TTYC_KNXT4, NULL, KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE, 0 }, - { TTYC_KNXT5, NULL, KEYC_NPAGE|KEYC_CTRL, 0 }, - { TTYC_KNXT6, NULL, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL, 0 }, - { TTYC_KNXT7, NULL, KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL, 0 }, - { TTYC_KPRV2, NULL, KEYC_PPAGE|KEYC_SHIFT, 0 }, - { TTYC_KPRV3, NULL, KEYC_PPAGE|KEYC_ESCAPE, 0 }, - { TTYC_KPRV4, NULL, KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE, 0 }, - { TTYC_KPRV5, NULL, KEYC_PPAGE|KEYC_CTRL, 0 }, - { TTYC_KPRV6, NULL, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL, 0 }, - { TTYC_KPRV7, NULL, KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL, 0 }, - { TTYC_KRIT2, NULL, KEYC_RIGHT|KEYC_SHIFT, 0 }, - { TTYC_KRIT3, NULL, KEYC_RIGHT|KEYC_ESCAPE, 0 }, - { TTYC_KRIT4, NULL, KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE, 0 }, - { TTYC_KRIT5, NULL, KEYC_RIGHT|KEYC_CTRL, 0 }, - { TTYC_KRIT6, NULL, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL, 0 }, - { TTYC_KRIT7, NULL, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL, 0 }, - { TTYC_KUP2, NULL, KEYC_UP|KEYC_SHIFT, 0 }, - { TTYC_KUP3, NULL, KEYC_UP|KEYC_ESCAPE, 0 }, - { TTYC_KUP4, NULL, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE, 0 }, - { TTYC_KUP5, NULL, KEYC_UP|KEYC_CTRL, 0 }, - { TTYC_KUP6, NULL, KEYC_UP|KEYC_SHIFT|KEYC_CTRL, 0 }, - { TTYC_KUP7, NULL, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL, 0 }, + /* Focus tracking. */ + { "\033[I", KEYC_FOCUS_IN }, + { "\033[O", KEYC_FOCUS_OUT }, }; +/* Default terminfo(5) keys. */ +struct tty_default_key_code { + enum tty_code_code code; + int key; +}; +const struct tty_default_key_code tty_default_code_keys[] = { + /* Function keys. */ + { TTYC_KF1, KEYC_F1 }, + { TTYC_KF2, KEYC_F2 }, + { TTYC_KF3, KEYC_F3 }, + { TTYC_KF4, KEYC_F4 }, + { TTYC_KF5, KEYC_F5 }, + { TTYC_KF6, KEYC_F6 }, + { TTYC_KF7, KEYC_F7 }, + { TTYC_KF8, KEYC_F8 }, + { TTYC_KF9, KEYC_F9 }, + { TTYC_KF10, KEYC_F10 }, + { TTYC_KF11, KEYC_F11 }, + { TTYC_KF12, KEYC_F12 }, + { TTYC_KF13, KEYC_F13 }, + { TTYC_KF14, KEYC_F14 }, + { TTYC_KF15, KEYC_F15 }, + { TTYC_KF16, KEYC_F16 }, + { TTYC_KF17, KEYC_F17 }, + { TTYC_KF18, KEYC_F18 }, + { TTYC_KF19, KEYC_F19 }, + { TTYC_KF20, KEYC_F20 }, + { TTYC_KICH1, KEYC_IC }, + { TTYC_KDCH1, KEYC_DC }, + { TTYC_KHOME, KEYC_HOME }, + { TTYC_KEND, KEYC_END }, + { TTYC_KNP, KEYC_NPAGE }, + { TTYC_KPP, KEYC_PPAGE }, + { TTYC_KCBT, KEYC_BTAB }, + + /* Arrow keys from terminfo. */ + { TTYC_KCUU1, KEYC_UP }, + { TTYC_KCUD1, KEYC_DOWN }, + { TTYC_KCUB1, KEYC_LEFT }, + { TTYC_KCUF1, KEYC_RIGHT }, + + /* Key and modifier capabilities. */ + { TTYC_KDC2, KEYC_DC|KEYC_SHIFT }, + { TTYC_KDC3, KEYC_DC|KEYC_ESCAPE }, + { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE }, + { TTYC_KDC5, KEYC_DC|KEYC_CTRL }, + { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KDC7, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL }, + { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT }, + { TTYC_KDN3, KEYC_DOWN|KEYC_ESCAPE }, + { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE }, + { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL }, + { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KDN7, KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL }, + { TTYC_KEND2, KEYC_END|KEYC_SHIFT }, + { TTYC_KEND3, KEYC_END|KEYC_ESCAPE }, + { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_ESCAPE }, + { TTYC_KEND5, KEYC_END|KEYC_CTRL }, + { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KEND7, KEYC_END|KEYC_ESCAPE|KEYC_CTRL }, + { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT }, + { TTYC_KHOM3, KEYC_HOME|KEYC_ESCAPE }, + { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE }, + { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL }, + { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KHOM7, KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL }, + { TTYC_KIC2, KEYC_IC|KEYC_SHIFT }, + { TTYC_KIC3, KEYC_IC|KEYC_ESCAPE }, + { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE }, + { TTYC_KIC5, KEYC_IC|KEYC_CTRL }, + { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KIC7, KEYC_IC|KEYC_ESCAPE|KEYC_CTRL }, + { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT }, + { TTYC_KLFT3, KEYC_LEFT|KEYC_ESCAPE }, + { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE }, + { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL }, + { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KLFT7, KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL }, + { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT }, + { TTYC_KNXT3, KEYC_NPAGE|KEYC_ESCAPE }, + { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE }, + { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL }, + { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KNXT7, KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL }, + { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT }, + { TTYC_KPRV3, KEYC_PPAGE|KEYC_ESCAPE }, + { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE }, + { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL }, + { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KPRV7, KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL }, + { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT }, + { TTYC_KRIT3, KEYC_RIGHT|KEYC_ESCAPE }, + { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE }, + { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL }, + { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KRIT7, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL }, + { TTYC_KUP2, KEYC_UP|KEYC_SHIFT }, + { TTYC_KUP3, KEYC_UP|KEYC_ESCAPE }, + { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE }, + { TTYC_KUP5, KEYC_UP|KEYC_CTRL }, + { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KUP7, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL }, +}; + +/* Add key to tree. */ void tty_keys_add(struct tty *tty, const char *s, int key) { @@ -343,27 +349,31 @@ tty_keys_add1(struct tty_key **tkp, const char *s, int key) /* Initialise a key tree from the table. */ void -tty_keys_init(struct tty *tty) +tty_keys_build(struct tty *tty) { - const struct tty_key_ent *tke; - u_int i; - const char *s; + const struct tty_default_key_raw *tdkr; + const struct tty_default_key_code *tdkc; + u_int i; + const char *s; + if (tty->key_tree != NULL) + tty_keys_free (tty); tty->key_tree = NULL; - for (i = 0; i < nitems(tty_keys); i++) { - tke = &tty_keys[i]; - if (tke->flags & TTYKEY_RAW) - s = tke->string; - else { - if (!tty_term_has(tty->term, tke->code)) - continue; - s = tty_term_string(tty->term, tke->code); - } - if (s[0] != '\033' || s[1] == '\0') - continue; + for (i = 0; i < nitems(tty_default_raw_keys); i++) { + tdkr = &tty_default_raw_keys[i]; + + s = tdkr->string; + if (*s != '\0') + tty_keys_add(tty, s, tdkr->key); + } + for (i = 0; i < nitems(tty_default_code_keys); i++) { + tdkc = &tty_default_code_keys[i]; + + s = tty_term_string(tty->term, tdkc->code); + if (*s != '\0') + tty_keys_add(tty, s, tdkc->key); - tty_keys_add(tty, s + 1, tke->key); } } @@ -440,34 +450,18 @@ tty_keys_next(struct tty *tty) cc_t bspace; int key, delay; + /* Get key buffer. */ buf = EVBUFFER_DATA(tty->event->input); len = EVBUFFER_LENGTH(tty->event->input); if (len == 0) return (0); log_debug("keys are %zu (%.*s)", len, (int) len, buf); - /* If a normal key, return it. */ - if (*buf != '\033') { - key = (u_char) *buf; - evbuffer_drain(tty->event->input, 1); - - /* - * Check for backspace key using termios VERASE - the terminfo - * kbs entry is extremely unreliable, so cannot be safely - * used. termios should have a better idea. - */ - bspace = tty->tio.c_cc[VERASE]; - if (bspace != _POSIX_VDISABLE && key == bspace) - key = KEYC_BSPACE; - goto handle_key; - } - /* Is this device attributes response? */ switch (tty_keys_device(tty, buf, len, &size)) { case 0: /* yes */ - evbuffer_drain(tty->event->input, size); key = KEYC_NONE; - goto handle_key; + goto complete_key; case -1: /* no, or not valid */ break; case 1: /* partial */ @@ -477,9 +471,8 @@ tty_keys_next(struct tty *tty) /* Is this a mouse key press? */ switch (tty_keys_mouse(tty, buf, len, &size)) { case 0: /* yes */ - evbuffer_drain(tty->event->input, size); key = KEYC_MOUSE; - goto handle_key; + goto complete_key; case -1: /* no, or not valid */ break; case 1: /* partial */ @@ -489,8 +482,7 @@ tty_keys_next(struct tty *tty) /* Try to parse a key with an xterm-style modifier. */ switch (xterm_keys_find(buf, len, &size, &key)) { case 0: /* found */ - evbuffer_drain(tty->event->input, size); - goto handle_key; + goto complete_key; case -1: /* not found */ break; case 1: @@ -498,108 +490,112 @@ tty_keys_next(struct tty *tty) } /* Look for matching key string and return if found. */ - tk = tty_keys_find(tty, buf + 1, len - 1, &size); + tk = tty_keys_find(tty, buf, len, &size); if (tk != NULL) { + if (tk->next != NULL) + goto partial_key; key = tk->key; - goto found_key; + goto complete_key; } - /* Skip the escape. */ - buf++; - len--; +first_key: + /* Is this a meta key? */ + if (len >= 2 && buf[0] == '\033') { + if (buf[1] != '\033') { + key = buf[1] | KEYC_ESCAPE; + size = 2; + goto complete_key; + } - /* Is there a normal key following? */ - if (len != 0 && *buf != '\033') { - key = *buf | KEYC_ESCAPE; - evbuffer_drain(tty->event->input, 2); - goto handle_key; - } - - /* Or a key string? */ - if (len > 1) { tk = tty_keys_find(tty, buf + 1, len - 1, &size); if (tk != NULL) { - key = tk->key | KEYC_ESCAPE; size++; /* include escape */ - goto found_key; + if (tk->next != NULL) + goto partial_key; + key = tk->key; + if (key != KEYC_NONE) + key |= KEYC_ESCAPE; + goto complete_key; } } - /* Escape and then nothing useful - fall through. */ + /* No key found, take first. */ + key = (u_char) *buf; + size = 1; + + /* + * Check for backspace key using termios VERASE - the terminfo + * kbs entry is extremely unreliable, so cannot be safely + * used. termios should have a better idea. + */ + bspace = tty->tio.c_cc[VERASE]; + if (bspace != _POSIX_VDISABLE && key == bspace) + key = KEYC_BSPACE; + + goto complete_key; partial_key: - /* - * Escape but no key string. If have already seen an escape and the - * timer has expired, give up waiting and send the escape. - */ - if ((tty->flags & TTY_ESCAPE) && - evtimer_initialized(&tty->key_timer) && - !evtimer_pending(&tty->key_timer, NULL)) { - evbuffer_drain(tty->event->input, 1); - key = '\033'; - goto handle_key; + log_debug("partial key %.*s", (int) len, buf); + + /* If timer is going, check for expiration. */ + if (tty->flags & TTY_TIMER) { + if (evtimer_initialized(&tty->key_timer) && + !evtimer_pending(&tty->key_timer, NULL)) + goto first_key; + return (0); } - /* Fall through to start the timer. */ - -start_timer: - /* If already waiting for timer, do nothing. */ - if (evtimer_initialized(&tty->key_timer) && - evtimer_pending(&tty->key_timer, NULL)) - return (0); - - /* Start the timer and wait for expiry or more data. */ + /* Get the time period. */ delay = options_get_number(&global_options, "escape-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; + /* Start the timer. */ if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); evtimer_set(&tty->key_timer, tty_keys_callback, tty); evtimer_add(&tty->key_timer, &tv); - tty->flags |= TTY_ESCAPE; + tty->flags |= TTY_TIMER; return (0); -found_key: - if (tk->next != NULL) { - /* Partial key. Start the timer if not already expired. */ - if (!(tty->flags & TTY_ESCAPE)) - goto start_timer; +complete_key: + log_debug("complete key %.*s %#x", (int) size, buf, key); - /* Otherwise, if no key, send the escape alone. */ - if (tk->key == KEYC_NONE) - goto partial_key; + /* Remove data from buffer. */ + evbuffer_drain(tty->event->input, size); - /* Or fall through to send the partial key found. */ - } - evbuffer_drain(tty->event->input, size + 1); - - goto handle_key; - -handle_key: + /* Remove key timer. */ if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); + tty->flags &= ~TTY_TIMER; + /* Check for focus events. */ + if (key == KEYC_FOCUS_OUT) { + tty->client->flags &= ~CLIENT_FOCUSED; + return (1); + } else if (key == KEYC_FOCUS_IN) { + tty->client->flags |= CLIENT_FOCUSED; + return (1); + } + + /* Fire the key. */ if (key != KEYC_NONE) server_client_handle_key(tty->client, key); - tty->flags &= ~TTY_ESCAPE; return (1); } /* Key timer callback. */ -/* ARGSUSED */ void tty_keys_callback(unused int fd, unused short events, void *data) { struct tty *tty = data; - if (!(tty->flags & TTY_ESCAPE)) - return; - - while (tty_keys_next(tty)) - ; + if (tty->flags & TTY_TIMER) { + while (tty_keys_next(tty)) + ; + } } /* @@ -611,20 +607,26 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; struct utf8_data utf8data; - u_int i, value, x, y, b; + u_int i, value, x, y, b, sgr, sgr_b, sgr_rel; + unsigned char c; /* * Standard mouse sequences are \033[M followed by three characters - * indicating buttons, X and Y, all based at 32 with 1,1 top-left. + * indicating button, X and Y, all based at 32 with 1,1 top-left. * * UTF-8 mouse sequences are similar but the three are expressed as * UTF-8 characters. + * + * SGR extended mouse sequences are \033[< followed by three numbers in + * decimal and separated by semicolons indicating button, X and Y. A + * trailing 'M' is click or scroll and trailing 'm' release. All are + * based at 0 with 1,1 top-left. */ *size = 0; - x = y = b = 0; + x = y = b = sgr = sgr_b = sgr_rel = 0; - /* First three bytes are always \033[M. */ + /* First two bytes are always \033[. */ if (buf[0] != '\033') return (-1); if (len == 1) @@ -633,50 +635,99 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (-1); if (len == 2) return (1); - if (buf[2] != 'M') - return (-1); - if (len == 3) - return (1); - /* Read the three inputs. */ - *size = 3; - for (i = 0; i < 3; i++) { - if (len < *size) - return (1); + /* + * Third byte is M in old standard and UTF-8 extension, < in SGR + * extension. + */ + if (buf[2] == 'M') { + /* Read the three inputs. */ + *size = 3; + for (i = 0; i < 3; i++) { + if (len <= *size) + return (1); - if (tty->mode & MODE_MOUSE_UTF8) { - if (utf8_open(&utf8data, buf[*size])) { - if (utf8data.size != 2) - return (-1); + if (tty->mode & MODE_MOUSE_UTF8) { + if (utf8_open(&utf8data, buf[*size])) { + if (utf8data.size != 2) + return (-1); + (*size)++; + if (len <= *size) + return (1); + utf8_append(&utf8data, buf[*size]); + value = utf8_combine(&utf8data); + } else + value = (u_char) buf[*size]; (*size)++; - if (len < *size) - return (1); - utf8_append(&utf8data, buf[*size]); - value = utf8_combine(&utf8data); - } else - value = (unsigned char)buf[*size]; - (*size)++; - } else { - value = (unsigned char)buf[*size]; - (*size)++; + } else { + value = (u_char) buf[*size]; + (*size)++; + } + + if (i == 0) + b = value; + else if (i == 1) + x = value; + else + y = value; } + log_debug("mouse input: %.*s", (int) *size, buf); - if (i == 0) - b = value; - else if (i == 1) - x = value; - else - y = value; - } - log_debug("mouse input: %.*s", (int) *size, buf); + /* Check and return the mouse input. */ + if (b < 32 || x < 33 || y < 33) + return (-1); + b -= 32; + x -= 33; + y -= 33; + } else if (buf[2] == '<') { + /* Read the three inputs. */ + *size = 3; + while (1) { + if (len <= *size) + return (1); + c = (u_char)buf[(*size)++]; + if (c == ';') + break; + if (c < '0' || c > '9') + return (-1); + sgr_b = 10 * sgr_b + (c - '0'); + } + while (1) { + if (len <= *size) + return (1); + c = (u_char)buf[(*size)++]; + if (c == ';') + break; + if (c < '0' || c > '9') + return (-1); + x = 10 * x + (c - '0'); + } + while (1) { + if (len <= *size) + return (1); + c = (u_char) buf[(*size)++]; + if (c == 'M' || c == 'm') + break; + if (c < '0' || c > '9') + return (-1); + y = 10 * y + (c - '0'); + } + log_debug("mouse input (sgr): %.*s", (int) *size, buf); - /* Check and return the mouse input. */ - if (b < 32 || x < 33 || y < 33) + /* Check and return the mouse input. */ + if (x < 1 || y < 1) + return (-1); + x--; + y--; + sgr = 1; + sgr_rel = (c == 'm'); + + /* Figure out what b would be in old format. */ + b = sgr_b; + if (sgr_rel) + b |= 3; + } else return (-1); - b -= 32; - x -= 33; - y -= 33; - log_debug("mouse position: x=%u y=%u b=%u", x, y, b); /* Fill in mouse structure. */ if (~m->event & MOUSE_EVENT_WHEEL) { @@ -684,6 +735,9 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->ly = m->y; } m->xb = b; + m->sgr = sgr; + m->sgr_xb = sgr_b; + m->sgr_rel = sgr_rel; if (b & 64) { /* wheel button */ b &= 3; if (b == 0) diff --git a/tty.c b/tty.c index e810f36f..ab75d948 100644 --- a/tty.c +++ b/tty.c @@ -149,19 +149,18 @@ tty_open(struct tty *tty, const char *overrides, char **cause) } tty->flags |= TTY_OPENED; - tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_ESCAPE); + tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_TIMER); tty->event = bufferevent_new( tty->fd, tty_read_callback, NULL, tty_error_callback, tty); tty_start_tty(tty); - tty_keys_init(tty); + tty_keys_build(tty); return (0); } -/* ARGSUSED */ void tty_read_callback(unused struct bufferevent *bufev, void *data) { @@ -171,7 +170,6 @@ tty_read_callback(unused struct bufferevent *bufev, void *data) ; } -/* ARGSUSED */ void tty_error_callback( unused struct bufferevent *bufev, unused short what, unused void *data) @@ -220,10 +218,10 @@ tty_start_tty(struct tty *tty) tty_putcode(tty, TTYC_CNORM); if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_puts(tty, "\033[?1000l"); + tty_puts(tty, "\033[?1000l\033[?1006l\033[?1005l"); if (tty_term_has(tty->term, TTYC_XT)) - tty_puts(tty, "\033[c"); + tty_puts(tty, "\033[c\033[>4;1m\033[?1004h"); tty->cx = UINT_MAX; tty->cy = UINT_MAX; @@ -267,8 +265,6 @@ tty_stop_tty(struct tty *tty) if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1) return; - setblocking(tty->fd, 1); - tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); if (tty_use_acs(tty)) tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); @@ -285,9 +281,14 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_raw(tty, "\033[?1000l"); + tty_raw(tty, "\033[?1000l\033[?1006l\033[?1005l"); + + if (tty_term_has(tty->term, TTYC_XT)) + tty_raw(tty, "\033[>4m\033[?1004l"); tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); + + setblocking(tty->fd, 1); } void @@ -332,7 +333,21 @@ tty_free(struct tty *tty) void tty_raw(struct tty *tty, const char *s) { - write(tty->fd, s, strlen(s)); + ssize_t n, slen; + u_int i; + + slen = strlen(s); + for (i = 0; i < 5; i++) { + n = write(tty->fd, s, slen); + if (n >= 0) { + s += n; + slen -= n; + if (slen == 0) + break; + } else if (n == -1 && errno != EAGAIN) + break; + usleep(100); + } } void @@ -474,10 +489,21 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } tty->cstyle = s->cstyle; } - if (changed & ALL_MOUSE_MODES) { + if (changed & (ALL_MOUSE_MODES|MODE_MOUSE_UTF8)) { if (mode & ALL_MOUSE_MODES) { + /* + * Enable the UTF-8 (1005) extension if configured to. + * Enable the SGR (1006) extension unconditionally, as + * this is safe from misinterpretation. Do it in this + * order, because in some terminals it's the last one + * that takes effect and SGR is the preferred one. + */ if (mode & MODE_MOUSE_UTF8) tty_puts(tty, "\033[?1005h"); + else + tty_puts(tty, "\033[?1005l"); + tty_puts(tty, "\033[?1006h"); + if (mode & MODE_MOUSE_ANY) tty_puts(tty, "\033[?1003h"); else if (mode & MODE_MOUSE_BUTTON) @@ -491,6 +517,8 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) tty_puts(tty, "\033[?1002l"); else if (tty->mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000l"); + + tty_puts(tty, "\033[?1006l"); if (tty->mode & MODE_MOUSE_UTF8) tty_puts(tty, "\033[?1005l"); } diff --git a/window-choose.c b/window-choose.c index 6dc149f0..3c68d101 100644 --- a/window-choose.c +++ b/window-choose.c @@ -31,6 +31,8 @@ void window_choose_key(struct window_pane *, struct session *, int); void window_choose_mouse( struct window_pane *, struct session *, struct mouse_event *); +void window_choose_default_callback(struct window_choose_data *); + void window_choose_fire_callback( struct window_pane *, struct window_choose_data *); void window_choose_redraw_screen(struct window_pane *); @@ -73,9 +75,9 @@ struct window_choose_mode_data { char *input_str; void (*callbackfn)(struct window_choose_data *); - void (*freefn)(struct window_choose_data *); }; +void window_choose_free1(struct window_choose_mode_data *); int window_choose_key_index(struct window_choose_mode_data *, u_int); int window_choose_index_key(struct window_choose_mode_data *, int); void window_choose_prompt_input(enum window_choose_input_type, @@ -101,8 +103,7 @@ window_choose_add(struct window_pane *wp, struct window_choose_data *wcd) void window_choose_ready(struct window_pane *wp, u_int cur, - void (*callbackfn)(struct window_choose_data *), - void (*freefn)(struct window_choose_data *)) + void (*callbackfn)(struct window_choose_data *)) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -112,7 +113,8 @@ window_choose_ready(struct window_pane *wp, u_int cur, data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s); data->callbackfn = callbackfn; - data->freefn = freefn; + if (data->callbackfn == NULL) + data->callbackfn = window_choose_default_callback; ARRAY_CONCAT(&data->old_list, &data->list); @@ -129,7 +131,6 @@ window_choose_init(struct window_pane *wp) wp->modedata = data = xmalloc(sizeof *data); data->callbackfn = NULL; - data->freefn = NULL; data->input_type = WINDOW_CHOOSE_NORMAL; data->input_str = xstrdup(""); data->input_prompt = NULL; @@ -154,35 +155,104 @@ window_choose_init(struct window_pane *wp) } struct window_choose_data * -window_choose_data_create(struct cmd_ctx *ctx) +window_choose_data_create(int type, struct client *c, struct session *s) { struct window_choose_data *wcd; wcd = xmalloc(sizeof *wcd); + wcd->type = type; + wcd->ft = format_create(); wcd->ft_template = NULL; + wcd->command = NULL; + wcd->wl = NULL; - wcd->tree_session = NULL; - wcd->client = ctx->curclient; - wcd->session = ctx->curclient->session; + wcd->pane_id = -1; wcd->idx = -1; - wcd->type = 0; + + wcd->tree_session = NULL; + + wcd->start_client = c; + wcd->start_client->references++; + wcd->start_session = s; + wcd->start_session->references++; return (wcd); } +void +window_choose_data_free(struct window_choose_data *wcd) +{ + wcd->start_client->references--; + wcd->start_session->references--; + + if (wcd->tree_session != NULL) + wcd->tree_session->references--; + + free(wcd->ft_template); + format_free(wcd->ft); + + free(wcd->command); + free(wcd); +} + +void +window_choose_data_run(struct window_choose_data *cdata) +{ + struct cmd_list *cmdlist; + char *cause; + + /* + * The command template will have already been replaced. But if it's + * NULL, bail here. + */ + if (cdata->command == NULL) + return; + + if (cmd_string_parse(cdata->command, &cmdlist, NULL, 0, &cause) != 0) { + if (cause != NULL) { + *cause = toupper((u_char) *cause); + status_message_set(cdata->start_client, "%s", cause); + free(cause); + } + return; + } + + cmdq_run(cdata->start_client->cmdq, cmdlist); + cmd_list_free(cmdlist); +} + +void +window_choose_default_callback(struct window_choose_data *wcd) +{ + if (wcd == NULL) + return; + if (wcd->start_client->flags & CLIENT_DEAD) + return; + + window_choose_data_run(wcd); +} + void window_choose_free(struct window_pane *wp) { - struct window_choose_mode_data *data = wp->modedata; + if (wp->modedata != NULL) + window_choose_free1(wp->modedata); +} + +void +window_choose_free1(struct window_choose_mode_data *data) +{ struct window_choose_mode_item *item; u_int i; + if (data == NULL) + return; + for (i = 0; i < ARRAY_LENGTH(&data->old_list); i++) { item = &ARRAY_ITEM(&data->old_list, i); - if (data->freefn != NULL && item->wcd != NULL) - data->freefn(item->wcd); + window_choose_data_free(item->wcd); free(item->name); } ARRAY_FREE(&data->list); @@ -209,17 +279,16 @@ window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) void window_choose_fire_callback( - struct window_pane *wp, struct window_choose_data *wcd) + struct window_pane *wp, struct window_choose_data *wcd) { struct window_choose_mode_data *data = wp->modedata; - const struct window_mode *oldmode; - oldmode = wp->mode; - wp->mode = NULL; + wp->modedata = NULL; + window_pane_reset_mode(wp); data->callbackfn(wcd); - wp->mode = oldmode; + window_choose_free1(data); } void @@ -299,7 +368,7 @@ window_choose_collapse_all(struct window_pane *wp) struct session *s, *chosen; u_int i; - chosen = ARRAY_ITEM(&data->list, data->selected).wcd->session; + chosen = ARRAY_ITEM(&data->list, data->selected).wcd->start_session; RB_FOREACH(s, sessions, &sessions) window_choose_collapse(wp, s); @@ -401,7 +470,6 @@ window_choose_expand(struct window_pane *wp, struct session *s, u_int pos) } } -/* ARGSUSED */ void window_choose_key(struct window_pane *wp, unused struct session *sess, int key) { @@ -416,7 +484,7 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) items = ARRAY_LENGTH(&data->list); if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) { - switch (mode_key_lookup(&data->mdata, key)) { + switch (mode_key_lookup(&data->mdata, key, NULL)) { case MODEKEYCHOICE_CANCEL: data->input_type = WINDOW_CHOOSE_NORMAL; window_choose_redraw_screen(wp); @@ -430,7 +498,6 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) } item = &ARRAY_ITEM(&data->list, n); window_choose_fire_callback(wp, item->wcd); - window_pane_reset_mode(wp); break; case MODEKEYCHOICE_BACKSPACE: input_len = strlen(data->input_str); @@ -448,15 +515,13 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) return; } - switch (mode_key_lookup(&data->mdata, key)) { + switch (mode_key_lookup(&data->mdata, key, NULL)) { case MODEKEYCHOICE_CANCEL: window_choose_fire_callback(wp, NULL); - window_pane_reset_mode(wp); break; case MODEKEYCHOICE_CHOOSE: item = &ARRAY_ITEM(&data->list, data->selected); window_choose_fire_callback(wp, item->wcd); - window_pane_reset_mode(wp); break; case MODEKEYCHOICE_TREE_TOGGLE: item = &ARRAY_ITEM(&data->list, data->selected); @@ -606,12 +671,10 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) item = &ARRAY_ITEM(&data->list, data->selected); window_choose_fire_callback(wp, item->wcd); - window_pane_reset_mode(wp); break; } } -/* ARGSUSED */ void window_choose_mouse( struct window_pane *wp, unused struct session *sess, struct mouse_event *m) @@ -635,7 +698,6 @@ window_choose_mouse( item = &ARRAY_ITEM(&data->list, data->selected); window_choose_fire_callback(wp, item->wcd); - window_pane_reset_mode(wp); } void @@ -681,7 +743,7 @@ window_choose_write_line( (item->wcd->type & TREE_SESSION) ? (item->state & TREE_EXPANDED ? "-" : "+") : "", item->name); } - while (s->cx < screen_size_x(s)) + while (s->cx < screen_size_x(s) - 1) screen_write_putc(ctx, &gc, ' '); if (data->input_type != WINDOW_CHOOSE_NORMAL) { @@ -707,7 +769,7 @@ window_choose_key_index(struct window_choose_mode_data *data, u_int idx) int mkey; for (ptr = keys; *ptr != '\0'; ptr++) { - mkey = mode_key_lookup(&data->mdata, *ptr); + mkey = mode_key_lookup(&data->mdata, *ptr, NULL); if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) continue; if (idx-- == 0) @@ -727,7 +789,7 @@ window_choose_index_key(struct window_choose_mode_data *data, int key) u_int idx = 0; for (ptr = keys; *ptr != '\0'; ptr++) { - mkey = mode_key_lookup(&data->mdata, *ptr); + mkey = mode_key_lookup(&data->mdata, *ptr, NULL); if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) continue; if (key == *ptr) @@ -790,58 +852,23 @@ window_choose_scroll_down(struct window_pane *wp) screen_write_stop(&ctx); } -void -window_choose_ctx(struct window_choose_data *cdata) -{ - struct cmd_ctx ctx; - struct cmd_list *cmdlist; - char *cause; - - /* The command template will have already been replaced. But if it's - * NULL, bail here. - */ - if (cdata->command == NULL) - return; - - if (cmd_string_parse(cdata->command, &cmdlist, &cause) != 0) { - if (cause != NULL) { - *cause = toupper((u_char) *cause); - status_message_set(cdata->client, "%s", cause); - free(cause); - } - return; - } - - ctx.msgdata = NULL; - ctx.curclient = cdata->client; - - ctx.error = key_bindings_error; - ctx.print = key_bindings_print; - ctx.info = key_bindings_info; - - ctx.cmdclient = NULL; - - cmd_list_exec(cmdlist, &ctx); - cmd_list_free(cmdlist); -} - struct window_choose_data * -window_choose_add_session(struct window_pane *wp, struct cmd_ctx *ctx, - struct session *s, const char *template, char *action, u_int idx) +window_choose_add_session(struct window_pane *wp, struct client *c, + struct session *s, const char *template, const char *action, u_int idx) { struct window_choose_data *wcd; - wcd = window_choose_data_create(ctx); - wcd->idx = s->idx; + wcd = window_choose_data_create(TREE_SESSION, c, c->session); + wcd->idx = s->id; + wcd->tree_session = s; - wcd->type = TREE_SESSION; - wcd->command = cmd_template_replace(action, s->name, 1); + wcd->tree_session->references++; + wcd->ft_template = xstrdup(template); format_add(wcd->ft, "line", "%u", idx); format_session(wcd->ft, s); - wcd->client->references++; - wcd->session->references++; + wcd->command = cmd_template_replace(action, s->name, 1); window_choose_add(wp, wcd); @@ -849,63 +876,60 @@ window_choose_add_session(struct window_pane *wp, struct cmd_ctx *ctx, } struct window_choose_data * -window_choose_add_item(struct window_pane *wp, struct cmd_ctx *ctx, - struct winlink *wl, const char *template, char *action, u_int idx) +window_choose_add_item(struct window_pane *wp, struct client *c, + struct winlink *wl, const char *template, const char *action, u_int idx) { struct window_choose_data *wcd; - char *action_data; + char *expanded; - wcd = window_choose_data_create(ctx); + wcd = window_choose_data_create(TREE_OTHER, c, c->session); wcd->idx = wl->idx; + wcd->ft_template = xstrdup(template); format_add(wcd->ft, "line", "%u", idx); - format_session(wcd->ft, wcd->session); - format_winlink(wcd->ft, wcd->session, wl); + format_session(wcd->ft, wcd->start_session); + format_winlink(wcd->ft, wcd->start_session, wl); format_window_pane(wcd->ft, wl->window->active); - wcd->client->references++; - wcd->session->references++; + /* + * Interpolate action here, since the data we pass back is the expanded + * template itself. + */ + xasprintf(&expanded, "%s", format_expand(wcd->ft, wcd->ft_template)); + wcd->command = cmd_template_replace(action, expanded, 1); + free(expanded); window_choose_add(wp, wcd); - /* - * Interpolate action_data here, since the data we pass back is the - * expanded template itself. - */ - xasprintf(&action_data, "%s", format_expand(wcd->ft, wcd->ft_template)); - wcd->command = cmd_template_replace(action, action_data, 1); - free(action_data); - return (wcd); } struct window_choose_data * -window_choose_add_window(struct window_pane *wp, struct cmd_ctx *ctx, +window_choose_add_window(struct window_pane *wp, struct client *c, struct session *s, struct winlink *wl, const char *template, - char *action, u_int idx) + const char *action, u_int idx) { struct window_choose_data *wcd; - char *action_data; - - wcd = window_choose_data_create(ctx); - - xasprintf(&action_data, "%s:%d", s->name, wl->idx); - wcd->command = cmd_template_replace(action, action_data, 1); - free(action_data); + char *expanded; + wcd = window_choose_data_create(TREE_WINDOW, c, c->session); wcd->idx = wl->idx; + wcd->wl = wl; + wcd->tree_session = s; - wcd->type = TREE_WINDOW; + wcd->tree_session->references++; + wcd->ft_template = xstrdup(template); format_add(wcd->ft, "line", "%u", idx); format_session(wcd->ft, s); format_winlink(wcd->ft, s, wl); format_window_pane(wcd->ft, wl->window->active); - wcd->client->references++; - wcd->session->references++; + xasprintf(&expanded, "%s:%d", s->name, wl->idx); + wcd->command = cmd_template_replace(action, expanded, 1); + free(expanded); window_choose_add(wp, wcd); diff --git a/window-clock.c b/window-clock.c index 4cd3f6a1..61cf1502 100644 --- a/window-clock.c +++ b/window-clock.c @@ -83,7 +83,6 @@ window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) window_clock_draw_screen(wp); } -/* ARGSUSED */ void window_clock_key( struct window_pane *wp, unused struct session *sess, unused int key) diff --git a/window-copy.c b/window-copy.c index c319aba6..51a8f108 100644 --- a/window-copy.c +++ b/window-copy.c @@ -52,6 +52,10 @@ void window_copy_goto_line(struct window_pane *, const char *); void window_copy_update_cursor(struct window_pane *, u_int, u_int); void window_copy_start_selection(struct window_pane *); int window_copy_update_selection(struct window_pane *); +void *window_copy_get_selection(struct window_pane *, size_t *); +void window_copy_copy_buffer(struct window_pane *, int, void *, size_t); +void window_copy_copy_pipe( + struct window_pane *, struct session *, int, const char *); void window_copy_copy_selection(struct window_pane *, int); void window_copy_clear_selection(struct window_pane *); void window_copy_copy_line( @@ -364,6 +368,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) u_int n; int np, keys; enum mode_key_cmd cmd; + const char *arg; np = data->numprefix; if (np <= 0) @@ -405,7 +410,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) return; } - cmd = mode_key_lookup(&data->mdata, key); + cmd = mode_key_lookup(&data->mdata, key, &arg); switch (cmd) { case MODEKEYCOPY_CANCEL: window_pane_reset_mode(wp); @@ -533,6 +538,13 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) window_copy_clear_selection(wp); window_copy_redraw_screen(wp); break; + case MODEKEYCOPY_COPYPIPE: + if (sess != NULL) { + window_copy_copy_pipe(wp, sess, data->numprefix, arg); + window_pane_reset_mode(wp); + return; + } + break; case MODEKEYCOPY_COPYSELECTION: if (sess != NULL) { window_copy_copy_selection(wp, data->numprefix); @@ -735,7 +747,7 @@ window_copy_key_input(struct window_pane *wp, int key) size_t inputlen; int np; - switch (mode_key_lookup(&data->mdata, key)) { + switch (mode_key_lookup(&data->mdata, key, NULL)) { case MODEKEYEDIT_CANCEL: data->numprefix = -1; return (-1); @@ -814,7 +826,6 @@ window_copy_key_numeric_prefix(struct window_pane *wp, int key) return (0); } -/* ARGSUSED */ void window_copy_mouse( struct window_pane *wp, struct session *sess, struct mouse_event *m) @@ -1260,19 +1271,19 @@ window_copy_update_selection(struct window_pane *wp) return (1); } -void -window_copy_copy_selection(struct window_pane *wp, int idx) +void * +window_copy_get_selection(struct window_pane *wp, size_t *len) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; char *buf; size_t off; - u_int i, xx, yy, sx, sy, ex, ey, limit; + u_int i, xx, yy, sx, sy, ex, ey; u_int firstsx, lastex, restex, restsx; int keys; if (!s->sel.flag) - return; + return (NULL); buf = xmalloc(1); off = 0; @@ -1365,19 +1376,61 @@ window_copy_copy_selection(struct window_pane *wp, int idx) /* Don't bother if no data. */ if (off == 0) { free(buf); - return; + return (NULL); } - off--; /* remove final \n */ + *len = off - 1; /* remove final \n */ + return (buf); +} - if (options_get_number(&global_options, "set-clipboard")) - screen_write_setselection(&wp->ictx.ctx, buf, off); +void +window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len) +{ + u_int limit; + struct screen_write_ctx ctx; + + if (options_get_number(&global_options, "set-clipboard")) { + screen_write_start(&ctx, wp, NULL); + screen_write_setselection(&ctx, buf, len); + screen_write_stop(&ctx); + } - /* Add the buffer to the stack. */ if (idx == -1) { limit = options_get_number(&global_options, "buffer-limit"); - paste_add(&global_buffers, buf, off, limit); + paste_add(&global_buffers, buf, len, limit); } else - paste_replace(&global_buffers, idx, buf, off); + paste_replace(&global_buffers, idx, buf, len); +} + +void +window_copy_copy_pipe( + struct window_pane *wp, struct session *sess, int idx, const char *arg) +{ + void *buf; + size_t len; + struct job *job; + + + buf = window_copy_get_selection(wp, &len); + if (buf == NULL) + return; + + job = job_run(arg, sess, NULL, NULL, NULL); + bufferevent_write(job->event, buf, len); + + window_copy_copy_buffer(wp, idx, buf, len); +} + +void +window_copy_copy_selection(struct window_pane *wp, int idx) +{ + void* buf; + size_t len; + + buf = window_copy_get_selection(wp, &len); + if (buf == NULL) + return; + + window_copy_copy_buffer(wp, idx, buf, len); } void diff --git a/window.c b/window.c index aed7596b..2be51e93 100644 --- a/window.c +++ b/window.c @@ -307,24 +307,36 @@ window_create1(u_int sx, u_int sy) struct window * window_create(const char *name, const char *cmd, const char *shell, const char *cwd, struct environ *env, struct termios *tio, - u_int sx, u_int sy, u_int hlimit,char **cause) + u_int sx, u_int sy, u_int hlimit, char **cause) { struct window *w; struct window_pane *wp; + const char *prefix; + char *cmd1; w = window_create1(sx, sy); wp = window_add_pane(w, hlimit); - layout_init(w); - if (window_pane_spawn(wp, cmd, shell, cwd, env, tio, cause) != 0) { + layout_init(w, wp); + + if (*cmd != '\0') { + prefix = options_get_string(&w->options, "command-prefix"); + xasprintf(&cmd1, "%s%s", prefix, cmd); + } else + cmd1 = xstrdup(""); + if (window_pane_spawn(wp, cmd1, shell, cwd, env, tio, cause) != 0) { window_destroy(w); + free(cmd1); return (NULL); } + free(cmd1); + w->active = TAILQ_FIRST(&w->panes); if (name != NULL) { w->name = xstrdup(name); options_set_number(&w->options, "automatic-rename", 0); } else w->name = default_window_name(w); + return (w); } @@ -333,6 +345,8 @@ window_destroy(struct window *w) { u_int i; + window_unzoom(w); + if (window_index(w, &i) != 0) fatalx("index not found"); ARRAY_SET(&windows, i, NULL); @@ -455,6 +469,58 @@ window_find_string(struct window *w, const char *s) return (window_get_active_at(w, x, y)); } +int +window_zoom(struct window_pane *wp) +{ + struct window *w = wp->window; + struct window_pane *wp1; + + if (w->flags & WINDOW_ZOOMED) + return (-1); + + if (!window_pane_visible(wp)) + return (-1); + + if (window_count_panes(w) == 1) + return (-1); + + if (w->active != wp) + window_set_active_pane(w, wp); + + TAILQ_FOREACH(wp1, &w->panes, entry) { + wp1->saved_layout_cell = wp1->layout_cell; + wp1->layout_cell = NULL; + } + + w->saved_layout_root = w->layout_root; + layout_init(w, wp); + w->flags |= WINDOW_ZOOMED; + + return (0); +} + +int +window_unzoom(struct window *w) +{ + struct window_pane *wp, *wp1; + + if (!(w->flags & WINDOW_ZOOMED)) + return (-1); + wp = w->active; + + w->flags &= ~WINDOW_ZOOMED; + layout_free(w); + w->layout_root = w->saved_layout_root; + + TAILQ_FOREACH(wp1, &w->panes, entry) { + wp1->layout_cell = wp1->saved_layout_cell; + wp1->saved_layout_cell = NULL; + } + layout_fix_panes(w, w->sx, w->sy); + + return (0); +} + struct window_pane * window_add_pane(struct window *w, u_int hlimit) { @@ -585,6 +651,8 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; + if (wl->window->flags & WINDOW_ZOOMED) + flags[pos++] = 'Z'; if (pos == 0) flags[pos++] = ' '; flags[pos] = '\0'; @@ -701,6 +769,8 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, wp->cwd = xstrdup(cwd); } + log_debug("spawn: %s -- %s", wp->shell, wp->cmd); + memset(&ws, 0, sizeof ws); ws.ws_col = screen_size_x(&wp->base); ws.ws_row = screen_size_y(&wp->base); @@ -803,7 +873,6 @@ window_pane_timer_callback(unused int fd, unused short events, void *data) wp->changes = 0; } -/* ARGSUSED */ void window_pane_read_callback(unused struct bufferevent *bufev, void *data) { @@ -830,7 +899,6 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) fatal("gettimeofday failed."); } -/* ARGSUSED */ void window_pane_error_callback( unused struct bufferevent *bufev, unused short what, void *data) @@ -843,32 +911,16 @@ window_pane_error_callback( void window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) { - struct winsize ws; - if (sx == wp->sx && sy == wp->sy) return; wp->sx = sx; wp->sy = sy; - memset(&ws, 0, sizeof ws); - ws.ws_col = sx; - ws.ws_row = sy; - screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL); if (wp->mode != NULL) wp->mode->resize(wp, sx, sy); - if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) -#ifdef __sun - /* - * Some versions of Solaris apparently can return an error when - * resizing; don't know why this happens, can't reproduce on - * other platforms and ignoring it doesn't seem to cause any - * issues. - */ - if (errno != EINVAL) -#endif - fatal("ioctl failed"); + wp->flags |= PANE_RESIZE; } /* @@ -1034,6 +1086,8 @@ window_pane_visible(struct window_pane *wp) { struct window *w = wp->window; + if (wp->layout_cell == NULL) + return (0); if (wp->xoff >= w->sx || wp->yoff >= w->sy) return (0); if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy) diff --git a/xterm-keys.c b/xterm-keys.c index f09072c2..8c885875 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -87,6 +87,34 @@ const struct xterm_keys_entry xterm_keys_table[] = { { KEYC_NPAGE, "\033[6;_~" }, { KEYC_IC, "\033[2;_~" }, { KEYC_DC, "\033[3;_~" }, + + { '!', "\033[27;_;33~" }, + { '#', "\033[27;_;35~" }, + { '(', "\033[27;_;40~" }, + { ')', "\033[27;_;41~" }, + { '+', "\033[27;_;43~" }, + { ',', "\033[27;_;44~" }, + { '-', "\033[27;_;45~" }, + { '.', "\033[27;_;46~" }, + { '0', "\033[27;_;48~" }, + { '1', "\033[27;_;49~" }, + { '2', "\033[27;_;50~" }, + { '3', "\033[27;_;51~" }, + { '4', "\033[27;_;52~" }, + { '5', "\033[27;_;53~" }, + { '6', "\033[27;_;54~" }, + { '7', "\033[27;_;55~" }, + { '8', "\033[27;_;56~" }, + { '9', "\033[27;_;57~" }, + { ':', "\033[27;_;58~" }, + { ';', "\033[27;_;59~" }, + { '<', "\033[27;_;60~" }, + { '=', "\033[27;_;61~" }, + { '>', "\033[27;_;62~" }, + { '?', "\033[27;_;63~" }, + { '\'', "\033[27;_;39~" }, + { '\r', "\033[27;_;13~" }, + { '\t', "\033[27;_;9~" }, }; /*