diff options
113 files changed, 5575 insertions, 2518 deletions
diff --git a/.github/lock.yml b/.github/lock.yml index 89126482..08cf2fc0 100644 --- a/.github/lock.yml +++ b/.github/lock.yml @@ -1,4 +1,4 @@ -daysUntilLock: 180 +daysUntilLock: 30 skipCreatedBefore: false exemptLabels: [] lockLabel: false @@ -1,3 +1,130 @@ +CHANGES FROM 3.1 TO 3.2 + +* Add extension terminfo(5) capabilities for margins. + +* Try $XDG_CONFIG_HOME/tmux/tmux.conf as well as ~/.config/tmux/tmux.conf for + configuration file (the search paths are in TMUX_CONF in Makefile.am). + +* Remove the DSR 1337 iTerm2 extension and replace by the extended device + attributes sequence (CSI > q) supported by more terminals. + +* Add a -s flag to copy-mode to specify a different pane for the source + content. This means it is possible to view two places in a pane's history at + the same time in different panes, or view the history while still using the + pane. Pressing r refreshes the content from the source pane. + +* Add an argument to list-commands to show only a single command. + +* Change copy mode to make copy of the pane history so it does not need to + freeze the pane. + +* Restore pane_current_path format from portable tmux on OpenBSD. + +* Wait until the initial command sequence is done before sending a device + attributes request and other bits that prompt a reply from the terminal. This + means that stray relies are not left on the terminal if the command has + attached and then immediately detached and tmux will not be around to receive + them. + +* Add a -f filter argument to the list commands like choose-tree. + +* Move specific hooks for panes to pane options and windows for window options + rather than all hooks being session options. These hooks are now window options: + + window-layout-changed + window-linked + window-pane-changed + window-renamed + window-unlinked + + And these now pane options: + + pane-died + pane-exited + pane-focus-in + pane-focus-out + pane-mode-changed + pane-set-clipboard + + Any existing configurations using these hooks on a session rather than + globally (that is, set-hook or set-option without -g) may need to be changed. + +* Show signal names when a process exits with remain-on-exit on platforms which + have a way to get them. + +* Start menu with top item selected if no mouse and use mode-style for the + selected item. + +* Add a copy-command option and change copy-pipe and friends to pipe to it if + used without arguments, allows all the default copy key bindings to be + changed to pipe with one option rather than needing to change each key + binding individually. + +* Tidy up the terminal detection and feature code and add named sets of + terminal features, each of which are defined in one place and map to a + builtin set of terminfo(5) capabilities. Features can be specified based on + TERM with a new terminal-features option or with the -T flag when running + tmux. tmux will also detect a few common terminals from the DA and DSR + responses. + + This is intended to make it easier to configure tmux's use of terminfo(5) + even in the presence of outdated ncurses(3) or terminfo(5) databases or for + features which do not yet have a terminfo(5) entry. Instead of having to grok + terminfo(5) capability names and what they should be set to in the + terminal-overrides option, the user can hopefully just give tmux a feature + name and let it do the right thing. + + The terminal-overrides option remains both for backwards compatibility and to + allow tweaks of individual capabilities. + +* Support mintty's application escape sequence (means tmux doesn't have to + delay to wait for Escape, so no need to reduce escape-time when using + mintty). + +* Change so main-pane-width and height can be given as a percentage. + +* Support for the iTerm2 synchronized updates feature (allows the terminal to + avoid unnecessary drawing while output is still in progress). + +* Make the mouse_word and mouse_line formats work in copy mode and enable the + default pane menu in copy mode. + +* Add a -T flag to resize-pane to trim lines below the cursor, moving lines out + of the history. + +* Add a way to mark environment variables as "hidden" so they can be used by + tmux (for example in formats) but are not set in the environment for new + panes. set-environment and show-environment have a new -h flag and there is a + new %hidden statement for the configuration file. + +* Change default position for display-menu -x and -y to centre rather than top + left. + +* Add support for per-client transient popups, similar to menus. These are + created with new command display-popup. Popups may either show fixed text and + trigger a tmux command when a key is pressed, or run a program (-R flag). + +* Change double and triple click bindings so that only one is fired (previously + double click was fired on the way to triple click). Also add default double + and triple click bindings to copy the word or line under the cursor and + change the existing bindings in copy mode to do the same. + +* Add a default binding for button 2 to paste. + +* Add -d flag to run-shell to delay before running the command and allow it to + run without a command so it just delays. + +* Add C-g to cancel command prompt with vi keys as well as emacs, and q in + command mode. + +* When the server socket is given with -S, create it with umask 177 instead of + 117 (because it may not be in a safe directory like the default directory in + /tmp). + +* Add a copy-mode -H flag to hide the position marker in the top right. + +* Add number operators for formats (+, -, *, / and m), + CHANGES FROM 3.1 TO 3.1a * Do not close stdout prematurely in control mode since it is needed to print diff --git a/Makefile.am b/Makefile.am index 082a467c..a395ecdb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,8 +12,8 @@ dist_EXTRA_tmux_SOURCES = compat/*.[ch] # Preprocessor flags. AM_CPPFLAGS += @XOPEN_DEFINES@ \ - -DTMUX_VERSION="\"@VERSION@\"" \ - -DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\"" + -DTMUX_VERSION='"@VERSION@"' \ + -DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' # Additional object files. LDADD = $(LIBOBJS) @@ -154,6 +154,7 @@ dist_tmux_SOURCES = \ options-table.c \ options.c \ paste.c \ + popup.c \ proc.c \ regsub.c \ resize.c \ @@ -170,6 +171,7 @@ dist_tmux_SOURCES = \ tmux.c \ tmux.h \ tty-acs.c \ + tty-features.c \ tty-keys.c \ tty-term.c \ tty.c \ diff --git a/arguments.c b/arguments.c index e2d18980..e1956ace 100644 --- a/arguments.c +++ b/arguments.c @@ -215,8 +215,10 @@ args_escape(const char *s) char *escaped, *result; int flags; - if (*s == '\0') - return (xstrdup(s)); + if (*s == '\0') { + xasprintf(&result, "''"); + return (result); + } if (s[0] != ' ' && (strchr(quoted, s[0]) != NULL || s[0] == '~') && s[1] == '\0') { @@ -343,3 +345,60 @@ args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, *cause = NULL; return (ll); } + +/* Convert an argument to a number which may be a percentage. */ +long long +args_percentage(struct args *args, u_char ch, long long minval, + long long maxval, long long curval, char **cause) +{ + const char *value; + struct args_entry *entry; + + if ((entry = args_find(args, ch)) == NULL) { + *cause = xstrdup("missing"); + return (0); + } + value = TAILQ_LAST(&entry->values, args_values)->value; + return (args_string_percentage(value, minval, maxval, curval, cause)); +} + +/* Convert a string to a number which may be a percentage. */ +long long +args_string_percentage(const char *value, long long minval, long long maxval, + long long curval, char **cause) +{ + const char *errstr; + long long ll; + size_t valuelen = strlen(value); + char *copy; + + if (value[valuelen - 1] == '%') { + copy = xstrdup(value); + copy[valuelen - 1] = '\0'; + + ll = strtonum(copy, 0, 100, &errstr); + free(copy); + if (errstr != NULL) { + *cause = xstrdup(errstr); + return (0); + } + ll = (curval * ll) / 100; + if (ll < minval) { + *cause = xstrdup("too large"); + return (0); + } + if (ll > maxval) { + *cause = xstrdup("too small"); + return (0); + } + } else { + ll = strtonum(value, minval, maxval, &errstr); + if (errstr != NULL) { + *cause = xstrdup(errstr); + return (0); + } + } + + *cause = NULL; + return (ll); +} diff --git a/attributes.c b/attributes.c index ca88a056..b839f06d 100644 --- a/attributes.c +++ b/attributes.c @@ -31,7 +31,8 @@ attributes_tostring(int attr) if (attr == 0) return ("none"); - len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s", + len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + (attr & GRID_ATTR_CHARSET) ? "acs," : "", (attr & GRID_ATTR_BRIGHT) ? "bright," : "", (attr & GRID_ATTR_DIM) ? "dim," : "", (attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "", @@ -62,6 +63,7 @@ attributes_fromstring(const char *str) const char *name; int attr; } table[] = { + { "acs", GRID_ATTR_CHARSET }, { "bright", GRID_ATTR_BRIGHT }, { "bold", GRID_ATTR_BRIGHT }, { "dim", GRID_ATTR_DIM }, @@ -66,45 +66,12 @@ set_cfg_file(const char *path) cfg_file = xstrdup(path); } -static char * -expand_cfg_file(const char *path, const char *home) -{ - char *expanded, *name; - const char *end; - struct environ_entry *value; - - if (strncmp(path, "~/", 2) == 0) { - if (home == NULL) - return (NULL); - xasprintf(&expanded, "%s%s", home, path + 1); - return (expanded); - } - - if (*path == '$') { - end = strchr(path, '/'); - if (end == NULL) - name = xstrdup(path + 1); - else - name = xstrndup(path + 1, end - path - 1); - value = environ_find(global_environ, name); - free(name); - if (value == NULL) - return (NULL); - if (end == NULL) - end = ""; - xasprintf(&expanded, "%s%s", value->value, end); - return (expanded); - } - - return (xstrdup(path)); -} - void start_cfg(void) { - const char *home = find_home(); - struct client *c; - char *path, *copy, *next, *expanded; + struct client *c; + char **paths; + u_int i, n; /* * Configuration files are loaded without a client, so commands are run @@ -123,18 +90,12 @@ start_cfg(void) } if (cfg_file == NULL) { - path = copy = xstrdup(TMUX_CONF); - while ((next = strsep(&path, ":")) != NULL) { - expanded = expand_cfg_file(next, home); - if (expanded == NULL) { - log_debug("couldn't expand %s", next); - continue; - } - log_debug("expanded %s to %s", next, expanded); - load_cfg(expanded, c, NULL, CMD_PARSE_QUIET, NULL); - free(expanded); + expand_paths(TMUX_CONF, &paths, &n); + for (i = 0; i < n; i++) { + load_cfg(paths[i], c, NULL, CMD_PARSE_QUIET, NULL); + free(paths[i]); } - free(copy); + free(paths); } else load_cfg(cfg_file, c, NULL, 0, NULL); @@ -182,7 +143,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, return (0); } - new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); + new_item0 = cmdq_get_command(pr->cmdlist, NULL); if (item != NULL) new_item0 = cmdq_insert_after(item, new_item0); else @@ -228,7 +189,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path, return (0); } - new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); + new_item0 = cmdq_get_command(pr->cmdlist, NULL); if (item != NULL) new_item0 = cmdq_insert_after(item, new_item0); else @@ -283,7 +244,7 @@ cfg_show_causes(struct session *s) wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) - window_pane_set_mode(wp, &window_view_mode, NULL, NULL); + window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); for (i = 0; i < cfg_ncauses; i++) { window_copy_add(wp, "%s", cfg_causes[i]); free(cfg_causes[i]); @@ -57,7 +57,7 @@ static struct client_files client_files = RB_INITIALIZER(&client_files); static __dead void client_exec(const char *,const char *); static int client_get_lock(char *); static int client_connect(struct event_base *, const char *, int); -static void client_send_identify(const char *, const char *); +static void client_send_identify(const char *, const char *, int); static void client_signal(int); static void client_dispatch(struct imsg *, void *); static void client_dispatch_attached(struct imsg *); @@ -97,7 +97,7 @@ client_get_lock(char *lockfile) /* Connect client to server. */ static int -client_connect(struct event_base *base, const char *path, int start_server) +client_connect(struct event_base *base, const char *path, int flags) { struct sockaddr_un sa; size_t size; @@ -122,7 +122,7 @@ retry: log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; - if (!start_server) + if (~flags & CLIENT_STARTSERVER) goto failed; close(fd); @@ -154,7 +154,7 @@ retry: close(lockfd); return (-1); } - fd = server_start(client_proc, base, lockfd, lockfile); + fd = server_start(client_proc, flags, base, lockfd, lockfile); } if (locked && lockfd >= 0) { @@ -233,12 +233,11 @@ client_exit(void) /* Client main loop. */ int -client_main(struct event_base *base, int argc, char **argv, int flags) +client_main(struct event_base *base, int argc, char **argv, int flags, int feat) { struct cmd_parse_result *pr; - struct cmd *cmd; struct msg_command *data; - int cmdflags, fd, i; + int fd, i; const char *ttynam, *cwd; pid_t ppid; enum msgtype msg; @@ -248,17 +247,13 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ signal(SIGCHLD, SIG_IGN); - /* Save the flags. */ - client_flags = flags; - /* Set up the initial command. */ - cmdflags = 0; if (shell_command != NULL) { msg = MSG_SHELL; - cmdflags = CMD_STARTSERVER; + flags |= CLIENT_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; - cmdflags = CMD_STARTSERVER; + flags |= CLIENT_STARTSERVER; } else { msg = MSG_COMMAND; @@ -269,21 +264,22 @@ client_main(struct event_base *base, int argc, char **argv, int flags) */ pr = cmd_parse_from_arguments(argc, argv, NULL); if (pr->status == CMD_PARSE_SUCCESS) { - TAILQ_FOREACH(cmd, &pr->cmdlist->list, qentry) { - if (cmd->entry->flags & CMD_STARTSERVER) - cmdflags |= CMD_STARTSERVER; - } + if (cmd_list_any_have(pr->cmdlist, CMD_STARTSERVER)) + flags |= CLIENT_STARTSERVER; cmd_list_free(pr->cmdlist); } else free(pr->error); } + /* Save the flags. */ + client_flags = flags; + /* Create client process structure (starts logging). */ client_proc = proc_start("client"); proc_set_signals(client_proc, client_signal); /* Initialize the client socket and start the server. */ - fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); + fd = client_connect(base, socket_path, client_flags); if (fd == -1) { if (errno == ECONNREFUSED) { fprintf(stderr, "no server running on %s\n", @@ -346,7 +342,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Send identify messages. */ - client_send_identify(ttynam, cwd); + client_send_identify(ttynam, cwd, feat); /* Send first command. */ if (msg == MSG_COMMAND) { @@ -412,7 +408,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Send identify messages to server. */ static void -client_send_identify(const char *ttynam, const char *cwd) +client_send_identify(const char *ttynam, const char *cwd, int feat) { const char *s; char **ss; @@ -425,6 +421,7 @@ client_send_identify(const char *ttynam, const char *cwd) if ((s = getenv("TERM")) == NULL) s = ""; proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); + proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat); proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); @@ -647,7 +644,7 @@ client_read_open(void *data, size_t datalen) struct msg_read_done reply; struct client_file find, *cf; const int flags = O_NONBLOCK|O_RDONLY; - int error = 0; + int error; if (datalen < sizeof *msg) fatalx("bad MSG_READ_OPEN size"); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 477d3517..8c30c767 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -50,10 +50,11 @@ enum cmd_retval cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, int xflag, int rflag, const char *cflag, int Eflag) { - struct cmd_find_state *current = &item->shared->current; + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state target; enum cmd_find_type type; int flags; - struct client *c = item->client, *c_loop; + struct client *c = cmdq_get_client(item), *c_loop; struct session *s; struct winlink *wl; struct window_pane *wp; @@ -80,11 +81,11 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, type = CMD_FIND_SESSION; flags = CMD_FIND_PREFER_UNATTACHED; } - if (cmd_find_target(&item->target, item, tflag, type, flags) != 0) + if (cmd_find_target(&target, item, tflag, type, flags) != 0) return (CMD_RETURN_ERROR); - s = item->target.s; - wl = item->target.wl; - wp = item->target.wp; + s = target.s; + wl = target.wl; + wp = target.wp; if (wl != NULL) { if (wp != NULL) @@ -118,7 +119,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, environ_update(s->options, c->environ, s->environ); c->session = s; - if (~item->shared->flags & CMDQ_SHARED_REPEAT) + if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); @@ -177,7 +178,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, static enum cmd_retval cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); return (cmd_attach_session(item, args_get(args, 't'), args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'), diff --git a/cmd-bind-key.c b/cmd-bind-key.c index bc6a3d40..dcb56c06 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -44,7 +44,7 @@ const struct cmd_entry cmd_bind_key_entry = { static enum cmd_retval cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); key_code key; const char *tablename, *note; struct cmd_parse_result *pr; diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 6c638103..87892d73 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_break_pane_entry = { .name = "break-pane", .alias = "breakp", - .args = { "dPF:n:s:t:", 0, 0 }, - .usage = "[-dP] [-F format] [-n window-name] [-s src-pane] " + .args = { "adPF:n:s:t:", 0, 0 }, + .usage = "[-adP] [-F format] [-n window-name] [-s src-pane] " "[-t dst-window]", .source = { 's', CMD_FIND_PANE, 0 }, @@ -48,29 +48,45 @@ const struct cmd_entry cmd_break_pane_entry = { static enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct cmd_find_state *current = &item->shared->current; - struct client *c = cmd_find_client(item, NULL, 1); - struct winlink *wl = item->source.wl; - struct session *src_s = item->source.s; - struct session *dst_s = item->target.s; - struct window_pane *wp = item->source.wp; + struct args *args = cmd_get_args(self); + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct cmd_find_state *source = cmdq_get_source(item); + struct client *tc = cmdq_get_target_client(item); + struct winlink *wl = source->wl; + struct session *src_s = source->s; + struct session *dst_s = target->s; + struct window_pane *wp = source->wp; struct window *w = wl->window; char *name, *cause; - int idx = item->target.idx; + int idx = target->idx; const char *template; char *cp; - if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { - cmdq_error(item, "index %d already in use", idx); - return (CMD_RETURN_ERROR); + if (args_has(args, 'a')) { + if (target->wl != NULL) + idx = winlink_shuffle_up(dst_s, target->wl); + else + idx = winlink_shuffle_up(dst_s, dst_s->curw); + if (idx == -1) + return (CMD_RETURN_ERROR); } + server_unzoom_window(w); if (window_count_panes(w) == 1) { - cmdq_error(item, "can't break with only one pane"); + if (server_link_window(src_s, wl, dst_s, idx, 0, + !args_has(args, 'd'), &cause) != 0) { + cmdq_error(item, "%s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + server_unlink_window(src_s, wl); + return (CMD_RETURN_NORMAL); + } + if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { + cmdq_error(item, "index in use: %d", idx); return (CMD_RETURN_ERROR); } - server_unzoom_window(w); TAILQ_REMOVE(&w->panes, wp, entry); window_lost_pane(w, wp); @@ -81,7 +97,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) wp->flags |= PANE_STYLECHANGED; TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; - w->latest = c; + w->latest = tc; if (!args_has(args, 'n')) { name = default_window_name(w); @@ -98,7 +114,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) if (idx == -1) idx = -1 - options_get_number(dst_s->options, "base-index"); wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ - if (!args_has(self->args, 'd')) { + if (!args_has(args, 'd')) { session_select(dst_s, wl->idx); cmd_find_from_session(current, dst_s, 0); } @@ -113,7 +129,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; - cp = format_single(item, template, c, dst_s, wl, wp); + cp = format_single(item, template, tc, dst_s, wl, wp); cmdq_print(item, "%s", cp); free(cp); } diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 18be3f77..588b0fd5 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -80,7 +80,7 @@ cmd_capture_pane_pending(struct args *args, struct window_pane *wp, size_t linelen; u_int i; - pending = input_pending(wp); + pending = input_pending(wp->ictx); if (pending == NULL) return (xstrdup("")); @@ -118,7 +118,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, sx = screen_size_x(&wp->base); if (args_has(args, 'a')) { - gd = wp->saved_grid; + gd = wp->base.saved_grid; if (gd == NULL) { if (!args_has(args, 'q')) { cmdq_error(item, "no alternate screen"); @@ -192,14 +192,14 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, static enum cmd_retval cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c = item->client; - struct window_pane *wp = item->target.wp; + struct args *args = cmd_get_args(self); + struct client *c = cmdq_get_client(item); + struct window_pane *wp = cmdq_get_target(item)->wp; char *buf, *cause; const char *bufname; size_t len; - if (self->entry == &cmd_clear_history_entry) { + if (cmd_get_entry(self) == &cmd_clear_history_entry) { window_pane_reset_mode_all(wp); grid_clear_history(wp->base.grid); return (CMD_RETURN_NORMAL); diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 8178ec9f..0ada8fd4 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -71,21 +71,22 @@ const struct cmd_entry cmd_choose_buffer_entry = { static enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct window_pane *wp = item->target.wp; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct window_pane *wp = target->wp; const struct window_mode *mode; - if (self->entry == &cmd_choose_buffer_entry) { + if (cmd_get_entry(self) == &cmd_choose_buffer_entry) { if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); mode = &window_buffer_mode; - } else if (self->entry == &cmd_choose_client_entry) { + } else if (cmd_get_entry(self) == &cmd_choose_client_entry) { if (server_client_how_many() == 0) return (CMD_RETURN_NORMAL); mode = &window_client_mode; } else mode = &window_tree_mode; - window_pane_set_mode(wp, mode, &item->target, args); + window_pane_set_mode(wp, NULL, mode, target, args); return (CMD_RETURN_NORMAL); } diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 9f0ea19f..e53c4320 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -44,7 +44,7 @@ const struct cmd_entry cmd_command_prompt_entry = { .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " "[template]", - .flags = 0, + .flags = CMD_CLIENT_TFLAG, .exec = cmd_command_prompt_exec }; @@ -64,17 +64,14 @@ struct cmd_command_prompt_cdata { static enum cmd_retval cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); const char *inputs, *prompts; struct cmd_command_prompt_cdata *cdata; - struct client *c; char *prompt, *ptr, *input = NULL; size_t n; - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - - if (c->prompt_string != NULL) + if (tc->prompt_string != NULL) return (CMD_RETURN_NORMAL); cdata = xcalloc(1, sizeof *cdata); @@ -124,7 +121,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_INCREMENTAL; else if (args_has(args, 'k')) cdata->flags |= PROMPT_KEY; - status_prompt_set(c, prompt, input, cmd_command_prompt_callback, + status_prompt_set(tc, prompt, input, cmd_command_prompt_callback, cmd_command_prompt_free, cdata, cdata->flags); free(prompt); @@ -136,10 +133,9 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, int done) { struct cmd_command_prompt_cdata *cdata = data; - struct cmdq_item *new_item; - char *new_template, *prompt, *ptr; + char *new_template, *prompt, *ptr, *error; char *input = NULL; - struct cmd_parse_result *pr; + enum cmd_parse_status status; if (s == NULL) return (0); @@ -166,21 +162,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, return (1); } - pr = cmd_parse_from_string(new_template, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - new_item = NULL; - break; - case CMD_PARSE_ERROR: - new_item = cmdq_get_error(pr->error); - free(pr->error); - cmdq_append(c, new_item); - break; - case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); - cmd_list_free(pr->cmdlist); - cmdq_append(c, new_item); - break; + status = cmd_parse_and_append(new_template, NULL, c, NULL, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_append(c, cmdq_get_error(error)); + free(error); } if (!done) diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index be21a78b..0d881178 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -42,7 +42,7 @@ const struct cmd_entry cmd_confirm_before_entry = { .args = { "p:t:", 1, 1 }, .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", - .flags = 0, + .flags = CMD_CLIENT_TFLAG, .exec = cmd_confirm_before_exec }; @@ -53,15 +53,12 @@ struct cmd_confirm_before_data { static enum cmd_retval cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_confirm_before_data *cdata; - struct client *c; + struct client *tc = cmdq_get_target_client(item); char *cmd, *copy, *new_prompt, *ptr; const char *prompt; - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if ((prompt = args_get(args, 'p')) != NULL) xasprintf(&new_prompt, "%s ", prompt); else { @@ -74,9 +71,8 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) cdata = xmalloc(sizeof *cdata); cdata->cmd = xstrdup(args->argv[0]); - status_prompt_set(c, new_prompt, NULL, - cmd_confirm_before_callback, cmd_confirm_before_free, cdata, - PROMPT_SINGLE); + status_prompt_set(tc, new_prompt, NULL, cmd_confirm_before_callback, + cmd_confirm_before_free, cdata, PROMPT_SINGLE); free(new_prompt); return (CMD_RETURN_NORMAL); @@ -87,8 +83,8 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, __unused int done) { struct cmd_confirm_before_data *cdata = data; - struct cmdq_item *new_item; - struct cmd_parse_result *pr; + char *error; + enum cmd_parse_status status; if (c->flags & CLIENT_DEAD) return (0); @@ -98,21 +94,10 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, if (tolower((u_char)s[0]) != 'y' || s[1] != '\0') return (0); - pr = cmd_parse_from_string(cdata->cmd, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - new_item = NULL; - break; - case CMD_PARSE_ERROR: - new_item = cmdq_get_error(pr->error); - free(pr->error); - cmdq_append(c, new_item); - break; - case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); - cmd_list_free(pr->cmdlist); - cmdq_append(c, new_item); - break; + status = cmd_parse_and_append(cdata->cmd, NULL, c, NULL, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_append(c, cmdq_get_error(error)); + free(error); } return (0); diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index bdb8245e..d8b4fd3e 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -30,9 +30,10 @@ const struct cmd_entry cmd_copy_mode_entry = { .name = "copy-mode", .alias = NULL, - .args = { "eHMt:uq", 0, 0 }, - .usage = "[-eHMuq] " CMD_TARGET_PANE_USAGE, + .args = { "eHMs:t:uq", 0, 0 }, + .usage = "[-eHMuq] [-s src-pane] " CMD_TARGET_PANE_USAGE, + .source = { 's', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, @@ -55,11 +56,13 @@ const struct cmd_entry cmd_clock_mode_entry = { static enum cmd_retval cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct cmdq_shared *shared = item->shared; - struct client *c = item->client; + struct args *args = cmd_get_args(self); + struct key_event *event = cmdq_get_event(item); + struct cmd_find_state *source = cmdq_get_source(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *c = cmdq_get_client(item); struct session *s; - struct window_pane *wp = item->target.wp; + struct window_pane *wp = target->wp, *swp; if (args_has(args, 'q')) { window_pane_reset_mode_all(wp); @@ -67,22 +70,26 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'M')) { - if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL) + if ((wp = cmd_mouse_pane(&event->m, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); } - if (self->entry == &cmd_clock_mode_entry) { - window_pane_set_mode(wp, &window_clock_mode, NULL, NULL); + if (cmd_get_entry(self) == &cmd_clock_mode_entry) { + window_pane_set_mode(wp, NULL, &window_clock_mode, NULL, NULL); return (CMD_RETURN_NORMAL); } - if (!window_pane_set_mode(wp, &window_copy_mode, NULL, args)) { + if (args_has(args, 's')) + swp = source->wp; + else + swp = wp; + if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) { if (args_has(args, 'M')) - window_copy_start_drag(c, &shared->mouse); + window_copy_start_drag(c, &event->m); } - if (args_has(self->args, 'u')) + if (args_has(args, 'u')) window_copy_pageup(wp, 0); return (CMD_RETURN_NORMAL); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 85b9a4ed..02a43f4e 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_detach_client_entry = { .source = { 's', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, - .flags = CMD_READONLY, + .flags = CMD_READONLY|CMD_CLIENT_TFLAG, .exec = cmd_detach_client_exec }; @@ -50,24 +50,22 @@ const struct cmd_entry cmd_suspend_client_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, - .flags = 0, + .flags = CMD_CLIENT_TFLAG, .exec = cmd_detach_client_exec }; static enum cmd_retval cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c, *cloop; - struct session *s; - enum msgtype msgtype; - const char *cmd = args_get(args, 'E'); - - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - - if (self->entry == &cmd_suspend_client_entry) { - server_client_suspend(c); + struct args *args = cmd_get_args(self); + struct cmd_find_state *source = cmdq_get_source(item); + struct client *tc = cmdq_get_target_client(item), *loop; + struct session *s; + enum msgtype msgtype; + const char *cmd = args_get(args, 'E'); + + if (cmd_get_entry(self) == &cmd_suspend_client_entry) { + server_client_suspend(tc); return (CMD_RETURN_NORMAL); } @@ -77,35 +75,35 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item) msgtype = MSG_DETACH; if (args_has(args, 's')) { - s = item->source.s; + s = source->s; if (s == NULL) return (CMD_RETURN_NORMAL); - TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session == s) { + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->session == s) { if (cmd != NULL) - server_client_exec(cloop, cmd); + server_client_exec(loop, cmd); else - server_client_detach(cloop, msgtype); + server_client_detach(loop, msgtype); } } return (CMD_RETURN_STOP); } if (args_has(args, 'a')) { - TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session != NULL && cloop != c) { + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->session != NULL && loop != tc) { if (cmd != NULL) - server_client_exec(cloop, cmd); + server_client_exec(loop, cmd); else - server_client_detach(cloop, msgtype); + server_client_detach(loop, msgtype); } } return (CMD_RETURN_NORMAL); } if (cmd != NULL) - server_client_exec(c, cmd); + server_client_exec(tc, cmd); else - server_client_detach(c, msgtype); + server_client_detach(tc, msgtype); return (CMD_RETURN_STOP); } diff --git a/cmd-display-menu.c b/cmd-display-menu.c index ac7a4cfe..0a5c7f78 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -29,6 +29,8 @@ static enum cmd_retval cmd_display_menu_exec(struct cmd *, struct cmdq_item *); +static enum cmd_retval cmd_display_popup_exec(struct cmd *, + struct cmdq_item *); const struct cmd_entry cmd_display_menu_entry = { .name = "display-menu", @@ -40,44 +42,164 @@ const struct cmd_entry cmd_display_menu_entry = { .target = { 't', CMD_FIND_PANE, 0 }, - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG, .exec = cmd_display_menu_exec }; +const struct cmd_entry cmd_display_popup_entry = { + .name = "display-popup", + .alias = "popup", + + .args = { "CEKc:d:h:R:t:w:x:y:", 0, -1 }, + .usage = "[-CEK] [-c target-client] [-d start-directory] [-h height] " + "[-R shell-command] " CMD_TARGET_PANE_USAGE " [-w width] " + "[-x position] [-y position] [command line ...]", + + .target = { 't', CMD_FIND_PANE, 0 }, + + .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG, + .exec = cmd_display_popup_exec +}; + +static void +cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, + struct args *args, u_int *px, u_int *py, u_int w, u_int h) +{ + struct tty *tty = &tc->tty; + struct cmd_find_state *target = cmdq_get_target(item); + struct key_event *event = cmdq_get_event(item); + struct session *s = tc->session; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; + struct style_ranges *ranges; + struct style_range *sr; + const char *xp, *yp; + u_int line, ox, oy, sx, sy, lines; + + lines = status_line_size(tc); + for (line = 0; line < lines; line++) { + ranges = &tc->status.entries[line].ranges; + TAILQ_FOREACH(sr, ranges, entry) { + if (sr->type == STYLE_RANGE_WINDOW) + break; + } + if (sr != NULL) + break; + } + if (line == lines) + ranges = &tc->status.entries[0].ranges; + + xp = args_get(args, 'x'); + if (xp == NULL || strcmp(xp, "C") == 0) + *px = (tty->sx - 1) / 2 - w / 2; + else if (strcmp(xp, "R") == 0) + *px = tty->sx - 1; + else if (strcmp(xp, "P") == 0) { + tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); + if (wp->xoff >= ox) + *px = wp->xoff - ox; + else + *px = 0; + } else if (strcmp(xp, "M") == 0) { + if (event->m.valid && event->m.x > w / 2) + *px = event->m.x - w / 2; + else + *px = 0; + } else if (strcmp(xp, "W") == 0) { + if (status_at_line(tc) == -1) + *px = 0; + else { + TAILQ_FOREACH(sr, ranges, entry) { + if (sr->type != STYLE_RANGE_WINDOW) + continue; + if (sr->argument == (u_int)wl->idx) + break; + } + if (sr != NULL) + *px = sr->start; + else + *px = 0; + } + } else + *px = strtoul(xp, NULL, 10); + if ((*px) + w >= tty->sx) + *px = tty->sx - w; + + yp = args_get(args, 'y'); + if (yp == NULL || strcmp(yp, "C") == 0) + *py = (tty->sy - 1) / 2 + h / 2; + else if (strcmp(yp, "P") == 0) { + tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); + if (wp->yoff + wp->sy >= oy) + *py = wp->yoff + wp->sy - oy; + else + *py = 0; + } else if (strcmp(yp, "M") == 0) { + if (event->m.valid) + *py = event->m.y + h; + else + *py = 0; + } else if (strcmp(yp, "S") == 0) { + if (options_get_number(s->options, "status-position") == 0) { + if (lines != 0) + *py = lines + h; + else + *py = 0; + } else { + if (lines != 0) + *py = tty->sy - lines; + else + *py = tty->sy; + } + } else if (strcmp(yp, "W") == 0) { + if (options_get_number(s->options, "status-position") == 0) { + if (lines != 0) + *py = line + 1 + h; + else + *py = 0; + } else { + if (lines != 0) + *py = tty->sy - lines + line; + else + *py = tty->sy; + } + } else + *py = strtoul(yp, NULL, 10); + if (*py < h) + *py = 0; + else + *py -= h; + if ((*py) + h >= tty->sy) + *py = tty->sy - h; +} + static enum cmd_retval cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; - struct cmd_find_state *fs = &item->target; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct key_event *event = cmdq_get_event(item); + struct client *tc = cmdq_get_target_client(item); struct menu *menu = NULL; - struct style_range *sr; struct menu_item menu_item; - const char *xp, *yp, *key; + const char *key; char *title, *name; - int at, flags, i; - u_int px, py, ox, oy, sx, sy; + int flags = 0, i; + u_int px, py; - if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (c->overlay_draw != NULL) + if (tc->overlay_draw != NULL) return (CMD_RETURN_NORMAL); - at = status_at_line(c); if (args_has(args, 'T')) - title = format_single(NULL, args_get(args, 'T'), c, s, wl, wp); + title = format_single_from_target(item, args_get(args, 'T')); else title = xstrdup(""); - menu = menu_create(title); for (i = 0; i != args->argc; /* nothing */) { name = args->argv[i++]; if (*name == '\0') { - menu_add_item(menu, NULL, item, c, fs); + menu_add_item(menu, NULL, item, tc, target); continue; } @@ -93,7 +215,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) menu_item.key = key_string_lookup_string(key); menu_item.command = args->argv[i++]; - menu_add_item(menu, &menu_item, item, c, fs); + menu_add_item(menu, &menu_item, item, tc, target); } free(title); if (menu == NULL) { @@ -104,75 +226,94 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) menu_free(menu); return (CMD_RETURN_NORMAL); } + cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4, + menu->count + 2); - xp = args_get(args, 'x'); - if (xp == NULL) - px = 0; - else if (strcmp(xp, "R") == 0) - px = c->tty.sx - 1; - else if (strcmp(xp, "P") == 0) { - tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); - if (wp->xoff >= ox) - px = wp->xoff - ox; - else - px = 0; - } else if (strcmp(xp, "M") == 0 && item->shared->mouse.valid) { - if (item->shared->mouse.x > (menu->width + 4) / 2) - px = item->shared->mouse.x - (menu->width + 4) / 2; - else - px = 0; + if (!event->m.valid) + flags |= MENU_NOMOUSE; + if (menu_display(menu, flags, item, px, py, tc, target, NULL, + NULL) != 0) + return (CMD_RETURN_NORMAL); + return (CMD_RETURN_WAIT); +} + +static enum cmd_retval +cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item); + struct tty *tty = &tc->tty; + const char *value, *cmd = NULL, **lines = NULL; + const char *shellcmd = NULL; + char *cwd, *cause; + int flags = 0; + u_int px, py, w, h, nlines = 0; + + if (args_has(args, 'C')) { + server_client_clear_overlay(tc); + return (CMD_RETURN_NORMAL); } - else if (strcmp(xp, "W") == 0) { - if (at == -1) - px = 0; - else { - TAILQ_FOREACH(sr, &c->status.entries[0].ranges, entry) { - if (sr->type != STYLE_RANGE_WINDOW) - continue; - if (sr->argument == (u_int)wl->idx) - break; - } - if (sr != NULL) - px = sr->start; - else - px = 0; + if (tc->overlay_draw != NULL) + return (CMD_RETURN_NORMAL); + + if (args->argc >= 1) + cmd = args->argv[0]; + if (args->argc >= 2) { + lines = (const char **)args->argv + 1; + nlines = args->argc - 1; + } + + if (nlines != 0) + h = popup_height(nlines, lines) + 2; + else + h = tty->sy / 2; + if (args_has(args, 'h')) { + h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause); + if (cause != NULL) { + cmdq_error(item, "height %s", cause); + free(cause); + return (CMD_RETURN_ERROR); } - } else - px = strtoul(xp, NULL, 10); - if (px + menu->width + 4 >= c->tty.sx) - px = c->tty.sx - menu->width - 4; + } - yp = args_get(args, 'y'); - if (yp == NULL) - py = 0; - else if (strcmp(yp, "P") == 0) { - tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); - if (wp->yoff + wp->sy >= oy) - py = wp->yoff + wp->sy - oy; - else - py = 0; - } else if (strcmp(yp, "M") == 0 && item->shared->mouse.valid) - py = item->shared->mouse.y + menu->count + 2; - else if (strcmp(yp, "S") == 0) { - if (at == -1) - py = c->tty.sy; - else if (at == 0) - py = status_line_size(c) + menu->count + 2; - else - py = at; - } else - py = strtoul(yp, NULL, 10); - if (py < menu->count + 2) - py = 0; + if (nlines != 0) + w = popup_width(item, nlines, lines, tc, target) + 2; else - py -= menu->count + 2; - if (py + menu->count + 2 >= c->tty.sy) - py = c->tty.sy - menu->count - 2; + w = tty->sx / 2; + if (args_has(args, 'w')) { + w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause); + if (cause != NULL) { + cmdq_error(item, "width %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + } - flags = 0; - if (!item->shared->mouse.valid) - flags |= MENU_NOMOUSE; - if (menu_display(menu, flags, item, px, py, c, fs, NULL, NULL) != 0) + if (w > tty->sx - 1) + w = tty->sx - 1; + if (h > tty->sy - 1) + h = tty->sy - 1; + cmd_display_menu_get_position(tc, item, args, &px, &py, w, h); + + value = args_get(args, 'd'); + if (value != NULL) + cwd = format_single_from_target(item, value); + else + cwd = xstrdup(server_client_get_cwd(tc, target->s)); + + value = args_get(args, 'R'); + if (value != NULL) + shellcmd = format_single_from_target(item, value); + + if (args_has(args, 'K')) + flags |= POPUP_WRITEKEYS; + if (args_has(args, 'E') > 1) + flags |= POPUP_CLOSEEXITZERO; + else if (args_has(args, 'E')) + flags |= POPUP_CLOSEEXIT; + if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, + cmd, cwd, tc, target) != 0) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } diff --git a/cmd-display-message.c b/cmd-display-message.c index 4d9bccb6..4e69f03a 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -45,7 +45,7 @@ const struct cmd_entry cmd_display_message_entry = { .target = { 't', CMD_FIND_PANE, 0 }, - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL, .exec = cmd_display_message_exec }; @@ -60,11 +60,12 @@ cmd_display_message_each(const char *key, const char *value, void *arg) static enum cmd_retval cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c, *target_c; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item), *c; + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; const char *template; char *msg, *cause; struct format_tree *ft; @@ -96,17 +97,16 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) * formats too, assuming it matches the session. If it doesn't, use the * best client for the session. */ - c = cmd_find_client(item, args_get(args, 'c'), 1); - if (c != NULL && c->session == s) - target_c = c; + if (tc != NULL && tc->session == s) + c = tc; else - target_c = cmd_find_best_client(s); - if (args_has(self->args, 'v')) + c = cmd_find_best_client(s); + if (args_has(args, 'v')) flags = FORMAT_VERBOSE; else flags = 0; - ft = format_create(item->client, item, FORMAT_NONE, flags); - format_defaults(ft, target_c, s, wl, wp); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, flags); + format_defaults(ft, c, s, wl, wp); if (args_has(args, 'a')) { format_each(ft, cmd_display_message_each, item); @@ -114,10 +114,10 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) } msg = format_expand_time(ft, template); - if (args_has(self->args, 'p')) + if (args_has(args, 'p')) cmdq_print(item, "%s", msg); - else if (c != NULL) - status_message_set(c, "%s", msg); + else if (tc != NULL) + status_message_set(tc, "%s", msg); free(msg); format_free(ft); diff --git a/cmd-display-panes.c b/cmd-display-panes.c index df97819c..0f12b14b 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_display_panes_entry = { .args = { "bd:t:", 0, 1 }, .usage = "[-b] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_display_panes_exec }; @@ -197,11 +197,10 @@ static int cmd_display_panes_key(struct client *c, struct key_event *event) { struct cmd_display_panes_data *cdata = c->overlay_data; - struct cmdq_item *new_item; - char *cmd, *expanded; + char *cmd, *expanded, *error; struct window *w = c->session->curw->window; struct window_pane *wp; - struct cmd_parse_result *pr; + enum cmd_parse_status status; if (event->key < '0' || event->key > '9') return (-1); @@ -214,21 +213,10 @@ cmd_display_panes_key(struct client *c, struct key_event *event) xasprintf(&expanded, "%%%u", wp->id); cmd = cmd_template_replace(cdata->command, expanded, 1); - pr = cmd_parse_from_string(cmd, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - new_item = NULL; - break; - case CMD_PARSE_ERROR: - new_item = cmdq_get_error(pr->error); - free(pr->error); - cmdq_append(c, new_item); - break; - case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); - cmd_list_free(pr->cmdlist); - cmdq_append(c, new_item); - break; + status = cmd_parse_and_append(cmd, NULL, c, NULL, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_append(c, cmdq_get_error(error)); + free(error); } free(cmd); @@ -239,18 +227,14 @@ cmd_display_panes_key(struct client *c, struct key_event *event) static enum cmd_retval cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c; - struct session *s; + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); + struct session *s = tc->session; u_int delay; char *cause; struct cmd_display_panes_data *cdata; - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - s = c->session; - - if (c->overlay_draw != NULL) + if (tc->overlay_draw != NULL) return (CMD_RETURN_NORMAL); if (args_has(args, 'd')) { @@ -273,7 +257,7 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) else cdata->item = item; - server_client_set_overlay(c, delay, cmd_display_panes_draw, + server_client_set_overlay(tc, delay, NULL, NULL, cmd_display_panes_draw, cmd_display_panes_key, cmd_display_panes_free, cdata); if (args_has(args, 'b')) diff --git a/cmd-find-window.c b/cmd-find-window.c index c29878b5..e1faeb2f 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -44,8 +44,9 @@ const struct cmd_entry cmd_find_window_entry = { static enum cmd_retval cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args, *new_args; - struct window_pane *wp = item->target.wp; + struct args *args = cmd_get_args(self), *new_args; + struct cmd_find_state *target = cmdq_get_target(item); + struct window_pane *wp = target->wp; const char *s = args->argv[0]; char *filter, *argv = { NULL }; int C, N, T; @@ -116,7 +117,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) args_set(new_args, 'Z', NULL); args_set(new_args, 'f', filter); - window_pane_set_mode(wp, &window_tree_mode, &item->target, new_args); + window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args); args_free(new_args); free(filter); @@ -960,10 +960,11 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { fs->current = &marked_pane; log_debug("%s: current is marked pane", __func__); - } else if (cmd_find_valid_state(&item->shared->current)) { - fs->current = &item->shared->current; + } else if (cmd_find_valid_state(cmdq_get_current(item))) { + fs->current = cmdq_get_current(item); log_debug("%s: current is from queue", __func__); - } else if (cmd_find_from_client(¤t, item->client, flags) == 0) { + } else if (cmd_find_from_client(¤t, cmdq_get_client(item), + flags) == 0) { fs->current = ¤t; log_debug("%s: current is from client", __func__); } else { @@ -980,7 +981,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, /* Mouse target is a plain = or {mouse}. */ if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { - m = &item->shared->mouse; + m = &cmdq_get_event(item)->m; switch (type) { case CMD_FIND_PANE: fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); @@ -1230,29 +1231,31 @@ no_pane: static struct client * cmd_find_current_client(struct cmdq_item *item, int quiet) { - struct client *c; + struct client *c = NULL, *found; struct session *s; struct window_pane *wp; struct cmd_find_state fs; - if (item->client != NULL && item->client->session != NULL) - return (item->client); + if (item != NULL) + c = cmdq_get_client(item); + if (c != NULL && c->session != NULL) + return (c); - c = NULL; - if ((wp = cmd_find_inside_pane(item->client)) != NULL) { + found = NULL; + if (c != NULL && (wp = cmd_find_inside_pane(c)) != NULL) { cmd_find_clear_state(&fs, CMD_FIND_QUIET); fs.w = wp->window; if (cmd_find_best_session_with_window(&fs) == 0) - c = cmd_find_best_client(fs.s); + found = cmd_find_best_client(fs.s); } else { s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET); if (s != NULL) - c = cmd_find_best_client(s); + found = cmd_find_best_client(s); } - if (c == NULL && !quiet) + if (found == NULL && item != NULL && !quiet) cmdq_error(item, "no current client"); - log_debug("%s: no target, return %p", __func__, c); - return (c); + log_debug("%s: no target, return %p", __func__, found); + return (found); } /* Find the target client or report an error and return NULL. */ diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 2befbc0c..d980472a 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -56,26 +56,23 @@ struct cmd_if_shell_data { struct client *client; struct cmdq_item *item; - struct mouse_event mouse; }; static enum cmd_retval cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct mouse_event *m = &item->shared->mouse; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct cmdq_state *state = cmdq_get_state(item); struct cmd_if_shell_data *cdata; - char *shellcmd, *cmd; - struct cmdq_item *new_item; - struct cmd_find_state *fs = &item->target; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = fs->s; - struct winlink *wl = fs->wl; - struct window_pane *wp = fs->wp; + char *shellcmd, *cmd, *error; + const char *file; + struct client *tc = cmdq_get_target_client(item); + struct session *s = target->s; struct cmd_parse_input pi; - struct cmd_parse_result *pr; + enum cmd_parse_status status; - shellcmd = format_single(item, args->argv[0], c, s, wl, wp); + shellcmd = format_single_from_target(item, args->argv[0]); if (args_has(args, 'F')) { if (*shellcmd != '0' && *shellcmd != '\0') cmd = args->argv[1]; @@ -88,26 +85,16 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); memset(&pi, 0, sizeof pi); - if (self->file != NULL) - pi.file = self->file; - pi.line = self->line; + cmd_get_source(self, &pi.file, &pi.line); pi.item = item; - pi.c = c; - cmd_find_copy_state(&pi.fs, fs); - - pr = cmd_parse_from_string(cmd, &pi); - switch (pr->status) { - case CMD_PARSE_EMPTY: - break; - case CMD_PARSE_ERROR: - cmdq_error(item, "%s", pr->error); - free(pr->error); + pi.c = tc; + cmd_find_copy_state(&pi.fs, target); + + status = cmd_parse_and_insert(cmd, &pi, item, state, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_error(item, "%s", error); + free(error); return (CMD_RETURN_ERROR); - case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); - cmdq_insert_after(item, new_item); - cmd_list_free(pr->cmdlist); - break; } return (CMD_RETURN_NORMAL); } @@ -119,12 +106,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->cmd_else = xstrdup(args->argv[2]); else cdata->cmd_else = NULL; - memcpy(&cdata->mouse, m, sizeof cdata->mouse); if (!args_has(args, 'b')) - cdata->client = item->client; + cdata->client = cmdq_get_client(item); else - cdata->client = c; + cdata->client = tc; if (cdata->client != NULL) cdata->client->references++; @@ -134,17 +120,18 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->item = NULL; memset(&cdata->input, 0, sizeof cdata->input); - if (self->file != NULL) - cdata->input.file = xstrdup(self->file); - cdata->input.line = self->line; - cdata->input.item = cdata->item; - cdata->input.c = c; + cmd_get_source(self, &file, &cdata->input.line); + if (file != NULL) + cdata->input.file = xstrdup(file); + cdata->input.c = tc; if (cdata->input.c != NULL) cdata->input.c->references++; - cmd_find_copy_state(&cdata->input.fs, fs); + cmd_find_copy_state(&cdata->input.fs, target); - if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, - cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) { + if (job_run(shellcmd, s, + server_client_get_cwd(cmdq_get_client(item), s), NULL, + cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1, + -1) == NULL) { cmdq_error(item, "failed to run command: %s", shellcmd); free(shellcmd); free(cdata); @@ -162,8 +149,8 @@ cmd_if_shell_callback(struct job *job) { struct cmd_if_shell_data *cdata = job_get_data(job); struct client *c = cdata->client; - struct mouse_event *m = &cdata->mouse; struct cmdq_item *new_item = NULL; + struct cmdq_state *new_state = NULL; char *cmd; int status; struct cmd_parse_result *pr; @@ -186,7 +173,13 @@ cmd_if_shell_callback(struct job *job) free(pr->error); break; case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0); + if (cdata->item == NULL) + new_state = cmdq_new_state(NULL, NULL, 0); + else + new_state = cmdq_get_state(cdata->item); + new_item = cmdq_get_command(pr->cmdlist, new_state); + if (cdata->item == NULL) + cmdq_free_state(new_state); cmd_list_free(pr->cmdlist); break; } diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 4b767e3e..2e4bec50 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -49,8 +49,8 @@ const struct cmd_entry cmd_move_pane_entry = { .name = "move-pane", .alias = "movep", - .args = { "bdhvp:l:s:t:", 0, 0 }, - .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, + .args = { "bdfhvp:l:s:t:", 0, 0 }, + .usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .target = { 't', CMD_FIND_PANE, 0 }, @@ -62,42 +62,33 @@ const struct cmd_entry cmd_move_pane_entry = { static enum cmd_retval cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct cmd_find_state *current = &item->shared->current; + struct args *args = cmd_get_args(self); + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct cmd_find_state *source = cmdq_get_source(item); struct session *dst_s; struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *src_wp, *dst_wp; - char *cause, *copy; - const char *errstr, *p; - size_t plen; - int size, percentage, dst_idx, not_same_window; + char *cause = NULL; + int size, percentage, dst_idx; int flags; enum layout_type type; struct layout_cell *lc; - if (self->entry == &cmd_join_pane_entry) - not_same_window = 1; - else - not_same_window = 0; - - dst_s = item->target.s; - dst_wl = item->target.wl; - dst_wp = item->target.wp; + dst_s = target->s; + dst_wl = target->wl; + dst_wp = target->wp; dst_w = dst_wl->window; dst_idx = dst_wl->idx; server_unzoom_window(dst_w); - src_wl = item->source.wl; - src_wp = item->source.wp; + src_wl = source->wl; + src_wp = source->wp; src_w = src_wl->window; server_unzoom_window(src_w); - if (not_same_window && src_w == dst_w) { - cmdq_error(item, "can't join a pane to its own window"); - return (CMD_RETURN_ERROR); - } - if (!not_same_window && src_wp == dst_wp) { + if (src_wp == dst_wp) { cmdq_error(item, "source and target panes must be different"); return (CMD_RETURN_ERROR); } @@ -107,40 +98,27 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) type = LAYOUT_LEFTRIGHT; size = -1; - if ((p = args_get(args, 'l')) != NULL) { - plen = strlen(p); - if (p[plen - 1] == '%') { - copy = xstrdup(p); - copy[plen - 1] = '\0'; - percentage = strtonum(copy, 0, INT_MAX, &errstr); - free(copy); - if (errstr != NULL) { - cmdq_error(item, "percentage %s", errstr); - return (CMD_RETURN_ERROR); - } - if (type == LAYOUT_TOPBOTTOM) - size = (dst_wp->sy * percentage) / 100; - else - size = (dst_wp->sx * percentage) / 100; + if (args_has(args, 'l')) { + if (type == LAYOUT_TOPBOTTOM) { + size = args_percentage(args, 'l', 0, INT_MAX, + dst_wp->sy, &cause); } else { - size = args_strtonum(args, 'l', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(item, "size %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } + size = args_percentage(args, 'l', 0, INT_MAX, + dst_wp->sx, &cause); } } else if (args_has(args, 'p')) { percentage = args_strtonum(args, 'p', 0, 100, &cause); - if (cause != NULL) { - cmdq_error(item, "percentage %s", cause); - free(cause); - return (CMD_RETURN_ERROR); + if (cause == NULL) { + if (type == LAYOUT_TOPBOTTOM) + size = (dst_wp->sy * percentage) / 100; + else + size = (dst_wp->sx * percentage) / 100; } - if (type == LAYOUT_TOPBOTTOM) - size = (dst_wp->sy * percentage) / 100; - else - size = (dst_wp->sx * percentage) / 100; + } + if (cause != NULL) { + cmdq_error(item, "size %s", cause); + free(cause); + return (CMD_RETURN_ERROR); } flags = 0; diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index f0aacb2a..2302d7bb 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -44,10 +44,12 @@ const struct cmd_entry cmd_kill_pane_entry = { static enum cmd_retval cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct winlink *wl = item->target.wl; - struct window_pane *loopwp, *tmpwp, *wp = item->target.wp; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; + struct window_pane *loopwp, *tmpwp, *wp = target->wp; - if (args_has(self->args, 'a')) { + if (args_has(args, 'a')) { server_unzoom_window(wl->window); TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { if (loopwp == wp) diff --git a/cmd-kill-server.c b/cmd-kill-server.c index d7eba692..76bcf267 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -54,7 +54,7 @@ const struct cmd_entry cmd_start_server_entry = { static enum cmd_retval cmd_kill_server_exec(struct cmd *self, __unused struct cmdq_item *item) { - if (self->entry == &cmd_kill_server_entry) + if (cmd_get_entry(self) == &cmd_kill_server_entry) kill(getpid(), SIGTERM); return (CMD_RETURN_NORMAL); diff --git a/cmd-kill-session.c b/cmd-kill-session.c index dcef8097..c10efba6 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -45,11 +45,10 @@ const struct cmd_entry cmd_kill_session_entry = { static enum cmd_retval cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct session *s, *sloop, *stmp; - struct winlink *wl; - - s = item->target.s; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct session *s = target->s, *sloop, *stmp; + struct winlink *wl; if (args_has(args, 'C')) { RB_FOREACH(wl, winlinks, &s->windows) { diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 50df83ee..68139faa 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -55,13 +55,14 @@ const struct cmd_entry cmd_unlink_window_entry = { static enum cmd_retval cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct winlink *wl = item->target.wl, *wl2, *wl3; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl, *wl2, *wl3; struct window *w = wl->window; - struct session *s = item->target.s; + struct session *s = target->s; - if (self->entry == &cmd_unlink_window_entry) { - if (!args_has(self->args, 'k') && !session_is_linked(s, w)) { + if (cmd_get_entry(self) == &cmd_unlink_window_entry) { + if (!args_has(args, 'k') && !session_is_linked(s, w)) { cmdq_error(item, "window only linked to one session"); return (CMD_RETURN_ERROR); } diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 0457a62d..45d5a4ee 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_buffers_entry = { .name = "list-buffers", .alias = "lsb", - .args = { "F:", 0, 0 }, - .usage = "[-F format]", + .args = { "F:f:", 0, 0 }, + .usage = "[-F format] [-f filter]", .flags = CMD_AFTERHOOK, .exec = cmd_list_buffers_exec @@ -46,23 +46,33 @@ const struct cmd_entry cmd_list_buffers_entry = { static enum cmd_retval cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct paste_buffer *pb; struct format_tree *ft; - char *line; - const char *template; + const char *template, *filter; + char *line, *expanded; + int flag; if ((template = args_get(args, 'F')) == NULL) template = LIST_BUFFERS_TEMPLATE; + filter = args_get(args, 'f'); pb = NULL; while ((pb = paste_walk(pb)) != NULL) { - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_defaults_paste_buffer(ft, pb); - line = format_expand(ft, template); - cmdq_print(item, "%s", line); - free(line); + if (filter != NULL) { + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + } else + flag = 1; + if (flag) { + line = format_expand(ft, template); + cmdq_print(item, "%s", line); + free(line); + } format_free(ft); } diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 9fab8f84..75118c8e 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -51,7 +51,8 @@ const struct cmd_entry cmd_list_clients_entry = { static enum cmd_retval cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct client *c; struct session *s; struct format_tree *ft; @@ -60,7 +61,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) char *line; if (args_has(args, 't')) - s = item->target.s; + s = target->s; else s = NULL; @@ -72,7 +73,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) if (c->session == NULL || (s != NULL && s != c->session)) continue; - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_add(ft, "line", "%u", idx); format_defaults(ft, c, NULL, NULL, NULL); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 7e340516..1141bbb5 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -85,7 +85,7 @@ static int cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, const char *tablename, u_int keywidth, key_code only, const char *prefix) { - struct client *c = cmd_find_client(item, NULL, 1); + struct client *tc = cmdq_get_target_client(item); struct key_table *table; struct key_binding *bd; const char *key; @@ -111,8 +111,8 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, else note = xstrdup(bd->note); tmp = utf8_padcstr(key, keywidth + 1); - if (args_has(args, '1') && c != NULL) - status_message_set(c, "%s%s%s", prefix, tmp, note); + if (args_has(args, '1') && tc != NULL) + status_message_set(tc, "%s%s%s", prefix, tmp, note); else cmdq_print(item, "%s%s%s", prefix, tmp, note); free(tmp); @@ -144,7 +144,7 @@ cmd_list_keys_get_prefix(struct args *args, key_code *prefix) static enum cmd_retval cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct key_table *table; struct key_binding *bd; const char *tablename, *r; @@ -153,7 +153,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) int repeat, width, tablewidth, keywidth, found = 0; size_t tmpsize, tmpused, cplen; - if (self->entry == &cmd_list_commands_entry) + if (cmd_get_entry(self) == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, item)); if (args->argc != 0) { @@ -269,7 +269,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) tmpsize *= 2; tmp = xrealloc(tmp, tmpsize); } - tmpused = strlcat(tmp, cp, tmpsize); + strlcat(tmp, cp, tmpsize); tmpused = strlcat(tmp, " ", tmpsize); free(cp); @@ -279,7 +279,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) tmpsize *= 2; tmp = xrealloc(tmp, tmpsize); } - tmpused = strlcat(tmp, cp, tmpsize); + strlcat(tmp, cp, tmpsize); tmpused = strlcat(tmp, " ", tmpsize); free(cp); @@ -313,7 +313,7 @@ out: static enum cmd_retval cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); const struct cmd_entry **entryp; const struct cmd_entry *entry; struct format_tree *ft; @@ -329,7 +329,7 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) "#{command_list_usage}"; } - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_defaults(ft, NULL, NULL, NULL, NULL); for (entryp = cmd_table; *entryp != NULL; entryp++) { diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 7f6994bd..c6dcff23 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -38,8 +38,8 @@ const struct cmd_entry cmd_list_panes_entry = { .name = "list-panes", .alias = "lsp", - .args = { "asF:t:", 0, 0 }, - .usage = "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, + .args = { "asF:f:t:", 0, 0 }, + .usage = "[-as] [-F format] [-f filter] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -50,9 +50,10 @@ const struct cmd_entry cmd_list_panes_entry = { static enum cmd_retval cmd_list_panes_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct session *s = target->s; + struct winlink *wl = target->wl; if (args_has(args, 'a')) cmd_list_panes_server(self, item); @@ -87,12 +88,13 @@ static void cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, struct cmdq_item *item, int type) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct window_pane *wp; u_int n; struct format_tree *ft; - const char *template; - char *line; + const char *template, *filter; + char *line, *expanded; + int flag; template = args_get(args, 'F'); if (template == NULL) { @@ -120,16 +122,25 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, break; } } + filter = args_get(args, 'f'); n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); - line = format_expand(ft, template); - cmdq_print(item, "%s", line); - free(line); + if (filter != NULL) { + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + } else + flag = 1; + if (flag) { + line = format_expand(ft, template); + cmdq_print(item, "%s", line); + free(line); + } format_free(ft); n++; diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 72ff47e8..fbc3db1d 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -42,8 +42,8 @@ const struct cmd_entry cmd_list_sessions_entry = { .name = "list-sessions", .alias = "ls", - .args = { "F:", 0, 0 }, - .usage = "[-F format]", + .args = { "F:f:", 0, 0 }, + .usage = "[-F format] [-f filter]", .flags = CMD_AFTERHOOK, .exec = cmd_list_sessions_exec @@ -52,25 +52,35 @@ const struct cmd_entry cmd_list_sessions_entry = { static enum cmd_retval cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct session *s; u_int n; struct format_tree *ft; - const char *template; - char *line; + const char *template, *filter; + char *line, *expanded; + int flag; if ((template = args_get(args, 'F')) == NULL) template = LIST_SESSIONS_TEMPLATE; + filter = args_get(args, 'f'); n = 0; RB_FOREACH(s, sessions, &sessions) { - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, NULL, NULL); - line = format_expand(ft, template); - cmdq_print(item, "%s", line); - free(line); + if (filter != NULL) { + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + } else + flag = 1; + if (flag) { + line = format_expand(ft, template); + cmdq_print(item, "%s", line); + free(line); + } format_free(ft); n++; diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 46ee6f0c..9c33c2d0 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -49,8 +49,8 @@ const struct cmd_entry cmd_list_windows_entry = { .name = "list-windows", .alias = "lsw", - .args = { "F:at:", 0, 0 }, - .usage = "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, + .args = { "F:f:at:", 0, 0 }, + .usage = "[-a] [-F format] [-f filter] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, @@ -61,12 +61,13 @@ const struct cmd_entry cmd_list_windows_entry = { static enum cmd_retval cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); if (args_has(args, 'a')) cmd_list_windows_server(self, item); else - cmd_list_windows_session(self, item->target.s, item, 0); + cmd_list_windows_session(self, target->s, item, 0); return (CMD_RETURN_NORMAL); } @@ -84,12 +85,13 @@ static void cmd_list_windows_session(struct cmd *self, struct session *s, struct cmdq_item *item, int type) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct winlink *wl; u_int n; struct format_tree *ft; - const char *template; - char *line; + const char *template, *filter; + char *line, *expanded; + int flag; template = args_get(args, 'F'); if (template == NULL) { @@ -102,16 +104,25 @@ cmd_list_windows_session(struct cmd *self, struct session *s, break; } } + filter = args_get(args, 'f'); n = 0; RB_FOREACH(wl, winlinks, &s->windows) { - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, NULL); - line = format_expand(ft, template); - cmdq_print(item, "%s", line); - free(line); + if (filter != NULL) { + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + } else + flag = 1; + if (flag) { + line = format_expand(ft, template); + cmdq_print(item, "%s", line); + free(line); + } format_free(ft); n++; diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 5e930126..49e834d6 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -83,12 +83,8 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error, static enum cmd_retval cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_load_buffer_data *cdata; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; const char *bufname = args_get(args, 'b'); char *path; @@ -99,8 +95,8 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) else cdata->name = NULL; - path = format_single(item, args->argv[0], c, s, wl, wp); - file_read(item->client, path, cmd_load_buffer_done, cdata); + path = format_single_from_target(item, args->argv[0]); + file_read(cmdq_get_client(item), path, cmd_load_buffer_done, cdata); free(path); return (CMD_RETURN_WAIT); diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 524fa451..a0df95b0 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -57,25 +57,22 @@ const struct cmd_entry cmd_lock_client_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_lock_server_exec }; static enum cmd_retval cmd_lock_server_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c; + struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item); - if (self->entry == &cmd_lock_server_entry) + if (cmd_get_entry(self) == &cmd_lock_server_entry) server_lock(); - else if (self->entry == &cmd_lock_session_entry) - server_lock_session(item->target.s); - else { - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - server_lock_client(c); - } + else if (cmd_get_entry(self) == &cmd_lock_session_entry) + server_lock_session(target->s); + else + server_lock_client(tc); recalculate_sizes(); return (CMD_RETURN_NORMAL); diff --git a/cmd-move-window.c b/cmd-move-window.c index cb64d1e0..94b6c950 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -59,49 +59,52 @@ const struct cmd_entry cmd_link_window_entry = { static enum cmd_retval cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - const char *tflag = args_get(args, 't'); - struct session *src; - struct session *dst; - struct winlink *wl; - char *cause; - int idx, kflag, dflag, sflag; + struct args *args = cmd_get_args(self); + struct cmd_find_state *source = cmdq_get_source(item); + struct cmd_find_state target; + const char *tflag = args_get(args, 't'); + struct session *src = source->s; + struct session *dst; + struct winlink *wl = source->wl; + char *cause; + int idx, kflag, dflag, sflag; if (args_has(args, 'r')) { - if (cmd_find_target(&item->target, item, tflag, - CMD_FIND_SESSION, CMD_FIND_QUIET) != 0) + if (cmd_find_target(&target, item, tflag, CMD_FIND_SESSION, + CMD_FIND_QUIET) != 0) return (CMD_RETURN_ERROR); - session_renumber_windows(item->target.s); + session_renumber_windows(target.s); recalculate_sizes(); - server_status_session(item->target.s); + server_status_session(target.s); return (CMD_RETURN_NORMAL); } - if (cmd_find_target(&item->target, item, tflag, CMD_FIND_WINDOW, + if (cmd_find_target(&target, item, tflag, CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX) != 0) return (CMD_RETURN_ERROR); - src = item->source.s; - dst = item->target.s; - wl = item->source.wl; - idx = item->target.idx; - - kflag = args_has(self->args, 'k'); - dflag = args_has(self->args, 'd'); - sflag = args_has(self->args, 's'); - - if (args_has(self->args, 'a')) { - if ((idx = winlink_shuffle_up(dst, dst->curw)) == -1) + dst = target.s; + idx = target.idx; + + kflag = args_has(args, 'k'); + dflag = args_has(args, 'd'); + sflag = args_has(args, 's'); + + if (args_has(args, 'a')) { + if (target.wl != NULL) + idx = winlink_shuffle_up(dst, target.wl); + else + idx = winlink_shuffle_up(dst, dst->curw); + if (idx == -1) return (CMD_RETURN_ERROR); } - if (server_link_window(src, wl, dst, idx, kflag, !dflag, - &cause) != 0) { - cmdq_error(item, "can't link window: %s", cause); + if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { + cmdq_error(item, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } - if (self->entry == &cmd_move_window_entry) + if (cmd_get_entry(self) == &cmd_move_window_entry) server_unlink_window(src, wl); /* diff --git a/cmd-new-session.c b/cmd-new-session.c index a75fc972..f08155c0 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -66,22 +66,25 @@ const struct cmd_entry cmd_has_session_entry = { static enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c = item->client; - struct session *s, *as, *groupwith; + struct args *args = cmd_get_args(self); + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *c = cmdq_get_client(item); + struct session *s, *as, *groupwith = NULL; struct environ *env; struct options *oo; struct termios tio, *tiop; - struct session_group *sg; - const char *errstr, *template, *group, *prefix, *tmp; + struct session_group *sg = NULL; + const char *errstr, *template, *group, *tmp; char *cause, *cwd = NULL, *cp, *newname = NULL; + char *name, *prefix = NULL; int detached, already_attached, is_control = 0; u_int sx, sy, dsx, dsy; struct spawn_context sc; enum cmd_retval retval; struct cmd_find_state fs; - if (self->entry == &cmd_has_session_entry) { + if (cmd_get_entry(self) == &cmd_has_session_entry) { /* * cmd_find_target() will fail if the session cannot be found, * so always return success here. @@ -96,17 +99,15 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) tmp = args_get(args, 's'); if (tmp != NULL) { - newname = format_single(item, tmp, c, NULL, NULL, NULL); - if (!session_check_name(newname)) { - cmdq_error(item, "bad session name: %s", newname); - goto fail; - } + name = format_single(item, tmp, c, NULL, NULL, NULL); + newname = session_check_name(name); + free(name); } if (args_has(args, 'A')) { if (newname != NULL) as = session_find(newname); else - as = item->target.s; + as = target->s; if (as != NULL) { retval = cmd_attach_session(item, as->name, args_has(args, 'D'), args_has(args, 'X'), 0, NULL, @@ -123,25 +124,17 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) /* Is this going to be part of a session group? */ group = args_get(args, 't'); if (group != NULL) { - groupwith = item->target.s; - if (groupwith == NULL) { - if (!session_check_name(group)) { - cmdq_error(item, "bad group name: %s", group); - goto fail; - } + groupwith = target->s; + if (groupwith == NULL) sg = session_group_find(group); - } else + else sg = session_group_contains(groupwith); if (sg != NULL) - prefix = sg->name; + prefix = xstrdup(sg->name); else if (groupwith != NULL) - prefix = groupwith->name; + prefix = xstrdup(groupwith->name); else - prefix = group; - } else { - groupwith = NULL; - sg = NULL; - prefix = NULL; + prefix = session_check_name(group); } /* Set -d if no client. */ @@ -172,7 +165,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) * over. */ if (!detached && !already_attached && c->tty.fd != -1) { - if (server_client_check_nested(item->client)) { + if (server_client_check_nested(cmdq_get_client(item))) { cmdq_error(item, "sessions should be nested with care, " "unset $TMUX to force"); goto fail; @@ -207,7 +200,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) goto fail; } } - } + } else + dsx = 80; if (args_has(args, 'y')) { tmp = args_get(args, 'y'); if (strcmp(tmp, "-") == 0) { @@ -222,7 +216,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) goto fail; } } - } + } else + dsy = 24; /* Find new session size. */ if (!detached && !is_control) { @@ -233,13 +228,14 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) } else { tmp = options_get_string(global_s_options, "default-size"); if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) { - sx = 80; - sy = 24; - } - if (args_has(args, 'x')) sx = dsx; - if (args_has(args, 'y')) sy = dsy; + } else { + if (args_has(args, 'x')) + sx = dsx; + if (args_has(args, 'y')) + sy = dsy; + } } if (sx == 0) sx = 1; @@ -264,7 +260,6 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; - sc.c = c; sc.name = args_get(args, 'n'); sc.argc = args->argc; @@ -311,7 +306,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) } else if (c->session != NULL) c->last_session = c->session; c->session = s; - if (~item->shared->flags & CMDQ_SHARED_REPEAT) + if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); @@ -339,20 +334,22 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) free(cp); } - if (!detached) { + if (!detached) c->flags |= CLIENT_ATTACHED; - cmd_find_from_session(&item->shared->current, s, 0); - } + if (!args_has(args, 'd')) + cmd_find_from_session(current, s, 0); cmd_find_from_session(&fs, s, 0); cmdq_insert_hook(s, item, &fs, "after-new-session"); free(cwd); free(newname); + free(prefix); return (CMD_RETURN_NORMAL); fail: free(cwd); free(newname); + free(prefix); return (CMD_RETURN_ERROR); } diff --git a/cmd-new-window.c b/cmd-new-window.c index 2fb92830..722f89b9 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -51,13 +51,14 @@ const struct cmd_entry cmd_new_window_entry = { static enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct cmd_find_state *current = &item->shared->current; + struct args *args = cmd_get_args(self); + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - int idx = item->target.idx; + struct client *tc = cmdq_get_target_client(item); + struct session *s = target->s; + struct winlink *wl = target->wl; + int idx = target->idx; struct winlink *new_wl; char *cause = NULL, *cp; const char *template, *add; @@ -72,7 +73,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; - sc.c = c; + sc.tc = tc; sc.name = args_get(args, 'n'); sc.argc = args->argc; @@ -81,7 +82,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) add = args_first_value(args, 'e', &value); while (add != NULL) { - environ_put(sc.environ, add); + environ_put(sc.environ, add, 0); add = args_next_value(&value); } @@ -108,7 +109,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - cp = format_single(item, template, c, s, new_wl, + cp = format_single(item, template, tc, s, new_wl, new_wl->window->active); cmdq_print(item, "%s", cp); free(cp); diff --git a/cmd-parse.y b/cmd-parse.y index 2375370b..891f2289 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -99,6 +99,7 @@ static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, } %token ERROR +%token HIDDEN %token IF %token ELSE %token ELIF @@ -138,6 +139,11 @@ statement : /* empty */ $$ = xmalloc (sizeof *$$); TAILQ_INIT($$); } + | hidden_assignment + { + $$ = xmalloc (sizeof *$$); + TAILQ_INIT($$); + } | condition { struct cmd_parse_state *ps = &parse_state; @@ -204,10 +210,21 @@ assignment : EQUALS if ((~flags & CMD_PARSE_PARSEONLY) && (ps->scope == NULL || ps->scope->flag)) - environ_put(global_environ, $1); + environ_put(global_environ, $1, 0); free($1); } +hidden_assignment : HIDDEN EQUALS + { + struct cmd_parse_state *ps = &parse_state; + int flags = ps->input->flags; + + if ((~flags & CMD_PARSE_PARSEONLY) && + (ps->scope == NULL || ps->scope->flag)) + environ_put(global_environ, $2, ENVIRON_HIDDEN); + free($2); + } + if_open : IF expanded { struct cmd_parse_state *ps = &parse_state; @@ -683,15 +700,17 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, /* * Parse each command into a command list. Create a new command list - * for each line so they get a new group (so the queue knows which ones - * to remove if a command fails when executed). + * for each line (unless the flag is set) so they get a new group (so + * the queue knows which ones to remove if a command fails when + * executed). */ result = cmd_list_new(); TAILQ_FOREACH(cmd, cmds, entry) { log_debug("%s: %u %s", __func__, cmd->line, cmd->name); cmd_log_argv(cmd->argc, cmd->argv, __func__); - if (cmdlist == NULL || cmd->line != line) { + if (cmdlist == NULL || + ((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) { if (cmdlist != NULL) { cmd_parse_print_commands(pi, line, cmdlist); cmd_list_move(result, cmdlist); @@ -758,9 +777,74 @@ cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi) struct cmd_parse_result * cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) { + struct cmd_parse_input input; + + if (pi == NULL) { + memset(&input, 0, sizeof input); + pi = &input; + } + + /* + * When parsing a string, put commands in one group even if there are + * multiple lines. This means { a \n b } is identical to "a ; b" when + * given as an argument to another command. + */ + pi->flags |= CMD_PARSE_ONEGROUP; return (cmd_parse_from_buffer(s, strlen(s), pi)); } +enum cmd_parse_status +cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi, + struct cmdq_item *after, struct cmdq_state *state, char **error) +{ + struct cmd_parse_result *pr; + struct cmdq_item *item; + + pr = cmd_parse_from_string(s, pi); + switch (pr->status) { + case CMD_PARSE_EMPTY: + break; + case CMD_PARSE_ERROR: + if (error != NULL) + *error = pr->error; + else + free(pr->error); + break; + case CMD_PARSE_SUCCESS: + item = cmdq_get_command(pr->cmdlist, state); + cmdq_insert_after(after, item); + cmd_list_free(pr->cmdlist); + break; + } + return (pr->status); +} + +enum cmd_parse_status +cmd_parse_and_append(const char *s, struct cmd_parse_input *pi, + struct client *c, struct cmdq_state *state, char **error) +{ + struct cmd_parse_result *pr; + struct cmdq_item *item; + + pr = cmd_parse_from_string(s, pi); + switch (pr->status) { + case CMD_PARSE_EMPTY: + break; + case CMD_PARSE_ERROR: + if (error != NULL) + *error = pr->error; + else + free(pr->error); + break; + case CMD_PARSE_SUCCESS: + item = cmdq_get_command(pr->cmdlist, state); + cmdq_append(c, item); + cmd_list_free(pr->cmdlist); + break; + } + return (pr->status); +} + struct cmd_parse_result * cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi) { @@ -1079,6 +1163,10 @@ yylex(void) if (*cp == '\0') return (TOKEN); ps->condition = 1; + if (strcmp(yylval.token, "%hidden") == 0) { + free(yylval.token); + return (HIDDEN); + } if (strcmp(yylval.token, "%if") == 0) { free(yylval.token); return (IF); diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 2b5db825..c8447426 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -46,8 +46,9 @@ const struct cmd_entry cmd_paste_buffer_entry = { static enum cmd_retval cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct window_pane *wp = item->target.wp; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct window_pane *wp = target->wp; struct paste_buffer *pb; const char *sepstr, *bufname, *bufdata, *bufend, *line; size_t seplen, bufsize; diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index ce1b3d37..b64229d8 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -55,11 +55,12 @@ const struct cmd_entry cmd_pipe_pane_entry = { static enum cmd_retval cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c = cmd_find_client(item, NULL, 1); - struct window_pane *wp = item->target.wp; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item); + struct window_pane *wp = target->wp; + struct session *s = target->s; + struct winlink *wl = target->wl; char *cmd; int old_fd, pipe_fd[2], null_fd, in, out; struct format_tree *ft; @@ -88,13 +89,13 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) * * bind ^p pipep -o 'cat >>~/output' */ - if (args_has(self->args, 'o') && old_fd != -1) + if (args_has(args, 'o') && old_fd != -1) return (CMD_RETURN_NORMAL); /* What do we want to do? Neither -I or -O is -O. */ - if (args_has(self->args, 'I')) { + if (args_has(args, 'I')) { in = 1; - out = args_has(self->args, 'O'); + out = args_has(args, 'O'); } else { in = 0; out = 1; @@ -107,8 +108,8 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) } /* Expand the command. */ - ft = format_create(item->client, item, FORMAT_NONE, 0); - format_defaults(ft, c, s, wl, wp); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); + format_defaults(ft, tc, s, wl, wp); cmd = format_expand_time(ft, args->argv[0]); format_free(ft); diff --git a/cmd-queue.c b/cmd-queue.c index a9e1dd3a..59f86c64 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -25,8 +25,69 @@ #include "tmux.h" -/* Global command queue. */ -static struct cmdq_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue); +/* Command queue flags. */ +#define CMDQ_FIRED 0x1 +#define CMDQ_WAITING 0x2 + +/* Command queue item type. */ +enum cmdq_type { + CMDQ_COMMAND, + CMDQ_CALLBACK, +}; + +/* Command queue item. */ +struct cmdq_item { + char *name; + struct cmdq_list *queue; + struct cmdq_item *next; + + struct client *client; + struct client *target_client; + + enum cmdq_type type; + u_int group; + + u_int number; + time_t time; + + int flags; + + struct cmdq_state *state; + struct cmd_find_state source; + struct cmd_find_state target; + + struct cmd_list *cmdlist; + struct cmd *cmd; + + cmdq_cb cb; + void *data; + + TAILQ_ENTRY(cmdq_item) entry; +}; +TAILQ_HEAD(cmdq_item_list, cmdq_item); + +/* + * Command queue state. This is the context for commands on the command queue. + * It holds information about how the commands were fired (the key and flags), + * any additional formats for the commands, and the current default target. + * Multiple commands can share the same state and a command may update the + * default target. + */ +struct cmdq_state { + int references; + int flags; + + struct format_tree *formats; + + struct key_event event; + struct cmd_find_state current; +}; + +/* Command queue. */ +struct cmdq_list { + struct cmdq_item *item; + struct cmdq_item_list list; +}; /* Get command queue name. */ static const char * @@ -47,9 +108,179 @@ cmdq_name(struct client *c) static struct cmdq_list * cmdq_get(struct client *c) { - if (c == NULL) - return (&global_queue); - return (&c->queue); + static struct cmdq_list *global_queue; + + if (c == NULL) { + if (global_queue == NULL) + global_queue = cmdq_new(); + return (global_queue); + } + return (c->queue); +} + +/* Create a queue. */ +struct cmdq_list * +cmdq_new(void) +{ + struct cmdq_list *queue; + + queue = xcalloc (1, sizeof *queue); + TAILQ_INIT (&queue->list); + return (queue); +} + +/* Free a queue. */ +void +cmdq_free(struct cmdq_list *queue) +{ + if (!TAILQ_EMPTY(&queue->list)) + fatalx("queue not empty"); + free(queue); +} + +/* Get item name. */ +const char * +cmdq_get_name(struct cmdq_item *item) +{ + return (item->name); +} + +/* Get item client. */ +struct client * +cmdq_get_client(struct cmdq_item *item) +{ + return (item->client); +} + +/* Get item target client. */ +struct client * +cmdq_get_target_client(struct cmdq_item *item) +{ + return (item->target_client); +} + +/* Get item state. */ +struct cmdq_state * +cmdq_get_state(struct cmdq_item *item) +{ + return (item->state); +} + +/* Get item target. */ +struct cmd_find_state * +cmdq_get_target(struct cmdq_item *item) +{ + return (&item->target); +} + +/* Get item source. */ +struct cmd_find_state * +cmdq_get_source(struct cmdq_item *item) +{ + return (&item->source); +} + +/* Get state event. */ +struct key_event * +cmdq_get_event(struct cmdq_item *item) +{ + return (&item->state->event); +} + +/* Get state current target. */ +struct cmd_find_state * +cmdq_get_current(struct cmdq_item *item) +{ + return (&item->state->current); +} + +/* Get state flags. */ +int +cmdq_get_flags(struct cmdq_item *item) +{ + return (item->state->flags); +} + +/* Create a new state. */ +struct cmdq_state * +cmdq_new_state(struct cmd_find_state *current, struct key_event *event, + int flags) +{ + struct cmdq_state *state; + + state = xcalloc(1, sizeof *state); + state->references = 1; + state->flags = flags; + + if (event != NULL) + memcpy(&state->event, event, sizeof state->event); + else + state->event.key = KEYC_NONE; + if (current != NULL && cmd_find_valid_state(current)) + cmd_find_copy_state(&state->current, current); + else + cmd_find_clear_state(&state->current, 0); + + return (state); +} + +/* Add a reference to a state. */ +struct cmdq_state * +cmdq_link_state(struct cmdq_state *state) +{ + state->references++; + return (state); +} + +/* Make a copy of a state. */ +struct cmdq_state * +cmdq_copy_state(struct cmdq_state *state) +{ + return (cmdq_new_state(&state->current, &state->event, state->flags)); +} + +/* Free a state. */ +void +cmdq_free_state(struct cmdq_state *state) +{ + if (--state->references != 0) + return; + + if (state->formats != NULL) + format_free(state->formats); + free(state); +} + +/* Add a format to command queue. */ +void +cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...) +{ + va_list ap; + char *value; + + va_start(ap, fmt); + xvasprintf(&value, fmt, ap); + va_end(ap); + + if (state->formats == NULL) + state->formats = format_create(NULL, NULL, FORMAT_NONE, 0); + format_add(state->formats, key, "%s", value); + + free(value); +} + +/* Merge formats from item. */ +void +cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft) +{ + const struct cmd_entry *entry; + + if (item->cmd != NULL) { + entry = cmd_get_entry (item->cmd); + format_add(ft, "command", "%s", entry->name); + } + if (item->state->formats != NULL) + format_merge(ft, item->state->formats); } /* Append an item. */ @@ -68,12 +299,12 @@ cmdq_append(struct client *c, struct cmdq_item *item) item->client = c; item->queue = queue; - TAILQ_INSERT_TAIL(queue, item, entry); + TAILQ_INSERT_TAIL(&queue->list, item, entry); log_debug("%s %s: %s", __func__, cmdq_name(c), item->name); item = next; } while (item != NULL); - return (TAILQ_LAST(queue, cmdq_list)); + return (TAILQ_LAST(&queue->list, cmdq_item_list)); } /* Insert an item. */ @@ -94,7 +325,7 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) item->client = c; item->queue = queue; - TAILQ_INSERT_AFTER(queue, after, item, entry); + TAILQ_INSERT_AFTER(&queue->list, after, item, entry); log_debug("%s %s: %s after %s", __func__, cmdq_name(c), item->name, after->name); @@ -107,17 +338,19 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) /* Insert a hook. */ void cmdq_insert_hook(struct session *s, struct cmdq_item *item, - struct cmd_find_state *fs, const char *fmt, ...) + struct cmd_find_state *current, const char *fmt, ...) { + struct cmdq_state *state = item->state; struct options *oo; va_list ap; char *name; struct cmdq_item *new_item; + struct cmdq_state *new_state; struct options_entry *o; struct options_array_item *a; struct cmd_list *cmdlist; - if (item->flags & CMDQ_NOHOOKS) + if (item->state->flags & CMDQ_STATE_NOHOOKS) return; if (s == NULL) oo = global_s_options; @@ -135,24 +368,27 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, } log_debug("running hook %s (parent %p)", name, item); + /* + * The hooks get a new state because they should not update the current + * target or formats for any subsequent commands. + */ + new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS); + cmdq_add_format(new_state, "hook", "%s", name); + a = options_array_first(o); while (a != NULL) { cmdlist = options_array_item_value(a)->cmdlist; - if (cmdlist == NULL) { - a = options_array_next(a); - continue; + if (cmdlist != NULL) { + new_item = cmdq_get_command(cmdlist, new_state); + if (item != NULL) + item = cmdq_insert_after(item, new_item); + else + item = cmdq_append(NULL, new_item); } - - new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS); - cmdq_format(new_item, "hook", "%s", name); - if (item != NULL) - item = cmdq_insert_after(item, new_item); - else - item = cmdq_append(NULL, new_item); - a = options_array_next(a); } + cmdq_free_state(new_state); free(name); } @@ -167,19 +403,13 @@ cmdq_continue(struct cmdq_item *item) static void cmdq_remove(struct cmdq_item *item) { - if (item->shared != NULL && --item->shared->references == 0) { - if (item->shared->formats != NULL) - format_free(item->shared->formats); - free(item->shared); - } - if (item->client != NULL) server_client_unref(item->client); - if (item->cmdlist != NULL) cmd_list_free(item->cmdlist); + cmdq_free_state(item->state); - TAILQ_REMOVE(item->queue, item, entry); + TAILQ_REMOVE(&item->queue->list, item, entry); free(item->name); free(item); @@ -204,48 +434,46 @@ cmdq_remove_group(struct cmdq_item *item) /* Get a command for the command queue. */ struct cmdq_item * -cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, - struct mouse_event *m, int flags) +cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state) { struct cmdq_item *item, *first = NULL, *last = NULL; struct cmd *cmd; - struct cmdq_shared *shared = NULL; - u_int group = 0; - - TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { - if (cmd->group != group) { - shared = xcalloc(1, sizeof *shared); - if (current != NULL) - cmd_find_copy_state(&shared->current, current); - else - cmd_find_clear_state(&shared->current, 0); - if (m != NULL) - memcpy(&shared->mouse, m, sizeof shared->mouse); - group = cmd->group; - } + const struct cmd_entry *entry; + int created = 0; + + if (state == NULL) { + state = cmdq_new_state(NULL, NULL, 0); + created = 1; + } + + cmd = cmd_list_first(cmdlist); + while (cmd != NULL) { + entry = cmd_get_entry(cmd); item = xcalloc(1, sizeof *item); - xasprintf(&item->name, "[%s/%p]", cmd->entry->name, item); + xasprintf(&item->name, "[%s/%p]", entry->name, item); item->type = CMDQ_COMMAND; - item->group = cmd->group; - item->flags = flags; + item->group = cmd_get_group(cmd); + item->state = cmdq_link_state(state); - item->shared = shared; item->cmdlist = cmdlist; item->cmd = cmd; - log_debug("%s: %s group %u", __func__, item->name, item->group); - - shared->references++; cmdlist->references++; + log_debug("%s: %s group %u", __func__, item->name, item->group); if (first == NULL) first = item; if (last != NULL) last->next = item; last = item; + + cmd = cmd_list_next(cmd); } + + if (created) + cmdq_free_state(state); return (first); } @@ -261,7 +489,7 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs, return (CMD_RETURN_NORMAL); } - value = args_get(item->cmd->args, flag->flag); + value = args_get(cmd_get_args(item->cmd), flag->flag); if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) { cmd_find_clear_state(fs, 0); return (CMD_RETURN_ERROR); @@ -273,14 +501,15 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs, static enum cmd_retval cmdq_fire_command(struct cmdq_item *item) { - struct client *c = item->client; - const char *name = cmdq_name(c); - struct cmdq_shared *shared = item->shared; + const char *name = cmdq_name(item->client); + struct cmdq_state *state = item->state; struct cmd *cmd = item->cmd; - const struct cmd_entry *entry = cmd->entry; + struct args *args = cmd_get_args(cmd); + const struct cmd_entry *entry = cmd_get_entry(cmd); + struct client *tc, *saved = item->client; enum cmd_retval retval; struct cmd_find_state *fsp, fs; - int flags; + int flags, quiet = 0; char *tmp; if (log_get_level() > 1) { @@ -289,11 +518,30 @@ cmdq_fire_command(struct cmdq_item *item) free(tmp); } - flags = !!(shared->flags & CMDQ_SHARED_CONTROL); + flags = !!(state->flags & CMDQ_STATE_CONTROL); cmdq_guard(item, "begin", flags); if (item->client == NULL) item->client = cmd_find_client(item, NULL, 1); + + if (entry->flags & CMD_CLIENT_CANFAIL) + quiet = 1; + if (entry->flags & CMD_CLIENT_CFLAG) { + tc = cmd_find_client(item, args_get(args, 'c'), quiet); + if (tc == NULL && !quiet) { + retval = CMD_RETURN_ERROR; + goto out; + } + } else if (entry->flags & CMD_CLIENT_TFLAG) { + tc = cmd_find_client(item, args_get(args, 't'), quiet); + if (tc == NULL && !quiet) { + retval = CMD_RETURN_ERROR; + goto out; + } + } else + tc = cmd_find_client(item, NULL, 1); + item->target_client = tc; + retval = cmdq_find_flag(item, &item->source, &entry->source); if (retval == CMD_RETURN_ERROR) goto out; @@ -301,6 +549,7 @@ cmdq_fire_command(struct cmdq_item *item) if (retval == CMD_RETURN_ERROR) goto out; + retval = entry->exec(cmd, item); if (retval == CMD_RETURN_ERROR) goto out; @@ -308,8 +557,8 @@ cmdq_fire_command(struct cmdq_item *item) if (entry->flags & CMD_AFTERHOOK) { if (cmd_find_valid_state(&item->target)) fsp = &item->target; - else if (cmd_find_valid_state(&item->shared->current)) - fsp = &item->shared->current; + else if (cmd_find_valid_state(&item->state->current)) + fsp = &item->state->current; else if (cmd_find_from_client(&fs, item->client, 0) == 0) fsp = &fs; else @@ -318,7 +567,7 @@ cmdq_fire_command(struct cmdq_item *item) } out: - item->client = c; + item->client = saved; if (retval == CMD_RETURN_ERROR) cmdq_guard(item, "error", flags); else @@ -337,7 +586,7 @@ cmdq_get_callback1(const char *name, cmdq_cb cb, void *data) item->type = CMDQ_CALLBACK; item->group = 0; - item->flags = 0; + item->state = cmdq_new_state(NULL, NULL, 0); item->cb = cb; item->data = data; @@ -371,25 +620,6 @@ cmdq_fire_callback(struct cmdq_item *item) return (item->cb(item, item->data)); } -/* Add a format to command queue. */ -void -cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...) -{ - struct cmdq_shared *shared = item->shared; - va_list ap; - char *value; - - va_start(ap, fmt); - xvasprintf(&value, fmt, ap); - va_end(ap); - - if (shared->formats == NULL) - shared->formats = format_create(NULL, NULL, FORMAT_NONE, 0); - format_add(shared->formats, key, "%s", value); - - free(value); -} - /* Process next item on command queue. */ u_int cmdq_next(struct client *c) @@ -401,18 +631,18 @@ cmdq_next(struct client *c) u_int items = 0; static u_int number; - if (TAILQ_EMPTY(queue)) { + if (TAILQ_EMPTY(&queue->list)) { log_debug("%s %s: empty", __func__, name); return (0); } - if (TAILQ_FIRST(queue)->flags & CMDQ_WAITING) { + if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) { log_debug("%s %s: waiting", __func__, name); return (0); } log_debug("%s %s: enter", __func__, name); for (;;) { - item = TAILQ_FIRST(queue); + item = queue->item = TAILQ_FIRST(&queue->list); if (item == NULL) break; log_debug("%s %s: %s (%d), flags %x", __func__, name, @@ -462,6 +692,7 @@ cmdq_next(struct client *c) } cmdq_remove(item); } + queue->item = NULL; log_debug("%s %s: exit (empty)", __func__, name); return (items); @@ -471,6 +702,15 @@ waiting: return (items); } +/* Get running item if any. */ +struct cmdq_item * +cmdq_running(struct client *c) +{ + struct cmdq_list *queue = cmdq_get(c); + + return (queue->item); +} + /* Print a guard line. */ void cmdq_guard(struct cmdq_item *item, const char *guard, int flags) @@ -511,8 +751,10 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) } else { wp = c->session->curw->window->active; wme = TAILQ_FIRST(&wp->modes); - if (wme == NULL || wme->mode != &window_view_mode) - window_pane_set_mode(wp, &window_view_mode, NULL, NULL); + if (wme == NULL || wme->mode != &window_view_mode) { + window_pane_set_mode(wp, NULL, &window_view_mode, NULL, + NULL); + } window_copy_add(wp, "%s", msg); } @@ -526,8 +768,9 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) struct client *c = item->client; struct cmd *cmd = item->cmd; va_list ap; - char *msg; - char *tmp; + char *msg, *tmp; + const char *file; + u_int line; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -535,9 +778,10 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) log_debug("%s: %s", __func__, msg); - if (c == NULL) - cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); - else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + if (c == NULL) { + cmd_get_source(cmd, &file, &line); + cfg_add_cause("%s:%u: %s", file, line, msg); + } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { if (~c->flags & CLIENT_UTF8) { tmp = msg; msg = utf8_sanitize(tmp); diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index b4c5e844..5514ff73 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -38,25 +38,21 @@ const struct cmd_entry cmd_refresh_client_entry = { .usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_refresh_client_exec }; static enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c; - struct tty *tty; + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); + struct tty *tty = &tc->tty; struct window *w; const char *size, *errstr; char *copy, *next, *s; u_int x, y, adjust; - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - tty = &c->tty; - if (args_has(args, 'c') || args_has(args, 'L') || args_has(args, 'R') || @@ -74,48 +70,47 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'c')) - c->pan_window = NULL; + tc->pan_window = NULL; else { - w = c->session->curw->window; - if (c->pan_window != w) { - c->pan_window = w; - c->pan_ox = tty->oox; - c->pan_oy = tty->ooy; + w = tc->session->curw->window; + if (tc->pan_window != w) { + tc->pan_window = w; + tc->pan_ox = tty->oox; + tc->pan_oy = tty->ooy; } if (args_has(args, 'L')) { - if (c->pan_ox > adjust) - c->pan_ox -= adjust; + if (tc->pan_ox > adjust) + tc->pan_ox -= adjust; else - c->pan_ox = 0; + tc->pan_ox = 0; } else if (args_has(args, 'R')) { - c->pan_ox += adjust; - if (c->pan_ox > w->sx - tty->osx) - c->pan_ox = w->sx - tty->osx; + tc->pan_ox += adjust; + if (tc->pan_ox > w->sx - tty->osx) + tc->pan_ox = w->sx - tty->osx; } else if (args_has(args, 'U')) { - if (c->pan_oy > adjust) - c->pan_oy -= adjust; + if (tc->pan_oy > adjust) + tc->pan_oy -= adjust; else - c->pan_oy = 0; + tc->pan_oy = 0; } else if (args_has(args, 'D')) { - c->pan_oy += adjust; - if (c->pan_oy > w->sy - tty->osy) - c->pan_oy = w->sy - tty->osy; + tc->pan_oy += adjust; + if (tc->pan_oy > w->sy - tty->osy) + tc->pan_oy = w->sy - tty->osy; } } - tty_update_client_offset(c); - server_redraw_client(c); + tty_update_client_offset(tc); + server_redraw_client(tc); return (CMD_RETURN_NORMAL); } if (args_has(args, 'l')) { - if (c->session != NULL) - tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?"); + tty_putcode_ptr2(&tc->tty, TTYC_MS, "", "?"); return (CMD_RETURN_NORMAL); } if (args_has(args, 'C') || args_has(args, 'F')) { if (args_has(args, 'C')) { - if (!(c->flags & CLIENT_CONTROL)) { + if (!(tc->flags & CLIENT_CONTROL)) { cmdq_error(item, "not a control client"); return (CMD_RETURN_ERROR); } @@ -130,12 +125,12 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "size too small or too big"); return (CMD_RETURN_ERROR); } - tty_set_size(&c->tty, x, y, 0, 0); - c->flags |= CLIENT_SIZECHANGED; + tty_set_size(&tc->tty, x, y, 0, 0); + tc->flags |= CLIENT_SIZECHANGED; recalculate_sizes(); } if (args_has(args, 'F')) { - if (!(c->flags & CLIENT_CONTROL)) { + if (!(tc->flags & CLIENT_CONTROL)) { cmdq_error(item, "not a control client"); return (CMD_RETURN_ERROR); } @@ -143,7 +138,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) while ((next = strsep(&s, ",")) != NULL) { /* Unknown flags are ignored. */ if (strcmp(next, "no-output") == 0) - c->flags |= CLIENT_CONTROL_NOOUTPUT; + tc->flags |= CLIENT_CONTROL_NOOUTPUT; } free(copy); } @@ -151,11 +146,11 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(args, 'S')) { - c->flags |= CLIENT_STATUSFORCE; - server_status_client(c); + tc->flags |= CLIENT_STATUSFORCE; + server_status_client(tc); } else { - c->flags |= CLIENT_STATUSFORCE; - server_redraw_client(c); + tc->flags |= CLIENT_STATUSFORCE; + server_redraw_client(tc); } return (CMD_RETURN_NORMAL); } diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 8385434a..51b8ffc8 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -46,22 +46,18 @@ const struct cmd_entry cmd_rename_session_entry = { static enum cmd_retval cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - char *newname; - - newname = format_single(item, args->argv[0], c, s, NULL, NULL); + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct session *s = target->s; + char *newname, *tmp; + + tmp = format_single_from_target(item, args->argv[0]); + newname = session_check_name(tmp); + free(tmp); if (strcmp(newname, s->name) == 0) { free(newname); return (CMD_RETURN_NORMAL); } - - if (!session_check_name(newname)) { - cmdq_error(item, "bad session name: %s", newname); - free(newname); - return (CMD_RETURN_ERROR); - } if (session_find(newname) != NULL) { cmdq_error(item, "duplicate session: %s", newname); free(newname); diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 4d2ebb75..1fb17ad9 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -45,13 +45,12 @@ const struct cmd_entry cmd_rename_window_entry = { static enum cmd_retval cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - char *newname; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; + char *newname; - newname = format_single(item, args->argv[0], c, s, wl, NULL); + newname = format_single_from_target(item, args->argv[0]); window_set_name(wl->window, newname); options_set_number(wl->window->options, "automatic-rename", 0); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 3962546d..105f48e0 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -36,8 +36,8 @@ const struct cmd_entry cmd_resize_pane_entry = { .name = "resize-pane", .alias = "resizep", - .args = { "DLMRt:Ux:y:Z", 0, 1 }, - .usage = "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " + .args = { "DLMRTt:Ux:y:Z", 0, 1 }, + .usage = "[-DLMRTUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " "[adjustment]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -49,26 +49,39 @@ const struct cmd_entry cmd_resize_pane_entry = { static enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct cmdq_shared *shared = item->shared; - struct window_pane *wp = item->target.wp; - struct winlink *wl = item->target.wl; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct key_event *event = cmdq_get_event(item); + struct window_pane *wp = target->wp; + struct winlink *wl = target->wl; struct window *w = wl->window; - struct client *c = item->client; - struct session *s = item->target.s; - const char *errstr, *p; - char *cause, *copy; + struct client *c = cmdq_get_client(item); + struct session *s = target->s; + const char *errstr; + char *cause; u_int adjust; - int x, y, percentage; - size_t plen; + int x, y; + struct grid *gd = wp->base.grid; + + if (args_has(args, 'T')) { + if (!TAILQ_EMPTY(&wp->modes)) + return (CMD_RETURN_NORMAL); + adjust = screen_size_y(&wp->base) - 1 - wp->base.cy; + if (adjust > gd->hsize) + adjust = gd->hsize; + grid_remove_history(gd, adjust); + wp->base.cy += adjust; + wp->flags |= PANE_REDRAW; + return (CMD_RETURN_NORMAL); + } if (args_has(args, 'M')) { - if (cmd_mouse_window(&shared->mouse, &s) == NULL) + if (!event->m.valid || cmd_mouse_window(&event->m, &s) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); c->tty.mouse_drag_update = cmd_resize_pane_mouse_update; - cmd_resize_pane_mouse_update(c, &shared->mouse); + cmd_resize_pane_mouse_update(c, &event->m); return (CMD_RETURN_NORMAL); } @@ -93,58 +106,21 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) } } - if ((p = args_get(args, 'x')) != NULL) { - plen = strlen(p); - if (p[plen - 1] == '%') { - copy = xstrdup(p); - copy[plen - 1] = '\0'; - percentage = strtonum(copy, 0, INT_MAX, &errstr); - free(copy); - if (errstr != NULL) { - cmdq_error(item, "width %s", errstr); - return (CMD_RETURN_ERROR); - } - x = (w->sx * percentage) / 100; - if (x < PANE_MINIMUM) - x = PANE_MINIMUM; - if (x > INT_MAX) - x = INT_MAX; - } else { - x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX, - &cause); - if (cause != NULL) { - cmdq_error(item, "width %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } + if (args_has(args, 'x')) { + x = args_percentage(args, 'x', 0, INT_MAX, w->sx, &cause); + if (cause != NULL) { + cmdq_error(item, "width %s", cause); + free(cause); + return (CMD_RETURN_ERROR); } layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x); } - if ((p = args_get(args, 'y')) != NULL) { - plen = strlen(p); - if (p[plen - 1] == '%') { - copy = xstrdup(p); - copy[plen - 1] = '\0'; - percentage = strtonum(copy, 0, INT_MAX, &errstr); - free(copy); - if (errstr != NULL) { - cmdq_error(item, "height %s", errstr); - return (CMD_RETURN_ERROR); - } - y = (w->sy * percentage) / 100; - if (y < PANE_MINIMUM) - y = PANE_MINIMUM; - if (y > INT_MAX) - y = INT_MAX; - } - else { - y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX, - &cause); - if (cause != NULL) { - cmdq_error(item, "height %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } + if (args_has(args, 'y')) { + y = args_percentage(args, 'y', 0, INT_MAX, w->sy, &cause); + if (cause != NULL) { + cmdq_error(item, "width %s", cause); + free(cause); + return (CMD_RETURN_ERROR); } layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y); } diff --git a/cmd-resize-window.c b/cmd-resize-window.c index 9cc74e82..1ebb7aca 100644 --- a/cmd-resize-window.c +++ b/cmd-resize-window.c @@ -46,10 +46,11 @@ const struct cmd_entry cmd_resize_window_entry = { static enum cmd_retval cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct winlink *wl = item->target.wl; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; struct window *w = wl->window; - struct session *s = item->target.s; + struct session *s = target->s; const char *errstr; char *cause; u_int adjust, sx, sy; diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 5e23fa15..498a89a7 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -47,11 +47,12 @@ const struct cmd_entry cmd_respawn_pane_entry = { static enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; char *cause = NULL; const char *add; struct args_value *value; @@ -71,7 +72,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) add = args_first_value(args, 'e', &value); while (add != NULL) { - environ_put(sc.environ, add); + environ_put(sc.environ, add, 0); add = args_next_value(&value); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 497e401e..39d19ddb 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -47,10 +47,12 @@ const struct cmd_entry cmd_respawn_window_entry = { static enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; + struct client *tc = cmdq_get_target_client(item); + struct session *s = target->s; + struct winlink *wl = target->wl; char *cause = NULL; const char *add; struct args_value *value; @@ -59,7 +61,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) sc.item = item; sc.s = s; sc.wl = wl; - sc.c = cmd_find_client(item, NULL, 1); + sc.tc = tc; sc.name = NULL; sc.argc = args->argc; @@ -68,7 +70,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) add = args_first_value(args, 'e', &value); while (add != NULL) { - environ_put(sc.environ, add); + environ_put(sc.environ, add, 0); add = args_next_value(&value); } diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index cc661163..55c1dde2 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -43,16 +43,18 @@ const struct cmd_entry cmd_rotate_window_entry = { static enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) { - struct cmd_find_state *current = &item->shared->current; - struct winlink *wl = item->target.wl; + struct args *args = cmd_get_args(self); + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; struct window *w = wl->window; struct window_pane *wp, *wp2; struct layout_cell *lc; u_int sx, sy, xoff, yoff; - window_push_zoom(w, args_has(self->args, 'Z')); + window_push_zoom(w, args_has(args, 'Z')); - if (args_has(self->args, 'D')) { + if (args_has(args, 'D')) { wp = TAILQ_LAST(&w->panes, window_panes); TAILQ_REMOVE(&w->panes, wp, entry); TAILQ_INSERT_HEAD(&w->panes, wp, entry); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 2f45f492..82d4a1a2 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -31,6 +31,7 @@ static enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmdq_item *); +static void cmd_run_shell_timer(int, short, void *); static void cmd_run_shell_callback(struct job *); static void cmd_run_shell_free(void *); static void cmd_run_shell_print(struct job *, const char *); @@ -39,8 +40,8 @@ const struct cmd_entry cmd_run_shell_entry = { .name = "run-shell", .alias = "run", - .args = { "bt:", 1, 1 }, - .usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command", + .args = { "bd:t:", 0, 1 }, + .usage = "[-b] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -50,8 +51,11 @@ const struct cmd_entry cmd_run_shell_entry = { struct cmd_run_shell_data { char *cmd; + char *cwd; struct cmdq_item *item; + struct session *s; int wp_id; + struct event timer; }; static void @@ -78,22 +82,26 @@ cmd_run_shell_print(struct job *job, const char *msg) wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) - window_pane_set_mode(wp, &window_view_mode, NULL, NULL); + window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); window_copy_add(wp, "%s", msg); } static enum cmd_retval cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct cmd_run_shell_data *cdata; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; + struct session *s = target->s; + struct window_pane *wp = target->wp; + const char *delay; + double d; + struct timeval tv; + char *end; cdata = xcalloc(1, sizeof *cdata); - cdata->cmd = format_single(item, args->argv[0], c, s, wl, wp); + if (args->argc != 0) + cdata->cmd = format_single_from_target(item, args->argv[0]); if (args_has(args, 't') && wp != NULL) cdata->wp_id = wp->id; @@ -103,12 +111,26 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'b')) cdata->item = item; - if (job_run(cdata->cmd, s, server_client_get_cwd(item->client, s), NULL, - cmd_run_shell_callback, cmd_run_shell_free, cdata, 0) == NULL) { - cmdq_error(item, "failed to run command: %s", cdata->cmd); - free(cdata); - return (CMD_RETURN_ERROR); - } + cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s)); + cdata->s = s; + if (s != NULL) + session_add_ref(s, __func__); + + evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata); + + if ((delay = args_get(args, 'd')) != NULL) { + d = strtod(delay, &end); + if (*end != '\0') { + cmdq_error(item, "invalid delay time: %s", delay); + cmd_run_shell_free(cdata); + return (CMD_RETURN_ERROR); + } + timerclear(&tv); + tv.tv_sec = (time_t)d; + tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U; + evtimer_add(&cdata->timer, &tv); + } else + cmd_run_shell_timer(-1, 0, cdata); if (args_has(args, 'b')) return (CMD_RETURN_NORMAL); @@ -116,10 +138,28 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) } static void +cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) +{ + struct cmd_run_shell_data *cdata = arg; + + if (cdata->cmd != NULL) { + if (job_run(cdata->cmd, cdata->s, cdata->cwd, NULL, + cmd_run_shell_callback, cmd_run_shell_free, cdata, 0, -1, + -1) == NULL) + cmd_run_shell_free(cdata); + } else { + if (cdata->item != NULL) + cmdq_continue(cdata->item); + cmd_run_shell_free(cdata); + } +} + +static void cmd_run_shell_callback(struct job *job) { struct cmd_run_shell_data *cdata = job_get_data(job); struct bufferevent *event = job_get_event(job); + struct cmdq_item *item = cdata->item; char *cmd = cdata->cmd, *msg = NULL, *line; size_t size; int retcode, status; @@ -149,13 +189,18 @@ cmd_run_shell_callback(struct job *job) } else if (WIFSIGNALED(status)) { retcode = WTERMSIG(status); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); + retcode += 128; } if (msg != NULL) cmd_run_shell_print(job, msg); free(msg); - if (cdata->item != NULL) - cmdq_continue(cdata->item); + if (item != NULL) { + if (cmdq_get_client(item) != NULL && + cmdq_get_client(item)->session == NULL) + cmdq_get_client(item)->retval = retcode; + cmdq_continue(item); + } } static void @@ -163,6 +208,10 @@ cmd_run_shell_free(void *data) { struct cmd_run_shell_data *cdata = data; + evtimer_del(&cdata->timer); + if (cdata->s != NULL) + session_remove_ref(cdata->s, __func__); + free(cdata->cwd); free(cdata->cmd); free(cdata); } diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 84e50242..656a89e1 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -72,11 +72,7 @@ cmd_save_buffer_done(__unused struct client *c, const char *path, int error, static enum cmd_retval cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; + struct args *args = cmd_get_args(self); struct paste_buffer *pb; int flags; const char *bufname = args_get(args, 'b'), *bufdata; @@ -97,15 +93,15 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) } bufdata = paste_buffer_data(pb, &bufsize); - if (self->entry == &cmd_show_buffer_entry) + if (cmd_get_entry(self) == &cmd_show_buffer_entry) path = xstrdup("-"); else - path = format_single(item, args->argv[0], c, s, wl, wp); - if (args_has(self->args, 'a')) + path = format_single_from_target(item, args->argv[0]); + if (args_has(args, 'a')) flags = O_APPEND; else flags = 0; - file_write(item->client, path, flags, bufdata, bufsize, + file_write(cmdq_get_client(item), path, flags, bufdata, bufsize, cmd_save_buffer_done, item); free(path); diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 775d32c5..7069eccc 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -71,20 +71,21 @@ const struct cmd_entry cmd_previous_layout_entry = { static enum cmd_retval cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct winlink *wl = item->target.wl; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; struct window *w = wl->window; - struct window_pane *wp = item->target.wp; + struct window_pane *wp = target->wp; const char *layoutname; char *oldlayout; int next, previous, layout; server_unzoom_window(w); - next = self->entry == &cmd_next_layout_entry; + next = (cmd_get_entry(self) == &cmd_next_layout_entry); if (args_has(args, 'n')) next = 1; - previous = self->entry == &cmd_previous_layout_entry; + previous = (cmd_get_entry(self) == &cmd_previous_layout_entry); if (args_has(args, 'p')) previous = 1; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index c63c7e61..db110ff9 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -83,19 +83,20 @@ cmd_select_pane_redraw(struct window *w) static enum cmd_retval cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct cmd_find_state *current = &item->shared->current; - struct client *c = cmd_find_client(item, NULL, 1); - struct winlink *wl = item->target.wl; + struct args *args = cmd_get_args(self); + const struct cmd_entry *entry = cmd_get_entry(self); + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; struct window *w = wl->window; - struct session *s = item->target.s; - struct window_pane *wp = item->target.wp, *lastwp, *markedwp; - char *pane_title; + struct session *s = target->s; + struct window_pane *wp = target->wp, *lastwp, *markedwp; + char *title; const char *style; struct style *sy; struct options_entry *o; - if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { + if (entry == &cmd_last_pane_entry || args_has(args, 'l')) { lastwp = w->last; if (lastwp == NULL && window_count_panes(w) == 2) { lastwp = TAILQ_PREV(w->active, window_panes, entry); @@ -106,12 +107,12 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "no last pane"); return (CMD_RETURN_ERROR); } - if (args_has(self->args, 'e')) + if (args_has(args, 'e')) lastwp->flags &= ~PANE_INPUTOFF; - else if (args_has(self->args, 'd')) + else if (args_has(args, 'd')) lastwp->flags |= PANE_INPUTOFF; else { - if (window_push_zoom(w, args_has(self->args, 'Z'))) + if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, lastwp); if (window_set_active_pane(w, lastwp, 1)) { @@ -146,7 +147,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'P') || args_has(self->args, 'g')) { + if (args_has(args, 'P') || args_has(args, 'g')) { if ((style = args_get(args, 'P')) != NULL) { o = options_set_style(wp->options, "window-style", 0, style); @@ -158,26 +159,26 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) style); wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); } - if (args_has(self->args, 'g')) { + if (args_has(args, 'g')) { sy = options_get_style(wp->options, "window-style"); cmdq_print(item, "%s", style_tostring(sy)); } return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'L')) { + if (args_has(args, 'L')) { window_push_zoom(w, 1); wp = window_pane_find_left(wp); window_pop_zoom(w); - } else if (args_has(self->args, 'R')) { + } else if (args_has(args, 'R')) { window_push_zoom(w, 1); wp = window_pane_find_right(wp); window_pop_zoom(w); - } else if (args_has(self->args, 'U')) { + } else if (args_has(args, 'U')) { window_push_zoom(w, 1); wp = window_pane_find_up(wp); window_pop_zoom(w); - } else if (args_has(self->args, 'D')) { + } else if (args_has(args, 'D')) { window_push_zoom(w, 1); wp = window_pane_find_down(wp); window_pop_zoom(w); @@ -185,27 +186,26 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (wp == NULL) return (CMD_RETURN_NORMAL); - if (args_has(self->args, 'e')) { + if (args_has(args, 'e')) { wp->flags &= ~PANE_INPUTOFF; return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'd')) { + if (args_has(args, 'd')) { wp->flags |= PANE_INPUTOFF; return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'T')) { - pane_title = format_single(item, args_get(self->args, 'T'), - c, s, wl, wp); - if (screen_set_title(&wp->base, pane_title)) + if (args_has(args, 'T')) { + title = format_single_from_target(item, args_get(args, 'T')); + if (screen_set_title(&wp->base, title)) server_status_window(wp->window); - free(pane_title); + free(title); return (CMD_RETURN_NORMAL); } if (wp == w->active) return (CMD_RETURN_NORMAL); - if (window_push_zoom(w, args_has(self->args, 'Z'))) + if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); if (window_set_active_pane(w, wp, 1)) { diff --git a/cmd-select-window.c b/cmd-select-window.c index 54965e89..377e3633 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -84,23 +84,25 @@ const struct cmd_entry cmd_last_window_entry = { static enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) { - struct cmd_find_state *current = &item->shared->current; - struct winlink *wl = item->target.wl; - struct session *s = item->target.s; + struct args *args = cmd_get_args(self); + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; + struct session *s = target->s; int next, previous, last, activity; - next = self->entry == &cmd_next_window_entry; - if (args_has(self->args, 'n')) + next = (cmd_get_entry(self) == &cmd_next_window_entry); + if (args_has(args, 'n')) next = 1; - previous = self->entry == &cmd_previous_window_entry; - if (args_has(self->args, 'p')) + previous = (cmd_get_entry(self) == &cmd_previous_window_entry); + if (args_has(args, 'p')) previous = 1; - last = self->entry == &cmd_last_window_entry; - if (args_has(self->args, 'l')) + last = (cmd_get_entry(self) == &cmd_last_window_entry); + if (args_has(args, 'l')) last = 1; if (next || previous || last) { - activity = args_has(self->args, 'a'); + activity = args_has(args, 'a'); if (next) { if (session_next(s, activity) != 0) { cmdq_error(item, "no next window"); @@ -125,7 +127,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) * If -T and select-window is invoked on same window as * current, switch to previous window. */ - if (args_has(self->args, 'T') && wl == s->curw) { + if (args_has(args, 'T') && wl == s->curw) { if (session_last(s) != 0) { cmdq_error(item, "no last window"); return (-1); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index cc04a73f..afaf0a81 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -57,21 +57,23 @@ const struct cmd_entry cmd_send_prefix_entry = { }; static struct cmdq_item * -cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, - struct cmdq_item *item, key_code key) +cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, + key_code key) { - struct session *s = fs->s; - struct winlink *wl = fs->wl; - struct window_pane *wp = fs->wp; + struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item); + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; struct window_mode_entry *wme; struct key_table *table; struct key_binding *bd; - wme = TAILQ_FIRST(&fs->wp->modes); + wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode->key_table == NULL) { - if (options_get_number(fs->wp->window->options, "xterm-keys")) + if (options_get_number(wp->window->options, "xterm-keys")) key |= KEYC_XTERM; - if (window_pane_key(wp, item->client, s, wl, key, NULL) != 0) + if (window_pane_key(wp, tc, s, wl, key, NULL) != 0) return (NULL); return (item); } @@ -80,18 +82,17 @@ cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, bd = key_bindings_get(table, key & ~KEYC_XTERM); if (bd != NULL) { table->references++; - item = key_bindings_dispatch(bd, item, c, NULL, &item->target); + after = key_bindings_dispatch(bd, after, tc, NULL, target); key_bindings_unref_table(table); } - return (item); + return (after); } static struct cmdq_item * -cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, - struct cmdq_item *item, struct args *args, int i) +cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, + struct args *args, int i) { const char *s = args->argv[i]; - struct cmdq_item *new_item; struct utf8_data *ud, *uc; wchar_t wc; key_code key; @@ -103,16 +104,16 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, n = strtol(s, &endptr, 16); if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') return (item); - return (cmd_send_keys_inject_key(c, fs, item, KEYC_LITERAL|n)); + return (cmd_send_keys_inject_key(item, after, KEYC_LITERAL|n)); } literal = args_has(args, 'l'); if (!literal) { key = key_string_lookup_string(s); if (key != KEYC_NONE && key != KEYC_UNKNOWN) { - new_item = cmd_send_keys_inject_key(c, fs, item, key); - if (new_item != NULL) - return (new_item); + after = cmd_send_keys_inject_key(item, after, key); + if (after != NULL) + return (after); } literal = 1; } @@ -121,24 +122,26 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, for (uc = ud; uc->size != 0; uc++) { if (utf8_combine(uc, &wc) != UTF8_DONE) continue; - item = cmd_send_keys_inject_key(c, fs, item, wc); + after = cmd_send_keys_inject_key(item, after, wc); } free(ud); } - return (item); + return (after); } static enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c = cmd_find_client(item, NULL, 1); - struct cmd_find_state *fs = &item->target; - struct window_pane *wp = item->target.wp; - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct mouse_event *m = &item->shared->mouse; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item); + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; + struct key_event *event = cmdq_get_event(item); + struct mouse_event *m = &event->m; struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct cmdq_item *after = item; int i; key_code key; u_int np = 1; @@ -152,7 +155,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } if (wme != NULL && (args_has(args, 'X') || args->argc == 0)) { - if (wme == NULL || wme->mode->command == NULL) { + if (wme->mode->command == NULL) { cmdq_error(item, "not in a mode"); return (CMD_RETURN_ERROR); } @@ -167,7 +170,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) } if (!m->valid) m = NULL; - wme->mode->command(wme, c, s, wl, args, m); + wme->mode->command(wme, tc, s, wl, args, m); return (CMD_RETURN_NORMAL); } @@ -177,27 +180,29 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "no mouse target"); return (CMD_RETURN_ERROR); } - window_pane_key(wp, item->client, s, wl, m->key, m); + window_pane_key(wp, tc, s, wl, m->key, m); return (CMD_RETURN_NORMAL); } - if (self->entry == &cmd_send_prefix_entry) { + if (cmd_get_entry(self) == &cmd_send_prefix_entry) { if (args_has(args, '2')) key = options_get_number(s->options, "prefix2"); else key = options_get_number(s->options, "prefix"); - cmd_send_keys_inject_key(c, fs, item, key); + cmd_send_keys_inject_key(item, item, key); return (CMD_RETURN_NORMAL); } if (args_has(args, 'R')) { window_pane_reset_palette(wp); - input_reset(wp, 1); + input_reset(wp->ictx, 1); } for (; np != 0; np--) { - for (i = 0; i < args->argc; i++) - item = cmd_send_keys_inject_string(c, fs, item, args, i); + for (i = 0; i < args->argc; i++) { + after = cmd_send_keys_inject_string(item, after, args, + i); + } } return (CMD_RETURN_NORMAL); diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 96fdf450..0f3fffce 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -54,7 +54,7 @@ const struct cmd_entry cmd_delete_buffer_entry = { static enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct paste_buffer *pb; char *bufdata, *cause; const char *bufname, *olddata; @@ -66,7 +66,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) else pb = paste_get_name(bufname); - if (self->entry == &cmd_delete_buffer_entry) { + if (cmd_get_entry(self) == &cmd_delete_buffer_entry) { if (pb == NULL) pb = paste_get_top(&bufname); if (pb == NULL) { diff --git a/cmd-set-environment.c b/cmd-set-environment.c index a80acd01..3c43b635 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_set_environment_entry = { .name = "set-environment", .alias = "setenv", - .args = { "grt:u", 1, 2 }, - .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", + .args = { "hgrt:u", 1, 2 }, + .usage = "[-hgru] " CMD_TARGET_SESSION_USAGE " name [value]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -46,9 +46,10 @@ const struct cmd_entry cmd_set_environment_entry = { static enum cmd_retval cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct environ *env; - const char *name, *value, *target; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct environ *env; + const char *name, *value, *tflag; name = args->argv[0]; if (*name == '\0') { @@ -65,27 +66,27 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) else value = args->argv[1]; - if (args_has(self->args, 'g')) + if (args_has(args, 'g')) env = global_environ; else { - if (item->target.s == NULL) { - target = args_get(args, 't'); - if (target != NULL) - cmdq_error(item, "no such session: %s", target); + if (target->s == NULL) { + tflag = args_get(args, 't'); + if (tflag != NULL) + cmdq_error(item, "no such session: %s", tflag); else cmdq_error(item, "no current session"); return (CMD_RETURN_ERROR); } - env = item->target.s->environ; + env = target->s->environ; } - if (args_has(self->args, 'u')) { + if (args_has(args, 'u')) { if (value != NULL) { cmdq_error(item, "can't specify a value with -u"); return (CMD_RETURN_ERROR); } environ_unset(env, name); - } else if (args_has(self->args, 'r')) { + } else if (args_has(args, 'r')) { if (value != NULL) { cmdq_error(item, "can't specify a value with -r"); return (CMD_RETURN_ERROR); @@ -96,7 +97,10 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "no value specified"); return (CMD_RETURN_ERROR); } - environ_set(env, name, "%s", value); + if (args_has(args, 'h')) + environ_set(env, name, ENVIRON_HIDDEN, "%s", value); + else + environ_set(env, name, 0, "%s", value); } return (CMD_RETURN_NORMAL); diff --git a/cmd-set-option.c b/cmd-set-option.c index 23b45230..1752d093 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -69,10 +69,10 @@ const struct cmd_entry cmd_set_hook_entry = { .name = "set-hook", .alias = NULL, - .args = { "agRt:u", 1, 2 }, - .usage = "[-agRu] " CMD_TARGET_SESSION_USAGE " hook [command]", + .args = { "agpRt:uw", 1, 2 }, + .usage = "[-agpRuw] " CMD_TARGET_PANE_USAGE " hook [command]", - .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, + .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_set_option_exec @@ -81,12 +81,11 @@ const struct cmd_entry cmd_set_hook_entry = { static enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); - struct cmd_find_state *fs = &item->target; - struct client *c, *loop; - struct session *s = fs->s; - struct winlink *wl = fs->wl; + struct cmd_find_state *target = cmdq_get_target(item); + struct client *loop; + struct session *s = target->s; struct window *w; struct window_pane *wp; struct options *oo; @@ -96,14 +95,13 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) int scope; struct style *sy; - window = (self->entry == &cmd_set_window_option_entry); + window = (cmd_get_entry(self) == &cmd_set_window_option_entry); /* Expand argument. */ - c = cmd_find_client(item, NULL, 1); - argument = format_single(item, args->argv[0], c, s, wl, NULL); + argument = format_single_from_target(item, args->argv[0]); /* If set-hook -R, fire the hook straight away. */ - if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) { + if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) { notify_hook(item, argument); free(argument); return (CMD_RETURN_NORMAL); @@ -123,12 +121,13 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) if (args->argc < 2) value = NULL; else if (args_has(args, 'F')) - value = format_single(item, args->argv[1], c, s, wl, NULL); + value = format_single_from_target(item, args->argv[1]); else value = xstrdup(args->argv[1]); /* Get the scope and table for the option .*/ - scope = options_scope_from_name(args, window, name, fs, &oo, &cause); + scope = options_scope_from_name(args, window, name, target, &oo, + &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) goto out; @@ -288,7 +287,7 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, struct options_entry *parent, const char *value) { const struct options_table_entry *oe; - struct args *args = self->args; + struct args *args = cmd_get_args(self); int append = args_has(args, 'a'); struct options_entry *o; long long number; @@ -309,6 +308,13 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, old = xstrdup(options_get_string(oo, oe->name)); options_set_string(oo, oe->name, append, "%s", value); new = options_get_string(oo, oe->name); + if (strcmp(oe->name, "default-shell") == 0 && + !checkshell(new)) { + options_set_string(oo, oe->name, 0, "%s", old); + free(old); + cmdq_error(item, "not a suitable shell: %s", value); + return (-1); + } if (oe->pattern != NULL && fnmatch(oe->pattern, new, 0) != 0) { options_set_string(oo, oe->name, 0, "%s", old); free(old); diff --git a/cmd-show-environment.c b/cmd-show-environment.c index eb19cf20..3ad31400 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -38,8 +38,8 @@ const struct cmd_entry cmd_show_environment_entry = { .name = "show-environment", .alias = "showenv", - .args = { "gst:", 0, 1 }, - .usage = "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", + .args = { "hgst:", 0, 1 }, + .usage = "[-hgs] " CMD_TARGET_SESSION_USAGE " [name]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -69,9 +69,15 @@ static void cmd_show_environment_print(struct cmd *self, struct cmdq_item *item, struct environ_entry *envent) { - char *escaped; + struct args *args = cmd_get_args(self); + char *escaped; - if (!args_has(self->args, 's')) { + if (!args_has(args, 'h') && (envent->flags & ENVIRON_HIDDEN)) + return; + if (args_has(args, 'h') && (~envent->flags & ENVIRON_HIDDEN)) + return; + + if (!args_has(args, 's')) { if (envent->value != NULL) cmdq_print(item, "%s=%s", envent->name, envent->value); else @@ -91,30 +97,31 @@ cmd_show_environment_print(struct cmd *self, struct cmdq_item *item, static enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct environ *env; struct environ_entry *envent; - const char *target; + const char *tflag; - if ((target = args_get(args, 't')) != NULL) { - if (item->target.s == NULL) { - cmdq_error(item, "no such session: %s", target); + if ((tflag = args_get(args, 't')) != NULL) { + if (target->s == NULL) { + cmdq_error(item, "no such session: %s", tflag); return (CMD_RETURN_ERROR); } } - if (args_has(self->args, 'g')) + if (args_has(args, 'g')) env = global_environ; else { - if (item->target.s == NULL) { - target = args_get(args, 't'); - if (target != NULL) - cmdq_error(item, "no such session: %s", target); + if (target->s == NULL) { + tflag = args_get(args, 't'); + if (tflag != NULL) + cmdq_error(item, "no such session: %s", tflag); else cmdq_error(item, "no current session"); return (CMD_RETURN_ERROR); } - env = item->target.s->environ; + env = target->s->environ; } if (args->argc != 0) { diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 8da12374..02fdb9cd 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -38,26 +38,28 @@ const struct cmd_entry cmd_show_messages_entry = { .args = { "JTt:", 0, 0 }, .usage = "[-JT] " CMD_TARGET_CLIENT_USAGE, - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, .exec = cmd_show_messages_exec }; -static int cmd_show_messages_terminals(struct cmdq_item *, int); - static int -cmd_show_messages_terminals(struct cmdq_item *item, int blank) +cmd_show_messages_terminals(struct cmd *self, struct cmdq_item *item, int blank) { + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); struct tty_term *term; u_int i, n; n = 0; LIST_FOREACH(term, &tty_terms, entry) { + if (args_has(args, 't') && term != tc->tty.term) + continue; if (blank) { cmdq_print(item, "%s", ""); blank = 0; } - cmdq_print(item, "Terminal %u: %s [references=%u, flags=0x%x]:", - n, term->name, term->references, term->flags); + cmdq_print(item, "Terminal %u: %s for %s, flags=0x%x:", n, + term->name, term->tty->client->name, term->flags); n++; for (i = 0; i < tty_term_ncodes(); i++) cmdq_print(item, "%s", tty_term_describe(term, i)); @@ -68,18 +70,15 @@ cmd_show_messages_terminals(struct cmdq_item *item, int blank) static enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct client *c; + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); struct message_entry *msg; char *tim; int done, blank; - if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - done = blank = 0; if (args_has(args, 'T')) { - blank = cmd_show_messages_terminals(item, blank); + blank = cmd_show_messages_terminals(self, item, blank); done = 1; } if (args_has(args, 'J')) { @@ -89,10 +88,9 @@ cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) if (done) return (CMD_RETURN_NORMAL); - TAILQ_FOREACH(msg, &c->message_log, entry) { + TAILQ_FOREACH(msg, &tc->message_log, entry) { tim = ctime(&msg->msg_time); *strchr(tim, '\n') = '\0'; - cmdq_print(item, "%s %s", tim, msg->msg); } diff --git a/cmd-show-options.c b/cmd-show-options.c index da481139..1286037c 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -64,10 +64,10 @@ const struct cmd_entry cmd_show_hooks_entry = { .name = "show-hooks", .alias = NULL, - .args = { "gt:", 0, 1 }, - .usage = "[-g] " CMD_TARGET_SESSION_USAGE, + .args = { "gpt:w", 0, 1 }, + .usage = "[-gpw] " CMD_TARGET_PANE_USAGE, - .target = { 't', CMD_FIND_SESSION, 0 }, + .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_show_options_exec @@ -76,20 +76,18 @@ const struct cmd_entry cmd_show_hooks_entry = { static enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct cmd_find_state *fs = &item->target; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); struct options *oo; char *argument, *name = NULL, *cause; int window, idx, ambiguous, parent, scope; struct options_entry *o; - window = (self->entry == &cmd_show_window_options_entry); + window = (cmd_get_entry(self) == &cmd_show_window_options_entry); if (args->argc == 0) { - scope = options_scope_from_flags(args, window, fs, &oo, &cause); + scope = options_scope_from_flags(args, window, target, &oo, + &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) return (CMD_RETURN_NORMAL); @@ -99,7 +97,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) } return (cmd_show_options_all(self, item, scope, oo)); } - argument = format_single(item, args->argv[0], c, s, wl, NULL); + argument = format_single_from_target(item, args->argv[0]); name = options_match(argument, &idx, &ambiguous); if (name == NULL) { @@ -111,7 +109,8 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "invalid option: %s", argument); goto fail; } - scope = options_scope_from_name(args, window, name, fs, &oo, &cause); + scope = options_scope_from_name(args, window, name, target, &oo, + &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) goto fail; @@ -142,6 +141,7 @@ static void cmd_show_options_print(struct cmd *self, struct cmdq_item *item, struct options_entry *o, int idx, int parent) { + struct args *args = cmd_get_args(self); struct options_array_item *a; const char *name = options_name(o); char *value, *tmp = NULL, *escaped; @@ -153,7 +153,7 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, if (options_isarray(o)) { a = options_array_first(o); if (a == NULL) { - if (!args_has(self->args, 'v')) + if (!args_has(args, 'v')) cmdq_print(item, "%s", name); return; } @@ -168,7 +168,7 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, } value = options_tostring(o, idx, 0); - if (args_has(self->args, 'v')) + if (args_has(args, 'v')) cmdq_print(item, "%s", value); else if (options_isstring(o)) { escaped = args_escape(value); @@ -192,6 +192,7 @@ static enum cmd_retval cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, struct options *oo) { + struct args *args = cmd_get_args(self); const struct options_table_entry *oe; struct options_entry *o; struct options_array_item *a; @@ -209,18 +210,16 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, if (~oe->scope & scope) continue; - if ((self->entry != &cmd_show_hooks_entry && - !args_has(self->args, 'H') && - oe != NULL && + if ((cmd_get_entry(self) != &cmd_show_hooks_entry && + !args_has(args, 'H') && (oe->flags & OPTIONS_TABLE_IS_HOOK)) || - (self->entry == &cmd_show_hooks_entry && - (oe == NULL || - (~oe->flags & OPTIONS_TABLE_IS_HOOK)))) + (cmd_get_entry(self) == &cmd_show_hooks_entry && + (~oe->flags & OPTIONS_TABLE_IS_HOOK))) continue; o = options_get_only(oo, oe->name); if (o == NULL) { - if (!args_has(self->args, 'A')) + if (!args_has(args, 'A')) continue; o = options_get(oo, oe->name); if (o == NULL) @@ -232,7 +231,7 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, if (!options_isarray(o)) cmd_show_options_print(self, item, o, -1, parent); else if ((a = options_array_first(o)) == NULL) { - if (!args_has(self->args, 'v')) { + if (!args_has(args, 'v')) { name = options_name(o); if (parent) cmdq_print(item, "%s*", name); diff --git a/cmd-source-file.c b/cmd-source-file.c index afe45a54..f5a0ca4b 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -122,9 +122,9 @@ cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path) static enum cmd_retval cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); struct cmd_source_file_data *cdata; - struct client *c = item->client; + struct client *c = cmdq_get_client(item); enum cmd_retval retval = CMD_RETURN_NORMAL; char *pattern, *cwd; const char *path, *error; diff --git a/cmd-split-window.c b/cmd-split-window.c index a5fa3acc..130aca2e 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -52,13 +52,14 @@ const struct cmd_entry cmd_split_window_entry = { static enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct cmd_find_state *current = &item->shared->current; + struct args *args = cmd_get_args(self); + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state *target = cmdq_get_target(item); struct spawn_context sc; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp, *new_wp; + struct client *tc = cmdq_get_target_client(item); + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp, *new_wp; enum layout_type type; struct layout_cell *lc; struct cmd_find_state fs; @@ -141,7 +142,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) add = args_first_value(args, 'e', &value); while (add != NULL) { - environ_put(sc.environ, add); + environ_put(sc.environ, add, 0); add = args_next_value(&value); } @@ -172,7 +173,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = SPLIT_WINDOW_TEMPLATE; - cp = format_single(item, template, c, s, wl, new_wp); + cp = format_single(item, template, tc, s, wl, new_wp); cmdq_print(item, "%s", cp); free(cp); } diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 3e0e6e60..021ac224 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -45,16 +45,18 @@ const struct cmd_entry cmd_swap_pane_entry = { static enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); + struct cmd_find_state *source = cmdq_get_source(item); + struct cmd_find_state *target = cmdq_get_target(item); struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; - dst_w = item->target.wl->window; - dst_wp = item->target.wp; - src_w = item->source.wl->window; - src_wp = item->source.wp; + dst_w = target->wl->window; + dst_wp = target->wp; + src_w = source->wl->window; + src_wp = source->wp; if (window_push_zoom(dst_w, args_has(args, 'Z'))) server_redraw_window(dst_w); diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 0c15479d..651a44da 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -45,20 +45,20 @@ const struct cmd_entry cmd_swap_window_entry = { static enum cmd_retval cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item) { - struct session *src, *dst; + struct args *args = cmd_get_args(self); + struct cmd_find_state *source = cmdq_get_source(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct session *src = source->s, *dst = target->s; struct session_group *sg_src, *sg_dst; - struct winlink *wl_src, *wl_dst; + struct winlink *wl_src = source->wl, *wl_dst = target->wl; struct window *w_src, *w_dst; - wl_src = item->source.wl; - src = item->source.s; sg_src = session_group_contains(src); - - wl_dst = item->target.wl; - dst = item->target.s; sg_dst = session_group_contains(dst); - if (src != dst && sg_src != NULL && sg_dst != NULL && + if (src != dst && + sg_src != NULL && + sg_dst != NULL && sg_src == sg_dst) { cmdq_error(item, "can't move window, sessions are grouped"); return (CMD_RETURN_ERROR); @@ -77,7 +77,7 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item) wl_src->window = w_dst; TAILQ_INSERT_TAIL(&w_dst->winlinks, wl_src, wentry); - if (args_has(self->args, 'd')) { + if (args_has(args, 'd')) { session_select(dst, wl_dst->idx); if (src != dst) session_select(src, wl_src->idx); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 61677761..82510ce6 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -40,18 +40,20 @@ const struct cmd_entry cmd_switch_client_entry = { /* -t is special */ - .flags = CMD_READONLY, + .flags = CMD_READONLY|CMD_CLIENT_CFLAG, .exec = cmd_switch_client_exec }; static enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state target; const char *tflag = args_get(args, 't'); enum cmd_find_type type; int flags; - struct client *c; + struct client *tc = cmdq_get_target_client(item); struct session *s; struct winlink *wl; struct window *w; @@ -59,9 +61,6 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) const char *tablename; struct key_table *table; - if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (tflag != NULL && tflag[strcspn(tflag, ":.%")] != '\0') { type = CMD_FIND_PANE; flags = 0; @@ -69,15 +68,14 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) type = CMD_FIND_SESSION; flags = CMD_FIND_PREFER_UNATTACHED; } - if (cmd_find_target(&item->target, item, tflag, type, flags) != 0) + if (cmd_find_target(&target, item, tflag, type, flags) != 0) return (CMD_RETURN_ERROR); - s = item->target.s; - wl = item->target.wl; - w = wl->window; - wp = item->target.wp; + s = target.s; + wl = target.wl; + wp = target.wp; if (args_has(args, 'r')) - c->flags ^= CLIENT_READONLY; + tc->flags ^= CLIENT_READONLY; tablename = args_get(args, 'T'); if (tablename != NULL) { @@ -87,24 +85,24 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } table->references++; - key_bindings_unref_table(c->keytable); - c->keytable = table; + key_bindings_unref_table(tc->keytable); + tc->keytable = table; return (CMD_RETURN_NORMAL); } if (args_has(args, 'n')) { - if ((s = session_next_session(c->session)) == NULL) { + if ((s = session_next_session(tc->session)) == NULL) { cmdq_error(item, "can't find next session"); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'p')) { - if ((s = session_previous_session(c->session)) == NULL) { + if ((s = session_previous_session(tc->session)) == NULL) { cmdq_error(item, "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 (tc->last_session != NULL && session_alive(tc->last_session)) + s = tc->last_session; else s = NULL; if (s == NULL) { @@ -112,10 +110,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } } else { - if (item->client == NULL) + if (cmdq_get_client(item) == NULL) return (CMD_RETURN_NORMAL); if (wl != NULL && wp != NULL) { - if (window_push_zoom(w, args_has(self->args, 'Z'))) + w = wl->window; + if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); window_set_active_pane(w, wp, 1); @@ -124,28 +123,28 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) } if (wl != NULL) { session_set_current(s, wl); - cmd_find_from_session(&item->shared->current, s, 0); + cmd_find_from_session(current, s, 0); } } if (!args_has(args, 'E')) - environ_update(s->options, c->environ, s->environ); - - if (c->session != NULL && c->session != s) - c->last_session = c->session; - c->session = s; - if (~item->shared->flags & CMDQ_SHARED_REPEAT) - server_client_set_key_table(c, NULL); - tty_update_client_offset(c); - status_timer_start(c); - notify_client("client-session-changed", c); + environ_update(s->options, tc->environ, s->environ); + + if (tc->session != NULL && tc->session != s) + tc->last_session = tc->session; + tc->session = s; + if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) + server_client_set_key_table(tc, NULL); + tty_update_client_offset(tc); + status_timer_start(tc); + notify_client("client-session-changed", tc); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); server_check_unattached(); - server_redraw_client(c); + server_redraw_client(tc); s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = c; + s->curw->window->latest = tc; recalculate_sizes(); alerts_check_session(s); diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index d65ac91a..4b9f39a6 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -42,7 +42,7 @@ const struct cmd_entry cmd_unbind_key_entry = { static enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); key_code key; const char *tablename; diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 4f438a7f..807a661a 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -120,7 +120,7 @@ cmd_wait_for_remove(struct wait_channel *wc) static enum cmd_retval cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = cmd_get_args(self); const char *name = args->argv[0]; struct wait_channel *wc, wc0; @@ -167,7 +167,7 @@ static enum cmd_retval cmd_wait_for_wait(struct cmdq_item *item, const char *name, struct wait_channel *wc) { - struct client *c = item->client; + struct client *c = cmdq_get_client(item); struct wait_item *wi; if (c == NULL) { @@ -198,7 +198,7 @@ cmd_wait_for_lock(struct cmdq_item *item, const char *name, { struct wait_item *wi; - if (item->client == NULL) { + if (cmdq_get_client(item) == NULL) { cmdq_error(item, "not able to lock"); return (CMD_RETURN_ERROR); } @@ -43,6 +43,7 @@ extern const struct cmd_entry cmd_delete_buffer_entry; extern const struct cmd_entry cmd_detach_client_entry; extern const struct cmd_entry cmd_display_menu_entry; extern const struct cmd_entry cmd_display_message_entry; +extern const struct cmd_entry cmd_display_popup_entry; extern const struct cmd_entry cmd_display_panes_entry; extern const struct cmd_entry cmd_down_pane_entry; extern const struct cmd_entry cmd_find_window_entry; @@ -132,6 +133,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_detach_client_entry, &cmd_display_menu_entry, &cmd_display_message_entry, + &cmd_display_popup_entry, &cmd_display_panes_entry, &cmd_find_window_entry, &cmd_has_session_entry, @@ -204,8 +206,27 @@ const struct cmd_entry *cmd_table[] = { NULL }; +/* Instance of a command. */ +struct cmd { + const struct cmd_entry *entry; + struct args *args; + u_int group; + + char *file; + u_int line; + + char *alias; + int argc; + char **argv; + + TAILQ_ENTRY(cmd) qentry; +}; +TAILQ_HEAD(cmds, cmd); + +/* Next group number for new command list. */ static u_int cmd_list_next_group = 1; +/* Log an argument vector. */ void printflike(3, 4) cmd_log_argv(int argc, char **argv, const char *fmt, ...) { @@ -222,6 +243,7 @@ cmd_log_argv(int argc, char **argv, const char *fmt, ...) free(prefix); } +/* Prepend to an argument vector. */ void cmd_prepend_argv(int *argc, char ***argv, char *arg) { @@ -238,6 +260,7 @@ cmd_prepend_argv(int *argc, char ***argv, char *arg) (*argc)++; } +/* Append to an argument vector. */ void cmd_append_argv(int *argc, char ***argv, char *arg) { @@ -245,6 +268,7 @@ cmd_append_argv(int *argc, char ***argv, char *arg) (*argv)[(*argc)++] = xstrdup(arg); } +/* Pack an argument vector up into a buffer. */ int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { @@ -267,6 +291,7 @@ cmd_pack_argv(int argc, char **argv, char *buf, size_t len) return (0); } +/* Unpack an argument vector from a packed buffer. */ int cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) { @@ -295,6 +320,7 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) return (0); } +/* Copy an argument vector, ensuring it is terminated by NULL. */ char ** cmd_copy_argv(int argc, char **argv) { @@ -311,6 +337,7 @@ cmd_copy_argv(int argc, char **argv) return (new_argv); } +/* Free an argument vector. */ void cmd_free_argv(int argc, char **argv) { @@ -323,6 +350,7 @@ cmd_free_argv(int argc, char **argv) free(argv); } +/* Convert argument vector to a string. */ char * cmd_stringify_argv(int argc, char **argv) { @@ -349,6 +377,38 @@ cmd_stringify_argv(int argc, char **argv) return (buf); } +/* Get entry for command. */ +const struct cmd_entry * +cmd_get_entry(struct cmd *cmd) +{ + return (cmd->entry); +} + +/* Get arguments for command. */ +struct args * +cmd_get_args(struct cmd *cmd) +{ + return (cmd->args); +} + +/* Get group for command. */ +u_int +cmd_get_group(struct cmd *cmd) +{ + return (cmd->group); +} + +/* Get file and line for command. */ +void +cmd_get_source(struct cmd *cmd, const char **file, u_int *line) +{ + if (file != NULL) + *file = cmd->file; + if (line != NULL) + *line = cmd->line; +} + +/* Look for an alias for a command. */ char * cmd_get_alias(const char *name) { @@ -379,6 +439,7 @@ cmd_get_alias(const char *name) return (NULL); } +/* Look up a command entry by name. */ static const struct cmd_entry * cmd_find(const char *name, char **cause) { @@ -428,6 +489,7 @@ ambiguous: return (NULL); } +/* Parse a single command from an argument vector. */ struct cmd * cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) { @@ -476,6 +538,7 @@ usage: return (NULL); } +/* Free a command. */ void cmd_free(struct cmd *cmd) { @@ -488,6 +551,7 @@ cmd_free(struct cmd *cmd) free(cmd); } +/* Get a command as a string. */ char * cmd_print(struct cmd *cmd) { @@ -503,6 +567,7 @@ cmd_print(struct cmd *cmd) return (out); } +/* Create a new command list. */ struct cmd_list * cmd_list_new(void) { @@ -511,29 +576,33 @@ cmd_list_new(void) cmdlist = xcalloc(1, sizeof *cmdlist); cmdlist->references = 1; cmdlist->group = cmd_list_next_group++; - TAILQ_INIT(&cmdlist->list); + cmdlist->list = xcalloc(1, sizeof *cmdlist->list); + TAILQ_INIT(cmdlist->list); return (cmdlist); } +/* Append a command to a command list. */ void cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) { cmd->group = cmdlist->group; - TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); + TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); } +/* Move all commands from one command list to another */ void cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) { struct cmd *cmd, *cmd1; - TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) { - TAILQ_REMOVE(&from->list, cmd, qentry); - TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); + TAILQ_FOREACH_SAFE(cmd, from->list, qentry, cmd1) { + TAILQ_REMOVE(from->list, cmd, qentry); + TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); } cmdlist->group = cmd_list_next_group++; } +/* Free a command list. */ void cmd_list_free(struct cmd_list *cmdlist) { @@ -542,36 +611,46 @@ cmd_list_free(struct cmd_list *cmdlist) if (--cmdlist->references != 0) return; - TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) { - TAILQ_REMOVE(&cmdlist->list, cmd, qentry); + TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) { + TAILQ_REMOVE(cmdlist->list, cmd, qentry); cmd_free(cmd); } - + free(cmdlist->list); free(cmdlist); } +/* Get a command list as a string. */ char * cmd_list_print(struct cmd_list *cmdlist, int escaped) { - struct cmd *cmd; + struct cmd *cmd, *next; char *buf, *this; size_t len; len = 1; buf = xcalloc(1, len); - TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { + TAILQ_FOREACH(cmd, cmdlist->list, qentry) { this = cmd_print(cmd); - len += strlen(this) + 4; + len += strlen(this) + 6; buf = xrealloc(buf, len); strlcat(buf, this, len); - if (TAILQ_NEXT(cmd, qentry) != NULL) { - if (escaped) - strlcat(buf, " \\; ", len); - else - strlcat(buf, " ; ", len); + + next = TAILQ_NEXT(cmd, qentry); + if (next != NULL) { + if (cmd->group != next->group) { + if (escaped) + strlcat(buf, " \\;\\; ", len); + else + strlcat(buf, " ;; ", len); + } else { + if (escaped) + strlcat(buf, " \\; ", len); + else + strlcat(buf, " ; ", len); + } } free(this); @@ -580,6 +659,46 @@ cmd_list_print(struct cmd_list *cmdlist, int escaped) return (buf); } +/* Get first command in list. */ +struct cmd * +cmd_list_first(struct cmd_list *cmdlist) +{ + return (TAILQ_FIRST(cmdlist->list)); +} + +/* Get next command in list. */ +struct cmd * +cmd_list_next(struct cmd *cmd) +{ + return (TAILQ_NEXT(cmd, qentry)); +} + +/* Do all of the commands in this command list have this flag? */ +int +cmd_list_all_have(struct cmd_list *cmdlist, int flag) +{ + struct cmd *cmd; + + TAILQ_FOREACH(cmd, cmdlist->list, qentry) { + if (~cmd->entry->flags & flag) + return (0); + } + return (1); +} + +/* Do any of the commands in this command list have this flag? */ +int +cmd_list_any_have(struct cmd_list *cmdlist, int flag) +{ + struct cmd *cmd; + + TAILQ_FOREACH(cmd, cmdlist->list, qentry) { + if (cmd->entry->flags & flag) + return (1); + } + return (0); +} + /* Adjust current mouse position for a pane. */ int cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, diff --git a/configure.ac b/configure.ac index 6a9ba594..dc133c25 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.1a) +AC_INIT([tmux], next-3.2) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) @@ -87,6 +87,12 @@ AC_CHECK_HEADERS([ \ util.h \ ]) +# Look for sys_signame. +AC_SEARCH_LIBS(sys_signame, , AC_DEFINE(HAVE_SYS_SIGNAME)) + +# Look for fmod. +AC_CHECK_LIB(m, fmod) + # Look for library needed for flock. AC_SEARCH_LIBS(flock, bsd) @@ -42,7 +42,7 @@ control_write(struct client *c, const char *fmt, ...) static enum cmd_retval control_error(struct cmdq_item *item, void *data) { - struct client *c = item->client; + struct client *c = cmdq_get_client(item); char *error = data; cmdq_guard(item, "begin", 1); @@ -56,13 +56,13 @@ control_error(struct cmdq_item *item, void *data) /* Control input callback. Read lines and fire commands. */ static void control_callback(__unused struct client *c, __unused const char *path, - int error, int closed, struct evbuffer *buffer, __unused void *data) + int read_error, int closed, struct evbuffer *buffer, __unused void *data) { - char *line; - struct cmdq_item *item; - struct cmd_parse_result *pr; + char *line, *error; + struct cmdq_state *state; + enum cmd_parse_status status; - if (closed || error != 0) + if (closed || read_error != 0) c->flags |= CLIENT_EXIT; for (;;) { @@ -76,21 +76,11 @@ control_callback(__unused struct client *c, __unused const char *path, break; } - pr = cmd_parse_from_string(line, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - break; - case CMD_PARSE_ERROR: - item = cmdq_get_callback(control_error, pr->error); - cmdq_append(c, item); - break; - case CMD_PARSE_SUCCESS: - item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); - item->shared->flags |= CMDQ_SHARED_CONTROL; - cmdq_append(c, item); - cmd_list_free(pr->cmdlist); - break; - } + state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL); + status = cmd_parse_and_append(line, NULL, c, state, &error); + if (status == CMD_PARSE_ERROR) + cmdq_append(c, cmdq_get_callback(control_error, error)); + cmdq_free_state(state); free(line); } @@ -86,8 +86,10 @@ environ_copy(struct environ *srcenv, struct environ *dstenv) RB_FOREACH(envent, environ, srcenv) { if (envent->value == NULL) environ_clear(dstenv, envent->name); - else - environ_set(dstenv, envent->name, "%s", envent->value); + else { + environ_set(dstenv, envent->name, envent->flags, + "%s", envent->value); + } } } @@ -103,18 +105,21 @@ environ_find(struct environ *env, const char *name) /* Set an environment variable. */ void -environ_set(struct environ *env, const char *name, const char *fmt, ...) +environ_set(struct environ *env, const char *name, int flags, const char *fmt, + ...) { struct environ_entry *envent; va_list ap; va_start(ap, fmt); if ((envent = environ_find(env, name)) != NULL) { + envent->flags = flags; free(envent->value); xvasprintf(&envent->value, fmt, ap); } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); + envent->flags = flags; xvasprintf(&envent->value, fmt, ap); RB_INSERT(environ, env, envent); } @@ -133,6 +138,7 @@ environ_clear(struct environ *env, const char *name) } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); + envent->flags = 0; envent->value = NULL; RB_INSERT(environ, env, envent); } @@ -140,7 +146,7 @@ environ_clear(struct environ *env, const char *name) /* Set an environment variable from a NAME=VALUE string. */ void -environ_put(struct environ *env, const char *var) +environ_put(struct environ *env, const char *var, int flags) { char *name, *value; @@ -152,7 +158,7 @@ environ_put(struct environ *env, const char *var) name = xstrdup(var); name[strcspn(name, "=")] = '\0'; - environ_set(env, name, "%s", value); + environ_set(env, name, flags, "%s", value); free(name); } @@ -170,7 +176,7 @@ environ_unset(struct environ *env, const char *name) free(envent); } -/* Copy variables from a destination into a source * environment. */ +/* Copy variables from a destination into a source environment. */ void environ_update(struct options *oo, struct environ *src, struct environ *dst) { @@ -188,7 +194,7 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst) if ((envent = environ_find(src, ov->string)) == NULL) environ_clear(dst, ov->string); else - environ_set(dst, envent->name, "%s", envent->value); + environ_set(dst, envent->name, 0, "%s", envent->value); a = options_array_next(a); } } @@ -201,7 +207,9 @@ environ_push(struct environ *env) environ = xcalloc(1, sizeof *environ); RB_FOREACH(envent, environ, env) { - if (envent->value != NULL && *envent->name != '\0') + if (envent->value != NULL && + *envent->name != '\0' && + (~envent->flags & ENVIRON_HIDDEN)) setenv(envent->name, envent->value, 1); } } @@ -243,14 +251,15 @@ environ_for_session(struct session *s, int no_TERM) if (!no_TERM) { value = options_get_string(global_options, "default-terminal"); - environ_set(env, "TERM", "%s", value); + environ_set(env, "TERM", 0, "%s", value); } if (s != NULL) idx = s->id; else idx = -1; - environ_set(env, "TMUX", "%s,%ld,%d", socket_path, (long)getpid(), idx); + environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(), + idx); return (env); } diff --git a/format-draw.c b/format-draw.c index 85248aa6..3ac33ce4 100644 --- a/format-draw.c +++ b/format-draw.c @@ -804,7 +804,7 @@ format_width(const char *expanded) if (cp[0] == '#' && cp[1] == '[') { end = format_skip(cp + 2, "]"); if (end == NULL) - return 0; + return (0); cp = end + 1; } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) @@ -23,6 +23,7 @@ #include <errno.h> #include <fnmatch.h> #include <libgen.h> +#include <math.h> #include <regex.h> #include <stdarg.h> #include <stdlib.h> @@ -49,7 +50,6 @@ static void format_add_tv(struct format_tree *, const char *, struct timeval *); static int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); - static void format_defaults_session(struct format_tree *, struct session *); static void format_defaults_client(struct format_tree *, struct client *); @@ -100,6 +100,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_SESSIONS 0x80 #define FORMAT_WINDOWS 0x100 #define FORMAT_PANES 0x200 +#define FORMAT_PRETTY 0x400 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 @@ -354,7 +355,7 @@ format_job_get(struct format_tree *ft, const char *cmd) if (force || (fj->job == NULL && fj->last != t)) { fj->job = job_run(expanded, NULL, server_client_get_cwd(ft->client, NULL), format_job_update, - format_job_complete, NULL, fj, JOB_NOWAIT); + format_job_complete, NULL, fj, JOB_NOWAIT, -1, -1); if (fj->job == NULL) { free(fj->out); xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); @@ -900,11 +901,12 @@ static void format_cb_pane_at_top(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; - struct window *w = wp->window; + struct window *w; int status, flag; if (wp == NULL) return; + w = wp->window; status = options_get_number(w->options, "pane-border-status"); if (status == PANE_STATUS_TOP) @@ -919,11 +921,12 @@ static void format_cb_pane_at_bottom(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; - struct window *w = wp->window; + struct window *w; int status, flag; if (wp == NULL) return; + w = wp->window; status = options_get_number(w->options, "pane-border-status"); if (status == PANE_STATUS_BOTTOM) @@ -952,7 +955,7 @@ format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe) char * format_grid_word(struct grid *gd, u_int x, u_int y) { - struct grid_line *gl; + const struct grid_line *gl; struct grid_cell gc; const char *ws; struct utf8_data *ud = NULL; @@ -963,7 +966,6 @@ format_grid_word(struct grid *gd, u_int x, u_int y) ws = options_get_string(global_s_options, "word-separators"); - y = gd->hsize + y; for (;;) { grid_get_cell(gd, x, y, &gc); if (gc.flags & GRID_FLAG_PADDING) @@ -976,7 +978,7 @@ format_grid_word(struct grid *gd, u_int x, u_int y) if (x == 0) { if (y == 0) break; - gl = &gd->linedata[y - 1]; + gl = grid_peek_line(gd, y - 1); if (~gl->flags & GRID_LINE_WRAPPED) break; y--; @@ -992,7 +994,7 @@ format_grid_word(struct grid *gd, u_int x, u_int y) if (end == 0 || x == end - 1) { if (y == gd->hsize + gd->sy - 1) break; - gl = &gd->linedata[y]; + gl = grid_peek_line(gd, y); if (~gl->flags & GRID_LINE_WRAPPED) break; y++; @@ -1024,6 +1026,7 @@ static void format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp; + struct grid *gd; u_int x, y; char *s; @@ -1032,12 +1035,19 @@ format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) wp = cmd_mouse_pane(&ft->m, NULL, NULL); if (wp == NULL) return; - if (!TAILQ_EMPTY (&wp->modes)) - return; if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) return; - s = format_grid_word(wp->base.grid, x, y); + if (!TAILQ_EMPTY(&wp->modes)) { + if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || + TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) + s = window_copy_get_word(wp, x, y); + else + s = NULL; + } else { + gd = wp->base.grid; + s = format_grid_word(gd, x, gd->hsize + y); + } if (s != NULL) fe->value = s; } @@ -1052,7 +1062,6 @@ format_grid_line(struct grid *gd, u_int y) size_t size = 0; char *s = NULL; - y = gd->hsize + y; for (x = 0; x < grid_line_length(gd, y); x++) { grid_get_cell(gd, x, y, &gc); if (gc.flags & GRID_FLAG_PADDING) @@ -1074,6 +1083,7 @@ static void format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp; + struct grid *gd; u_int x, y; char *s; @@ -1082,18 +1092,25 @@ format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) wp = cmd_mouse_pane(&ft->m, NULL, NULL); if (wp == NULL) return; - if (!TAILQ_EMPTY (&wp->modes)) - return; if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) return; - s = format_grid_line(wp->base.grid, y); + if (!TAILQ_EMPTY(&wp->modes)) { + if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || + TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) + s = window_copy_get_line(wp, y); + else + s = NULL; + } else { + gd = wp->base.grid; + s = format_grid_line(gd, gd->hsize + y); + } if (s != NULL) fe->value = s; } -/* Merge a format tree. */ -static void +/* Merge one format tree into another. */ +void format_merge(struct format_tree *ft, struct format_tree *from) { struct format_entry *fe; @@ -1108,19 +1125,13 @@ format_merge(struct format_tree *ft, struct format_tree *from) static void format_create_add_item(struct format_tree *ft, struct cmdq_item *item) { - struct mouse_event *m; + struct key_event *event = cmdq_get_event(item); + struct mouse_event *m = &event->m; struct window_pane *wp; u_int x, y; - if (item->cmd != NULL) - format_add(ft, "command", "%s", item->cmd->entry->name); - - if (item->shared == NULL) - return; - if (item->shared->formats != NULL) - format_merge(ft, item->shared->formats); + cmdq_merge_formats(item, ft); - m = &item->shared->mouse; if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) { format_add(ft, "mouse_pane", "%%%u", wp->id); if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) { @@ -1312,6 +1323,53 @@ format_quote(const char *s) return (out); } +/* Make a prettier time. */ +static char * +format_pretty_time(time_t t) +{ + struct tm now_tm, tm; + time_t now; + char s[6]; + int y, m, d; + + time(&now); + if (now < t) + now = t; + localtime_r(&now, &now_tm); + localtime_r(&t, &tm); + + y = now_tm.tm_year - 1; + if (tm.tm_year < y || + (tm.tm_year == y && + (tm.tm_mon <= now_tm.tm_mon || tm.tm_mday <= now_tm.tm_mday))) { + /* Last year. */ + strftime(s, sizeof s, "%h%y", &tm); + return (xstrdup(s)); + } + if (now_tm.tm_mon == 0) + m = 11; + else + m = now_tm.tm_mon - 1; + if (tm.tm_mon < m || (tm.tm_mon == m && tm.tm_mday < now_tm.tm_mday)) { + /* Last month. */ + strftime(s, sizeof s, "%d%b", &tm); + return (xstrdup(s)); + } + if (now_tm.tm_mday == 0) + d = 31; + else + d = now_tm.tm_mday - 1; + if (tm.tm_mday < d || + (tm.tm_mday == d && tm.tm_mday < now_tm.tm_mday)) { + /* This day. */ + strftime(s, sizeof s, "%a%d", &tm); + return (xstrdup(s)); + } + /* Today. */ + strftime(s, sizeof s, "%H:%M", &tm); + return (xstrdup(s)); +} + /* Find a format entry. */ static char * format_find(struct format_tree *ft, const char *key, int modifiers) @@ -1348,9 +1406,13 @@ format_find(struct format_tree *ft, const char *key, int modifiers) if (modifiers & FORMAT_TIMESTRING) { if (fe->t == 0) return (NULL); - ctime_r(&fe->t, s); - s[strcspn(s, "\n")] = '\0'; - found = xstrdup(s); + if (modifiers & FORMAT_PRETTY) + found = format_pretty_time(fe->t); + else { + ctime_r(&fe->t, s); + s[strcspn(s, "\n")] = '\0'; + found = xstrdup(s); + } goto found; } if (fe->t != 0) { @@ -1522,7 +1584,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lbdtqETSWP<>", cp[0]) != NULL && + if (strchr("lbdqETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -1543,7 +1605,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) } /* Now try single character with arguments. */ - if (strchr("mCs=p", cp[0]) == NULL) + if (strchr("mCst=pe", cp[0]) == NULL) break; c = cp[0]; @@ -1601,7 +1663,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) return (NULL); } *s = cp + 1; - return list; + return (list); } /* Match against an fnmatch(3) pattern or regular expression. */ @@ -1799,6 +1861,108 @@ format_loop_panes(struct format_tree *ft, const char *fmt) return (value); } +static char * +format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, + const char *copy) +{ + int argc = mexp->argc; + const char *errstr; + char *endch, *value, *left = NULL, *right = NULL; + int use_fp = 0; + u_int prec = 0; + double mleft, mright, result; + enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator; + + if (strcmp(mexp->argv[0], "+") == 0) + operator = ADD; + else if (strcmp(mexp->argv[0], "-") == 0) + operator = SUBTRACT; + else if (strcmp(mexp->argv[0], "*") == 0) + operator = MULTIPLY; + else if (strcmp(mexp->argv[0], "/") == 0) + operator = DIVIDE; + else if (strcmp(mexp->argv[0], "%") == 0 || + strcmp(mexp->argv[0], "m") == 0) + operator = MODULUS; + else { + format_log(ft, "expression has no valid operator: '%s'", + mexp->argv[0]); + goto fail; + } + + /* The second argument may be flags. */ + if (argc >= 2 && strchr(mexp->argv[1], 'f') != NULL) { + use_fp = 1; + prec = 2; + } + + /* The third argument may be precision. */ + if (argc >= 3) { + prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) { + format_log (ft, "expression precision %s: %s", errstr, + mexp->argv[2]); + goto fail; + } + } + + if (format_choose(ft, copy, &left, &right, 1) != 0) { + format_log(ft, "expression syntax error"); + goto fail; + } + + mleft = strtod(left, &endch); + if (*endch != '\0') { + format_log(ft, "expression left side is invalid: %s", left); + goto fail; + } + + mright = strtod(right, &endch); + if (*endch != '\0') { + format_log(ft, "expression right side is invalid: %s", right); + goto fail; + } + + if (!use_fp) { + mleft = (long long)mleft; + mright = (long long)mright; + } + format_log(ft, "expression left side is: %.*f", prec, mleft); + format_log(ft, "expression right side is: %.*f", prec, mright); + + switch (operator) { + case ADD: + result = mleft + mright; + break; + case SUBTRACT: + result = mleft - mright; + break; + case MULTIPLY: + result = mleft * mright; + break; + case DIVIDE: + result = mleft / mright; + break; + case MODULUS: + result = fmod(mleft, mright); + break; + } + if (use_fp) + xasprintf(&value, "%.*f", prec, result); + else + xasprintf(&value, "%.*f", prec, (double)(long long)result); + format_log(ft, "expression result is %s", value); + + free(right); + free(left); + return (value); + +fail: + free(right); + free(left); + return (NULL); +} + /* Replace a key. */ static int format_replace(struct format_tree *ft, const char *key, size_t keylen, @@ -1811,7 +1975,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, size_t valuelen; int modifiers = 0, limit = 0, width = 0, j; struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; - struct format_modifier **sub = NULL; + struct format_modifier **sub = NULL, *mexp = NULL; u_int i, count, nsub = 0; /* Make a copy of the key. */ @@ -1863,6 +2027,11 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, if (errptr != NULL) width = 0; break; + case 'e': + if (fm->argc < 1 || fm->argc > 3) + break; + mexp = fm; + break; case 'l': modifiers |= FORMAT_LITERAL; break; @@ -1874,6 +2043,10 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, break; case 't': modifiers |= FORMAT_TIMESTRING; + if (fm->argc < 1) + break; + if (strchr(fm->argv[0], 'p') != NULL) + modifiers |= FORMAT_PRETTY; break; case 'q': modifiers |= FORMAT_QUOTE; @@ -2039,6 +2212,10 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, free(condition); free(found); + } else if (mexp != NULL) { + value = format_replace_expression(mexp, ft, copy); + if (value == NULL) + value = xstrdup(""); } else { /* Neither: look up directly. */ value = format_find(ft, copy, modifiers); @@ -2296,7 +2473,7 @@ format_single(struct cmdq_item *item, const char *fmt, struct client *c, char *expanded; if (item != NULL) - ft = format_create(item->client, item, FORMAT_NONE, 0); + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); else ft = format_create(NULL, item, FORMAT_NONE, 0); format_defaults(ft, c, s, wl, wp); @@ -2306,11 +2483,23 @@ format_single(struct cmdq_item *item, const char *fmt, struct client *c, return (expanded); } +/* Expand a single string using target. */ +char * +format_single_from_target(struct cmdq_item *item, const char *fmt) +{ + struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item); + + return (format_single(item, fmt, tc, target->s, target->wl, target->wp)); +} + /* Set defaults for any of arguments that are not NULL. */ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp) { + struct paste_buffer *pb; + if (c != NULL && c->name != NULL) log_debug("%s: c=%s", __func__, c->name); else @@ -2350,6 +2539,10 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s, format_defaults_winlink(ft, wl); if (wp != NULL) format_defaults_pane(ft, wp); + + pb = paste_get_top (NULL); + if (pb != NULL) + format_defaults_paste_buffer(ft, pb); } /* Set default format keys for a session. */ @@ -2361,6 +2554,7 @@ format_defaults_session(struct format_tree *ft, struct session *s) ft->s = s; format_add(ft, "session_name", "%s", s->name); + format_add(ft, "session_path", "%s", s->cwd); format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); format_add(ft, "session_id", "$%u", s->id); @@ -2391,6 +2585,11 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add_cb(ft, "session_alerts", format_cb_session_alerts); format_add_cb(ft, "session_stack", format_cb_session_stack); + + if (server_check_marked() && marked_pane.s == s) + format_add(ft, "session_marked", "1"); + else + format_add(ft, "session_marked", "0"); } /* Set default format keys for a client. */ @@ -2415,8 +2614,11 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_control_mode", "%d", !!(c->flags & CLIENT_CONTROL)); - if (tty->term_name != NULL) - format_add(ft, "client_termname", "%s", tty->term_name); + format_add(ft, "client_termname", "%s", c->term_name); + format_add(ft, "client_termfeatures", "%s", + tty_get_features(c->term_features)); + if (c->term_type != NULL) + format_add(ft, "client_termtype", "%s", c->term_type); format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); @@ -2431,7 +2633,7 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_prefix", "%d", 1); format_add(ft, "client_key_table", "%s", c->keytable->name); - if (tty->flags & TTY_UTF8) + if (c->flags & CLIENT_UTF8) format_add(ft, "client_utf8", "%d", 1); else format_add(ft, "client_utf8", "%d", 0); @@ -2552,6 +2754,9 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "history_limit", "%u", gd->hlimit); format_add_cb(ft, "history_bytes", format_cb_history_bytes); + format_add(ft, "pane_written", "%zu", wp->written); + format_add(ft, "pane_skipped", "%zu", wp->skipped); + if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); format_add(ft, "pane_index", "%u", idx); @@ -2614,9 +2819,11 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); - format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); - format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); - format_add(ft, "alternate_saved_y", "%u", wp->saved_cy); + format_add(ft, "alternate_on", "%d", wp->base.saved_grid != NULL); + if (wp->base.saved_cx != UINT_MAX) + format_add(ft, "alternate_saved_x", "%u", wp->base.saved_cx); + if (wp->base.saved_cy != UINT_MAX) + format_add(ft, "alternate_saved_y", "%u", wp->base.saved_cy); format_add(ft, "cursor_flag", "%d", !!(wp->base.mode & MODE_CURSOR)); @@ -48,8 +48,6 @@ static const struct grid_cell_entry grid_cleared_entry = { GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } } }; -static void grid_empty_line(struct grid *, u_int, u_int); - /* Store cell in entry. */ static void grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc, @@ -258,7 +256,10 @@ grid_create(u_int sx, u_int sy, u_int hlimit) gd->sx = sx; gd->sy = sy; - gd->flags = GRID_HISTORY; + if (hlimit != 0) + gd->flags = GRID_HISTORY; + else + gd->flags = 0; gd->hscrolled = 0; gd->hsize = 0; @@ -348,6 +349,19 @@ grid_collect_history(struct grid *gd) gd->hscrolled = gd->hsize; } +/* Remove lines from the bottom of the history. */ +void +grid_remove_history(struct grid *gd, u_int ny) +{ + u_int yy; + + if (ny > gd->hsize) + return; + for (yy = 0; yy < ny; yy++) + grid_free_line(gd, gd->hsize + gd->sy - 1 - yy); + gd->hsize -= ny; +} + /* * Scroll the entire visible screen, moving one line into the history. Just * allocate a new line at the bottom and move the history size indicator. @@ -438,7 +452,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg) } /* Empty a line and set background colour if needed. */ -static void +void grid_empty_line(struct grid *gd, u_int py, u_int bg) { memset(&gd->linedata[py], 0, sizeof gd->linedata[py]); @@ -755,15 +769,15 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values) 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; + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + values[n++] = gc->bg + 10; break; } } @@ -1327,17 +1341,13 @@ grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy) void grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy) { - u_int yy, ax = 0, ay = 0; + u_int yy, ay = 0; for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) { if (ay == wy) break; - if (gd->linedata[yy].flags & GRID_LINE_WRAPPED) - ax += gd->linedata[yy].cellused; - else { - ax = 0; + if (~gd->linedata[yy].flags & GRID_LINE_WRAPPED) ay++; - } } /* diff --git a/input-keys.c b/input-keys.c index 69c5199e..04ecb264 100644 --- a/input-keys.c +++ b/input-keys.c @@ -148,9 +148,25 @@ input_split2(u_int c, u_char *dst) return (1); } +/* Translate a key code into an output key sequence for a pane. */ +int +input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) +{ + log_debug("writing key 0x%llx (%s) to %%%u", key, + key_string_lookup_key(key), wp->id); + + if (KEYC_IS_MOUSE(key)) { + if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) + input_key_mouse(wp, m); + return (0); + } + return (input_key(wp, wp->screen, wp->event, key)); +} + /* Translate a key code into an output key sequence. */ int -input_key(struct window_pane *wp, key_code key, struct mouse_event *m) +input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, + key_code key) { const struct input_key_ent *ike; u_int i; @@ -159,20 +175,14 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) key_code justkey, newkey; struct utf8_data ud; - log_debug("writing key 0x%llx (%s) to %%%u", key, - key_string_lookup_key(key), wp->id); - - /* If this is a mouse key, pass off to mouse function. */ - if (KEYC_IS_MOUSE(key)) { - if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) - input_key_mouse(wp, m); + /* Mouse keys need a pane. */ + if (KEYC_IS_MOUSE(key)) return (0); - } /* Literal keys go as themselves (can't be more than eight bits). */ if (key & KEYC_LITERAL) { ud.data[0] = (u_char)key; - bufferevent_write(wp->event, &ud.data[0], 1); + bufferevent_write(bev, &ud.data[0], 1); return (0); } @@ -191,17 +201,17 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) justkey = (key & ~(KEYC_XTERM|KEYC_ESCAPE)); if (justkey <= 0x7f) { if (key & KEYC_ESCAPE) - bufferevent_write(wp->event, "\033", 1); + bufferevent_write(bev, "\033", 1); ud.data[0] = justkey; - bufferevent_write(wp->event, &ud.data[0], 1); + bufferevent_write(bev, &ud.data[0], 1); return (0); } if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) return (-1); if (key & KEYC_ESCAPE) - bufferevent_write(wp->event, "\033", 1); - bufferevent_write(wp->event, ud.data, ud.size); + bufferevent_write(bev, "\033", 1); + bufferevent_write(bev, ud.data, ud.size); return (0); } @@ -209,9 +219,9 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) * Then try to look this up as an xterm key, if the flag to output them * is set. */ - if (options_get_number(wp->window->options, "xterm-keys")) { + if (wp == NULL || options_get_number(wp->window->options, "xterm-keys")) { if ((out = xterm_keys_lookup(key)) != NULL) { - bufferevent_write(wp->event, out, strlen(out)); + bufferevent_write(bev, out, strlen(out)); free(out); return (0); } @@ -222,11 +232,9 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) for (i = 0; i < nitems(input_keys); i++) { ike = &input_keys[i]; - if ((ike->flags & INPUTKEY_KEYPAD) && - !(wp->screen->mode & MODE_KKEYPAD)) + if ((ike->flags & INPUTKEY_KEYPAD) && (~s->mode & MODE_KKEYPAD)) continue; - if ((ike->flags & INPUTKEY_CURSOR) && - !(wp->screen->mode & MODE_KCURSOR)) + if ((ike->flags & INPUTKEY_CURSOR) && (~s->mode & MODE_KCURSOR)) continue; if ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key) @@ -243,32 +251,27 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) - bufferevent_write(wp->event, "\033", 1); - bufferevent_write(wp->event, ike->data, dlen); + bufferevent_write(bev, "\033", 1); + bufferevent_write(bev, ike->data, dlen); return (0); } -/* Translate mouse and output. */ -static void -input_key_mouse(struct window_pane *wp, struct mouse_event *m) +/* Get mouse event string. */ +int +input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y, + const char **rbuf, size_t *rlen) { - struct screen *s = wp->screen; - int mode = s->mode; - char buf[40]; + static char buf[40]; size_t len; - u_int x, y; - if ((mode & ALL_MOUSE_MODES) == 0) - return; - if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) - return; - if (!window_pane_visible(wp)) - return; + *rbuf = NULL; + *rlen = 0; /* If this pane is not in button or all mode, discard motion events. */ - if (MOUSE_DRAG(m->b) && - (mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL)) == 0) - return; + if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0) + return (0); + if ((s->mode & ALL_MOUSE_MODES) == 0) + return (0); /* * If this event is a release event and not in all mode, discard it. @@ -279,14 +282,14 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) if (m->sgr_type != ' ') { if (MOUSE_DRAG(m->sgr_b) && MOUSE_BUTTONS(m->sgr_b) == 3 && - (~mode & MODE_MOUSE_ALL)) - return; + (~s->mode & MODE_MOUSE_ALL)) + return (0); } else { if (MOUSE_DRAG(m->b) && MOUSE_BUTTONS(m->b) == 3 && MOUSE_BUTTONS(m->lb) == 3 && - (~mode & MODE_MOUSE_ALL)) - return; + (~s->mode & MODE_MOUSE_ALL)) + return (0); } /* @@ -303,19 +306,43 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) m->sgr_b, x + 1, y + 1, m->sgr_type); } else if (s->mode & MODE_MOUSE_UTF8) { if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) - return; + return (0); len = xsnprintf(buf, sizeof buf, "\033[M"); len += input_split2(m->b + 32, &buf[len]); len += input_split2(x + 33, &buf[len]); len += input_split2(y + 33, &buf[len]); } else { if (m->b > 223) - return; + return (0); len = xsnprintf(buf, sizeof buf, "\033[M"); buf[len++] = m->b + 32; buf[len++] = x + 33; buf[len++] = y + 33; } + + *rbuf = buf; + *rlen = len; + return (1); +} + +/* Translate mouse and output. */ +static void +input_key_mouse(struct window_pane *wp, struct mouse_event *m) +{ + struct screen *s = wp->screen; + u_int x, y; + const char *buf; + size_t len; + + /* Ignore events if no mouse mode or the pane is not visible. */ + if (m->ignore || (s->mode & ALL_MOUSE_MODES) == 0) + return; + if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) + return; + if (!window_pane_visible(wp)) + return; + if (!input_key_get_mouse(s, m, x, y, &buf, &len)) + return; log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id); bufferevent_write(wp->event, buf, len); } @@ -75,6 +75,7 @@ struct input_param { /* Input parser context. */ struct input_ctx { struct window_pane *wp; + struct bufferevent *event; struct screen_write_ctx ctx; struct input_cell cell; @@ -128,7 +129,7 @@ struct input_transition; static int input_split(struct input_ctx *); static int input_get(struct input_ctx *, u_int, int, int); static void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...); -static void input_set_state(struct window_pane *, +static void input_set_state(struct input_ctx *, const struct input_transition *); static void input_reset_cell(struct input_ctx *); @@ -253,6 +254,7 @@ enum input_csi_type { INPUT_CSI_TBC, INPUT_CSI_VPA, INPUT_CSI_WINOPS, + INPUT_CSI_XDA, }; /* Control (CSI) command table. */ @@ -289,6 +291,7 @@ static const struct input_table_entry input_csi_table[] = { { 'm', "", INPUT_CSI_SGR }, { 'n', "", INPUT_CSI_DSR }, { 'q', " ", INPUT_CSI_DECSCUSR }, + { 'q', ">", INPUT_CSI_XDA }, { 'r', "", INPUT_CSI_DECSTBM }, { 's', "", INPUT_CSI_SCP }, { 't', "", INPUT_CSI_WINOPS }, @@ -731,10 +734,9 @@ static void input_timer_callback(__unused int fd, __unused short events, void *arg) { struct input_ctx *ictx = arg; - struct window_pane *wp = ictx->wp; - log_debug("%s: %%%u %s expired" , __func__, wp->id, ictx->state->name); - input_reset(wp, 0); + log_debug("%s: %s expired" , __func__, ictx->state->name); + input_reset(ictx, 0); } /* Start the timer. */ @@ -788,12 +790,14 @@ input_restore_state(struct input_ctx *ictx) } /* Initialise input parser. */ -void -input_init(struct window_pane *wp) +struct input_ctx * +input_init(struct window_pane *wp, struct bufferevent *bev) { struct input_ctx *ictx; - ictx = wp->ictx = xcalloc(1, sizeof *ictx); + ictx = xcalloc(1, sizeof *ictx); + ictx->wp = wp; + ictx->event = bev; ictx->input_space = INPUT_BUF_START; ictx->input_buf = xmalloc(INPUT_BUF_START); @@ -804,15 +808,15 @@ input_init(struct window_pane *wp) evtimer_set(&ictx->timer, input_timer_callback, ictx); - input_reset(wp, 0); + input_reset(ictx, 0); + return (ictx); } /* Destroy input parser. */ void -input_free(struct window_pane *wp) +input_free(struct input_ctx *ictx) { - struct input_ctx *ictx = wp->ictx; - u_int i; + u_int i; for (i = 0; i < ictx->param_list_len; i++) { if (ictx->param_list[i].type == INPUT_STRING) @@ -825,19 +829,18 @@ input_free(struct window_pane *wp) evbuffer_free(ictx->since_ground); free(ictx); - wp->ictx = NULL; } /* Reset input state and clear screen. */ void -input_reset(struct window_pane *wp, int clear) +input_reset(struct input_ctx *ictx, int clear) { - struct input_ctx *ictx = wp->ictx; struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; input_reset_cell(ictx); - if (clear) { + if (clear && wp != NULL) { if (TAILQ_EMPTY(&wp->modes)) screen_write_start(sctx, wp, &wp->base); else @@ -856,17 +859,15 @@ input_reset(struct window_pane *wp, int clear) /* Return pending data. */ struct evbuffer * -input_pending(struct window_pane *wp) +input_pending(struct input_ctx *ictx) { - return (wp->ictx->since_ground); + return (ictx->since_ground); } /* Change input state. */ static void -input_set_state(struct window_pane *wp, const struct input_transition *itr) +input_set_state(struct input_ctx *ictx, const struct input_transition *itr) { - struct input_ctx *ictx = wp->ictx; - if (ictx->state->exit != NULL) ictx->state->exit(ictx); ictx->state = itr->state; @@ -874,46 +875,15 @@ input_set_state(struct window_pane *wp, const struct input_transition *itr) ictx->state->enter(ictx); } -/* Parse input. */ -void -input_parse(struct window_pane *wp) -{ - struct evbuffer *evb = wp->event->input; - - input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb)); - evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); -} - -/* Parse given input. */ -void -input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) +/* Parse data. */ +static void +input_parse(struct input_ctx *ictx, u_char *buf, size_t len) { - struct input_ctx *ictx = wp->ictx; struct screen_write_ctx *sctx = &ictx->ctx; const struct input_state *state = NULL; const struct input_transition *itr = NULL; size_t off = 0; - if (len == 0) - return; - - window_update_activity(wp->window); - wp->flags |= PANE_CHANGED; - notify_input(wp, buf, len); - - /* - * Open the screen. Use NULL wp if there is a mode set as don't want to - * update the tty. - */ - if (TAILQ_EMPTY(&wp->modes)) - screen_write_start(sctx, wp, &wp->base); - else - screen_write_start(sctx, NULL, &wp->base); - ictx->wp = wp; - - log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, - ictx->state->name, len, (int)len, buf); - /* Parse the input. */ while (off < len) { ictx->ch = buf[off++]; @@ -956,14 +926,63 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) /* And switch state, if necessary. */ if (itr->state != NULL) - input_set_state(wp, itr); + input_set_state(ictx, itr); /* If not in ground state, save input. */ if (ictx->state != &input_state_ground) evbuffer_add(ictx->since_ground, &ictx->ch, 1); } +} + +/* Parse input from pane. */ +void +input_parse_pane(struct window_pane *wp) +{ + struct evbuffer *evb = wp->event->input; + + input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb)); + evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); +} + +/* Parse given input. */ +void +input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) +{ + struct input_ctx *ictx = wp->ictx; + struct screen_write_ctx *sctx = &ictx->ctx; + + if (len == 0) + return; + + window_update_activity(wp->window); + wp->flags |= PANE_CHANGED; + notify_input(wp, buf, len); - /* Close the screen. */ + /* NULL wp if there is a mode set as don't want to update the tty. */ + if (TAILQ_EMPTY(&wp->modes)) + screen_write_start(sctx, wp, &wp->base); + else + screen_write_start(sctx, NULL, &wp->base); + + log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, + ictx->state->name, len, (int)len, buf); + + input_parse(ictx, buf, len); + screen_write_stop(sctx); +} + +/* Parse given input for screen. */ +void +input_parse_screen(struct input_ctx *ictx, struct screen *s, u_char *buf, + size_t len) +{ + struct screen_write_ctx *sctx = &ictx->ctx; + + if (len == 0) + return; + + screen_write_start(sctx, NULL, s); + input_parse(ictx, buf, len); screen_write_stop(sctx); } @@ -1043,14 +1062,15 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) static void input_reply(struct input_ctx *ictx, const char *fmt, ...) { - va_list ap; - char *reply; + struct bufferevent *bev = ictx->event; + va_list ap; + char *reply; va_start(ap, fmt); xvasprintf(&reply, fmt, ap); va_end(ap); - bufferevent_write(ictx->wp->event, reply, strlen(reply)); + bufferevent_write(bev, reply, strlen(reply)); free(reply); } @@ -1177,7 +1197,8 @@ input_c0_dispatch(struct input_ctx *ictx) case '\000': /* NUL */ break; case '\007': /* BEL */ - alerts_queue(wp->window, WINDOW_BELL); + if (wp != NULL) + alerts_queue(wp->window, WINDOW_BELL); break; case '\010': /* BS */ screen_write_backspace(sctx); @@ -1224,6 +1245,7 @@ static int input_esc_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; struct input_table_entry *entry; @@ -1240,7 +1262,8 @@ input_esc_dispatch(struct input_ctx *ictx) switch (entry->type) { case INPUT_ESC_RIS: - window_pane_reset_palette(ictx->wp); + if (wp != NULL) + window_pane_reset_palette(wp); input_reset_cell(ictx); screen_write_reset(sctx); break; @@ -1303,7 +1326,6 @@ input_csi_dispatch(struct input_ctx *ictx) struct input_table_entry *entry; int i, n, m; u_int cx, bg = ictx->cell.cell.bg; - char *copy, *cp; if (ictx->flags & INPUT_DISCARD) return (0); @@ -1435,13 +1457,6 @@ input_csi_dispatch(struct input_ctx *ictx) case 6: input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); break; - case 1337: /* Terminal version, from iTerm2. */ - copy = xstrdup(getversion()); - for (cp = copy; *cp != '\0'; cp++) - *cp = toupper((u_char)*cp); - input_reply(ictx, "\033[TMUX %sn", copy); - free(copy); - break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; @@ -1576,6 +1591,10 @@ input_csi_dispatch(struct input_ctx *ictx) if (n != -1) screen_set_cursor_style(s, n); break; + case INPUT_CSI_XDA: + input_reply(ictx, "\033P>|tmux %s\033\\", getversion()); + break; + } ictx->last = -1; @@ -1612,6 +1631,7 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; + struct grid_cell *gc = &ictx->cell.cell; u_int i; for (i = 0; i < ictx->param_list_len; i++) { @@ -1623,7 +1643,7 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) break; case 3: /* DECCOLM */ screen_write_cursormove(sctx, 0, 0, 1); - screen_write_clearscreen(sctx, ictx->cell.cell.bg); + screen_write_clearscreen(sctx, gc->bg); break; case 6: /* DECOM */ screen_write_mode_clear(sctx, MODE_ORIGIN); @@ -1655,10 +1675,16 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_off(wp, &ictx->cell.cell, 0); + if (wp != NULL) + window_pane_alternate_off(wp, gc, 0); + else + screen_alternate_off(sctx->s, gc, 0); break; case 1049: - window_pane_alternate_off(wp, &ictx->cell.cell, 1); + if (wp != NULL) + window_pane_alternate_off(wp, gc, 1); + else + screen_alternate_off(sctx->s, gc, 1); break; case 2004: screen_write_mode_clear(sctx, MODE_BRACKETPASTE); @@ -1700,6 +1726,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; + struct grid_cell *gc = &ictx->cell.cell; u_int i; for (i = 0; i < ictx->param_list_len; i++) { @@ -1742,7 +1769,8 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) if (sctx->s->mode & MODE_FOCUSON) break; screen_write_mode_set(sctx, MODE_FOCUSON); - wp->flags |= PANE_FOCUSPUSH; /* force update */ + if (wp != NULL) + wp->flags |= PANE_FOCUSPUSH; /* force update */ break; case 1005: screen_write_mode_set(sctx, MODE_MOUSE_UTF8); @@ -1752,10 +1780,16 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_on(wp, &ictx->cell.cell, 0); + if (wp != NULL) + window_pane_alternate_on(wp, gc, 0); + else + screen_alternate_on(sctx->s, gc, 0); break; case 1049: - window_pane_alternate_on(wp, &ictx->cell.cell, 1); + if (wp != NULL) + window_pane_alternate_on(wp, gc, 1); + else + screen_alternate_on(sctx->s, gc, 1); break; case 2004: screen_write_mode_set(sctx, MODE_BRACKETPASTE); @@ -1772,7 +1806,9 @@ static void input_csi_dispatch_winops(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; + struct screen *s = sctx->s; struct window_pane *wp = ictx->wp; + u_int x = screen_size_x(s), y = screen_size_y(s); int n, m; m = 0; @@ -1823,12 +1859,13 @@ input_csi_dispatch_winops(struct input_ctx *ictx) case 0: case 2: screen_pop_title(sctx->s); - server_status_window(ictx->wp->window); + if (wp != NULL) + server_status_window(wp->window); break; } break; case 18: - input_reply(ictx, "\033[8;%u;%ut", wp->sy, wp->sx); + input_reply(ictx, "\033[8;%u;%ut", x, y); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -2193,6 +2230,7 @@ static void input_exit_osc(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; u_char *p = ictx->input_buf; u_int option; @@ -2213,7 +2251,7 @@ input_exit_osc(struct input_ctx *ictx) switch (option) { case 0: case 2: - if (screen_set_title(sctx->s, p)) + if (screen_set_title(sctx->s, p) && wp != NULL) server_status_window(ictx->wp->window); break; case 4: @@ -2222,7 +2260,8 @@ input_exit_osc(struct input_ctx *ictx) case 7: if (utf8_isvalid(p)) { screen_set_path(sctx->s, p); - server_status_window(ictx->wp->window); + if (wp != NULL) + server_status_window(wp->window); } break; case 10: @@ -2267,13 +2306,14 @@ static void input_exit_apc(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; if (ictx->flags & INPUT_DISCARD) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); - if (screen_set_title(sctx->s, ictx->input_buf)) - server_status_window(ictx->wp->window); + if (screen_set_title(sctx->s, ictx->input_buf) && wp != NULL) + server_status_window(wp->window); } /* Rename string started. */ @@ -2294,6 +2334,8 @@ input_exit_rename(struct input_ctx *ictx) struct window_pane *wp = ictx->wp; struct options_entry *oe; + if (wp == NULL) + return; if (ictx->flags & INPUT_DISCARD) return; if (!options_get_number(ictx->wp->options, "allow-rename")) @@ -2309,9 +2351,9 @@ input_exit_rename(struct input_ctx *ictx) options_remove(oe); return; } - window_set_name(ictx->wp->window, ictx->input_buf); - options_set_number(ictx->wp->window->options, "automatic-rename", 0); - server_status_window(ictx->wp->window); + window_set_name(wp->window, ictx->input_buf); + options_set_number(wp->window->options, "automatic-rename", 0); + server_status_window(wp->window); } /* Open UTF-8 character. */ @@ -2407,6 +2449,9 @@ input_osc_4(struct input_ctx *ictx, const char *p) long idx; u_int r, g, b; + if (wp == NULL) + return; + copy = s = xstrdup(p); while (s != NULL && *s != '\0') { idx = strtol(s, &next, 10); @@ -2441,6 +2486,8 @@ input_osc_10(struct input_ctx *ictx, const char *p) u_int r, g, b; char tmp[16]; + if (wp == NULL) + return; if (strcmp(p, "?") == 0) return; @@ -2465,6 +2512,8 @@ input_osc_11(struct input_ctx *ictx, const char *p) u_int r, g, b; char tmp[16]; + if (wp == NULL) + return; if (strcmp(p, "?") == 0) return; @@ -2494,6 +2543,8 @@ input_osc_52(struct input_ctx *ictx, const char *p) struct screen_write_ctx ctx; struct paste_buffer *pb; + if (wp == NULL) + return; state = options_get_number(global_options, "set-clipboard"); if (state != 2) return; @@ -2518,13 +2569,13 @@ input_osc_52(struct input_ctx *ictx, const char *p) outlen = 0; out = NULL; } - bufferevent_write(wp->event, "\033]52;;", 6); + bufferevent_write(ictx->event, "\033]52;;", 6); if (outlen != 0) - bufferevent_write(wp->event, out, outlen); + bufferevent_write(ictx->event, out, outlen); if (ictx->input_end == INPUT_END_BEL) - bufferevent_write(wp->event, "\007", 1); + bufferevent_write(ictx->event, "\007", 1); else - bufferevent_write(wp->event, "\033\\", 2); + bufferevent_write(ictx->event, "\033\\", 2); free(out); return; } @@ -2555,6 +2606,9 @@ input_osc_104(struct input_ctx *ictx, const char *p) char *copy, *s; long idx; + if (wp == NULL) + return; + if (*p == '\0') { window_pane_reset_palette(wp); return; @@ -17,6 +17,7 @@ */ #include <sys/types.h> +#include <sys/ioctl.h> #include <sys/socket.h> #include <fcntl.h> @@ -68,18 +69,15 @@ static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs); struct job * job_run(const char *cmd, struct session *s, const char *cwd, job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, - void *data, int flags) + void *data, int flags, int sx, int sy) { struct job *job; struct environ *env; pid_t pid; - int nullfd, out[2]; + int nullfd, out[2], master; const char *home; sigset_t set, oldset; - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) - return (NULL); - log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd); + struct winsize ws; /* * Do not set TERM during .tmux.conf, it is nice to be able to use @@ -89,13 +87,26 @@ job_run(const char *cmd, struct session *s, const char *cwd, sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); - switch (pid = fork()) { + + if (flags & JOB_PTY) { + memset(&ws, 0, sizeof ws); + ws.ws_col = sx; + ws.ws_row = sy; + pid = fdforkpty(ptm_fd, &master, NULL, NULL, &ws); + } else { + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) + goto fail; + pid = fork(); + } + log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd); + + switch (pid) { case -1: - sigprocmask(SIG_SETMASK, &oldset, NULL); - environ_free(env); - close(out[0]); - close(out[1]); - return (NULL); + if (~flags & JOB_PTY) { + close(out[0]); + close(out[1]); + } + goto fail; case 0: proc_clear_signals(server_proc, 1); sigprocmask(SIG_SETMASK, &oldset, NULL); @@ -108,22 +119,23 @@ job_run(const char *cmd, struct session *s, const char *cwd, 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] != STDIN_FILENO && out[1] != STDOUT_FILENO) - close(out[1]); - close(out[0]); - - nullfd = open(_PATH_DEVNULL, O_RDWR, 0); - if (nullfd == -1) - fatal("open failed"); - if (dup2(nullfd, STDERR_FILENO) == -1) - fatal("dup2 failed"); - if (nullfd != STDERR_FILENO) - close(nullfd); - + if (~flags & JOB_PTY) { + if (dup2(out[1], STDIN_FILENO) == -1) + fatal("dup2 failed"); + if (dup2(out[1], STDOUT_FILENO) == -1) + fatal("dup2 failed"); + if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) + close(out[1]); + close(out[0]); + + nullfd = open(_PATH_DEVNULL, O_RDWR, 0); + if (nullfd == -1) + fatal("open failed"); + if (dup2(nullfd, STDERR_FILENO) == -1) + fatal("dup2 failed"); + if (nullfd != STDERR_FILENO) + close(nullfd); + } closefrom(STDERR_FILENO + 1); execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); @@ -132,7 +144,6 @@ job_run(const char *cmd, struct session *s, const char *cwd, sigprocmask(SIG_SETMASK, &oldset, NULL); environ_free(env); - close(out[1]); job = xmalloc(sizeof *job); job->state = JOB_RUNNING; @@ -149,7 +160,11 @@ job_run(const char *cmd, struct session *s, const char *cwd, job->freecb = freecb; job->data = data; - job->fd = out[0]; + if (~flags & JOB_PTY) { + close(out[1]); + job->fd = out[0]; + } else + job->fd = master; setblocking(job->fd, 0); job->event = bufferevent_new(job->fd, job_read_callback, @@ -160,6 +175,11 @@ job_run(const char *cmd, struct session *s, const char *cwd, log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); return (job); + +fail: + sigprocmask(SIG_SETMASK, &oldset, NULL); + environ_free(env); + return (NULL); } /* Kill and free an individual job. */ @@ -184,6 +204,24 @@ job_free(struct job *job) free(job); } +/* Resize job. */ +void +job_resize(struct job *job, u_int sx, u_int sy) +{ + struct winsize ws; + + if (job->fd == -1 || (~job->flags & JOB_PTY)) + return; + + log_debug("resize job %p: %ux%u", job, sx, sy); + + memset(&ws, 0, sizeof ws); + ws.ws_col = sx; + ws.ws_row = sy; + if (ioctl(job->fd, TIOCSWINSZ, &ws) == -1) + fatal("ioctl failed"); +} + /* Job buffer read callback. */ static void job_read_callback(__unused struct bufferevent *bufev, void *data) @@ -208,7 +246,7 @@ job_write_callback(__unused struct bufferevent *bufev, void *data) log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, (long) job->pid, len); - if (len == 0) { + if (len == 0 && (~job->flags & JOB_KEEPWRITE)) { shutdown(job->fd, SHUT_WR); bufferevent_disable(job->event, EV_WRITE); } diff --git a/key-bindings.c b/key-bindings.c index 4387c011..09a4eafa 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -34,8 +34,8 @@ " 'New Session' 's' {new-session}" \ " 'New Window' 'w' {new-window}" #define DEFAULT_WINDOW_MENU \ - " 'Swap Left' 'l' {swap-window -t:-1}" \ - " 'Swap Right' 'r' {swap-window -t:+1}" \ + " '#{?#{>:#{session_windows},1},,-}Swap Left' 'l' {swap-window -t:-1}" \ + " '#{?#{>:#{session_windows},1},,-}Swap Right' 'r' {swap-window -t:+1}" \ " '#{?pane_marked_set,,-}Swap Marked' 's' {swap-window}" \ " ''" \ " 'Kill' 'X' {kill-window}" \ @@ -46,22 +46,25 @@ " 'New After' 'w' {new-window -a}" \ " 'New At End' 'W' {new-window}" #define DEFAULT_PANE_MENU \ - " '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {copy-mode -t=; send -Xt= search-backward \"#{q:mouse_word}\"}" \ - " '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {send-keys -l -- \"#{q:mouse_word}\"}" \ - " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {set-buffer -- \"#{q:mouse_word}\"}" \ - " '#{?mouse_line,Copy Line,}' 'l' {set-buffer -- \"#{q:mouse_line}\"}" \ + " '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \ + " '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \ + " ''" \ + " '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {if -F '#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}' 'copy-mode -t='; send -Xt= search-backward \"#{q:mouse_word}\"}" \ + " '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {copy-mode -q; send-keys -l -- \"#{q:mouse_word}\"}" \ + " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \ + " '#{?mouse_line,Copy Line,}' 'l' {copy-mode -q; set-buffer -- \"#{q:mouse_line}\"}" \ " ''" \ " 'Horizontal Split' 'h' {split-window -h}" \ " 'Vertical Split' 'v' {split-window -v}" \ " ''" \ - " 'Swap Up' 'u' {swap-pane -U}" \ - " 'Swap Down' 'd' {swap-pane -D}" \ + " '#{?#{>:#{window_panes},1},,-}Swap Up' 'u' {swap-pane -U}" \ + " '#{?#{>:#{window_panes},1},,-}Swap Down' 'd' {swap-pane -D}" \ " '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \ " ''" \ " 'Kill' 'X' {kill-pane}" \ " 'Respawn' 'R' {respawn-pane -k}" \ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ - " '#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}" + " '#{?#{>:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}" static int key_bindings_cmp(struct key_binding *, struct key_binding *); RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp); @@ -229,6 +232,7 @@ void key_bindings_init(void) { static const char *defaults[] = { + /* Prefix keys. */ "bind -N 'Send the prefix key' C-b send-prefix", "bind -N 'Rotate through the panes' C-o rotate-window", "bind -N 'Suspend the current client' C-z suspend-client", @@ -312,21 +316,51 @@ key_bindings_init(void) "bind -N 'Resize the pane left' -r C-Left resize-pane -L", "bind -N 'Resize the pane right' -r C-Right resize-pane -R", - "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", + /* Menu keys */ + "bind < display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, + "bind > display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU, + + /* Mouse button 1 down on pane. */ + "bind -n MouseDown1Pane select-pane -t=\\; send -M", + + /* Mouse button 1 drag on pane. */ + "bind -n MouseDrag1Pane if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M }", + + /* Mouse wheel up on pane. */ + "bind -n WheelUpPane if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -e }", + + /* Mouse button 2 down on pane. */ + "bind -n MouseDown2Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { paste -p }", + + /* Mouse button 1 double click on pane. */ + "bind -n DoubleClick1Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-word; run -d0.3; send -X copy-pipe-and-cancel }", + + /* Mouse button 1 triple click on pane. */ + "bind -n TripleClick1Pane select-pane -t=\\; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel }", + + /* Mouse button 1 drag on border. */ "bind -n MouseDrag1Border resize-pane -M", + + /* Mouse button 1 down on status line. */ "bind -n MouseDown1Status select-window -t=", + + /* Mouse wheel down on status line. */ "bind -n WheelDownStatus next-window", + + /* Mouse wheel up on status line. */ "bind -n WheelUpStatus previous-window", - "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", - "bind -n WheelUpPane if -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", - "bind -n MouseDown3StatusLeft display-menu -t= -xM -yS -T \"#[align=centre]#{session_name}\" " DEFAULT_SESSION_MENU, - "bind -n MouseDown3Status display-menu -t= -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU, - "bind < display-menu -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU, - "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{pane_in_mode}}' 'select-pane -t=; send-keys -M' {display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU "}", - "bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU, - "bind > display-menu -xP -yP -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU, + /* Mouse button 3 down on status left. */ + "bind -n MouseDown3StatusLeft display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU, + + /* Mouse button 3 down on status line. */ + "bind -n MouseDown3Status display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU, + + /* Mouse button 3 down on pane. */ + "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", + "bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU, + /* Copy mode (emacs) keys. */ "bind -Tcopy-mode C-Space send -X begin-selection", "bind -Tcopy-mode C-a send -X start-of-line", "bind -Tcopy-mode C-c send -X cancel", @@ -340,7 +374,7 @@ key_bindings_init(void) "bind -Tcopy-mode C-r command-prompt -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", "bind -Tcopy-mode C-s command-prompt -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'", "bind -Tcopy-mode C-v send -X page-down", - "bind -Tcopy-mode C-w send -X copy-selection-and-cancel", + "bind -Tcopy-mode C-w send -X copy-pipe-and-cancel", "bind -Tcopy-mode Escape send -X cancel", "bind -Tcopy-mode Space send -X page-down", "bind -Tcopy-mode , send -X jump-reverse", @@ -353,16 +387,17 @@ key_bindings_init(void) "bind -Tcopy-mode g command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode n send -X search-again", "bind -Tcopy-mode q send -X cancel", + "bind -Tcopy-mode r send -X refresh-from-pane", "bind -Tcopy-mode t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'", "bind -Tcopy-mode Home send -X start-of-line", "bind -Tcopy-mode End send -X end-of-line", "bind -Tcopy-mode MouseDown1Pane select-pane", "bind -Tcopy-mode MouseDrag1Pane select-pane\\; send -X begin-selection", - "bind -Tcopy-mode MouseDragEnd1Pane send -X copy-selection-and-cancel", + "bind -Tcopy-mode MouseDragEnd1Pane send -X copy-pipe-and-cancel", "bind -Tcopy-mode WheelUpPane select-pane\\; send -N5 -X scroll-up", "bind -Tcopy-mode WheelDownPane select-pane\\; send -N5 -X scroll-down", - "bind -Tcopy-mode DoubleClick1Pane select-pane\\; send -X select-word", - "bind -Tcopy-mode TripleClick1Pane select-pane\\; send -X select-line", + "bind -Tcopy-mode DoubleClick1Pane select-pane\\; send -X select-word\\; run -d0.3\\; send -X copy-pipe-and-cancel", + "bind -Tcopy-mode TripleClick1Pane select-pane\\; send -X select-line\\; run -d0.3\\; send -X copy-pipe-and-cancel", "bind -Tcopy-mode NPage send -X page-down", "bind -Tcopy-mode PPage send -X page-up", "bind -Tcopy-mode Up send -X cursor-up", @@ -388,7 +423,7 @@ key_bindings_init(void) "bind -Tcopy-mode M-m send -X back-to-indentation", "bind -Tcopy-mode M-r send -X middle-line", "bind -Tcopy-mode M-v send -X page-up", - "bind -Tcopy-mode M-w send -X copy-selection-and-cancel", + "bind -Tcopy-mode M-w send -X copy-pipe-and-cancel", "bind -Tcopy-mode 'M-{' send -X previous-paragraph", "bind -Tcopy-mode 'M-}' send -X next-paragraph", "bind -Tcopy-mode M-Up send -X halfpage-up", @@ -396,6 +431,7 @@ key_bindings_init(void) "bind -Tcopy-mode C-Up send -X scroll-up", "bind -Tcopy-mode C-Down send -X scroll-down", + /* Copy mode (vi) keys. */ "bind -Tcopy-mode-vi '#' send -FX search-backward '#{copy_cursor_word}'", "bind -Tcopy-mode-vi * send -FX search-forward '#{copy_cursor_word}'", "bind -Tcopy-mode-vi C-c send -X cancel", @@ -404,8 +440,8 @@ key_bindings_init(void) "bind -Tcopy-mode-vi C-b send -X page-up", "bind -Tcopy-mode-vi C-f send -X page-down", "bind -Tcopy-mode-vi C-h send -X cursor-left", - "bind -Tcopy-mode-vi C-j send -X copy-selection-and-cancel", - "bind -Tcopy-mode-vi Enter send -X copy-selection-and-cancel", + "bind -Tcopy-mode-vi C-j send -X copy-pipe-and-cancel", + "bind -Tcopy-mode-vi Enter send -X copy-pipe-and-cancel", "bind -Tcopy-mode-vi C-u send -X halfpage-up", "bind -Tcopy-mode-vi C-v send -X rectangle-toggle", "bind -Tcopy-mode-vi C-y send -X scroll-up", @@ -454,6 +490,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi n send -X search-again", "bind -Tcopy-mode-vi o send -X other-end", "bind -Tcopy-mode-vi q send -X cancel", + "bind -Tcopy-mode-vi r send -X refresh-from-pane", "bind -Tcopy-mode-vi t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'", "bind -Tcopy-mode-vi v send -X rectangle-toggle", "bind -Tcopy-mode-vi w send -X next-word", @@ -462,11 +499,11 @@ key_bindings_init(void) "bind -Tcopy-mode-vi % send -X next-matching-bracket", "bind -Tcopy-mode-vi MouseDown1Pane select-pane", "bind -Tcopy-mode-vi MouseDrag1Pane select-pane\\; send -X begin-selection", - "bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel", + "bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-pipe-and-cancel", "bind -Tcopy-mode-vi WheelUpPane select-pane\\; send -N5 -X scroll-up", "bind -Tcopy-mode-vi WheelDownPane select-pane\\; send -N5 -X scroll-down", - "bind -Tcopy-mode-vi DoubleClick1Pane select-pane\\; send -X select-word", - "bind -Tcopy-mode-vi TripleClick1Pane select-pane\\; send -X select-line", + "bind -Tcopy-mode-vi DoubleClick1Pane select-pane\\; send -X select-word\\; run -d0.3\\; send -X copy-pipe-and-cancel", + "bind -Tcopy-mode-vi TripleClick1Pane select-pane\\; send -X select-line\\; run -d0.3\\; send -X copy-pipe-and-cancel", "bind -Tcopy-mode-vi BSpace send -X cursor-left", "bind -Tcopy-mode-vi NPage send -X page-down", "bind -Tcopy-mode-vi PPage send -X page-up", @@ -484,7 +521,7 @@ key_bindings_init(void) pr = cmd_parse_from_string(defaults[i], NULL); if (pr->status != CMD_PARSE_SUCCESS) fatalx("bad default key: %s", defaults[i]); - cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL, NULL, 0)); + cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL)); cmd_list_free(pr->cmdlist); } } @@ -498,27 +535,24 @@ key_bindings_read_only(struct cmdq_item *item, __unused void *data) struct cmdq_item * key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, - struct client *c, struct mouse_event *m, struct cmd_find_state *fs) + struct client *c, struct key_event *event, struct cmd_find_state *fs) { - struct cmd *cmd; struct cmdq_item *new_item; - int readonly; + struct cmdq_state *new_state; + int readonly, flags = 0; if (c == NULL || (~c->flags & CLIENT_READONLY)) readonly = 1; - else { - readonly = 1; - TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { - if (~cmd->entry->flags & CMD_READONLY) - readonly = 0; - } - } + else + readonly = cmd_list_all_have(bd->cmdlist, CMD_READONLY); if (!readonly) new_item = cmdq_get_callback(key_bindings_read_only, NULL); else { - new_item = cmdq_get_command(bd->cmdlist, fs, m, 0); if (bd->flags & KEY_BINDING_REPEAT) - new_item->shared->flags |= CMDQ_SHARED_REPEAT; + flags |= CMDQ_STATE_REPEAT; + new_state = cmdq_new_state(fs, event, flags); + new_item = cmdq_get_command(bd->cmdlist, new_state); + cmdq_free_state(new_state); } if (item != NULL) new_item = cmdq_insert_after(item, new_item); diff --git a/key-string.c b/key-string.c index 38e5b8a7..2a0602b2 100644 --- a/key-string.c +++ b/key-string.c @@ -100,6 +100,9 @@ static const struct { KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3), KEYC_MOUSE_STRING(WHEELUP, WheelUp), KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), + KEYC_MOUSE_STRING(SECONDCLICK1, SecondClick1), + KEYC_MOUSE_STRING(SECONDCLICK2, SecondClick2), + KEYC_MOUSE_STRING(SECONDCLICK3, SecondClick3), KEYC_MOUSE_STRING(DOUBLECLICK1, DoubleClick1), KEYC_MOUSE_STRING(DOUBLECLICK2, DoubleClick2), KEYC_MOUSE_STRING(DOUBLECLICK3, DoubleClick3), @@ -226,10 +229,8 @@ key_string_lookup_string(const char *string) key -= 64; else if (key == 32) key = 0; - else if (key == '?') - key = 127; else if (key == 63) - key = KEYC_BSPACE; + key = 127; else return (KEYC_UNKNOWN); modifiers &= ~KEYC_CTRL; diff --git a/layout-set.c b/layout-set.c index f712b059..9ef28416 100644 --- a/layout-set.c +++ b/layout-set.c @@ -18,6 +18,7 @@ #include <sys/types.h> +#include <stdlib.h> #include <string.h> #include "tmux.h" @@ -186,6 +187,8 @@ layout_set_main_h(struct window *w) struct window_pane *wp; struct layout_cell *lc, *lcmain, *lcother, *lcchild; u_int n, mainh, otherh, sx, sy; + char *cause; + const char *s; layout_print_cell(w->layout_root, __func__, 1); @@ -198,8 +201,15 @@ layout_set_main_h(struct window *w) /* Find available height - take off one line for the border. */ sy = w->sy - 1; - /* Get the main pane height and work out the other pane height. */ - mainh = options_get_number(w->options, "main-pane-height"); + /* Get the main pane height. */ + s = options_get_string(w->options, "main-pane-height"); + mainh = args_string_percentage(s, 0, sy, sy, &cause); + if (cause != NULL) { + mainh = 24; + free(cause); + } + + /* Work out the other pane height. */ if (mainh + PANE_MINIMUM >= sy) { if (sy <= PANE_MINIMUM + PANE_MINIMUM) mainh = PANE_MINIMUM; @@ -207,10 +217,12 @@ layout_set_main_h(struct window *w) mainh = sy - PANE_MINIMUM; otherh = PANE_MINIMUM; } else { - otherh = options_get_number(w->options, "other-pane-height"); - if (otherh == 0) + s = options_get_string(w->options, "other-pane-height"); + otherh = args_string_percentage(s, 0, sy, sy, &cause); + if (cause != NULL || otherh == 0) { otherh = sy - mainh; - else if (otherh > sy || sy - otherh < mainh) + free(cause); + } else if (otherh > sy || sy - otherh < mainh) otherh = sy - mainh; else mainh = sy - otherh; @@ -273,6 +285,8 @@ layout_set_main_v(struct window *w) struct window_pane *wp; struct layout_cell *lc, *lcmain, *lcother, *lcchild; u_int n, mainw, otherw, sx, sy; + char *cause; + const char *s; layout_print_cell(w->layout_root, __func__, 1); @@ -285,8 +299,15 @@ layout_set_main_v(struct window *w) /* Find available width - take off one line for the border. */ sx = w->sx - 1; - /* Get the main pane width and work out the other pane width. */ - mainw = options_get_number(w->options, "main-pane-width"); + /* Get the main pane width. */ + s = options_get_string(w->options, "main-pane-width"); + mainw = args_string_percentage(s, 0, sx, sx, &cause); + if (cause != NULL) { + mainw = 80; + free(cause); + } + + /* Work out the other pane width. */ if (mainw + PANE_MINIMUM >= sx) { if (sx <= PANE_MINIMUM + PANE_MINIMUM) mainw = PANE_MINIMUM; @@ -294,10 +315,12 @@ layout_set_main_v(struct window *w) mainw = sx - PANE_MINIMUM; otherw = PANE_MINIMUM; } else { - otherw = options_get_number(w->options, "other-pane-width"); - if (otherw == 0) + s = options_get_string(w->options, "other-pane-width"); + otherw = args_string_percentage(s, 0, sx, sx, &cause); + if (cause != NULL || otherw == 0) { otherw = sx - mainw; - else if (otherw > sx || sx - otherw < mainw) + free(cause); + } else if (otherw > sx || sx - otherw < mainw) otherw = sx - mainw; else mainw = sx - otherw; @@ -130,6 +130,16 @@ menu_free(struct menu *menu) free(menu); } +static int +menu_mode_cb(struct client *c, __unused u_int *cx, __unused u_int *cy) +{ + struct menu_data *md = c->overlay_data; + + if (~md->flags & MENU_NOMOUSE) + return (MODE_MOUSE_ALL); + return (0); +} + static void menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) { @@ -138,21 +148,19 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) struct screen *s = &md->s; struct menu *menu = md->menu; struct screen_write_ctx ctx; - u_int i, px, py; + u_int i, px = md->px, py = md->py; + struct grid_cell gc; + + memcpy(&gc, &grid_default_cell, sizeof gc); + style_apply(&gc, c->session->curw->window->options, "mode-style"); screen_write_start(&ctx, NULL, s); screen_write_clearscreen(&ctx, 8); - screen_write_menu(&ctx, menu, md->choice); + screen_write_menu(&ctx, menu, md->choice, &gc); screen_write_stop(&ctx); - px = md->px; - py = md->py; - for (i = 0; i < screen_size_y(&md->s); i++) tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i); - - if (~md->flags & MENU_NOMOUSE) - tty_update_mode(tty, MODE_MOUSE_ALL, NULL); } static void @@ -179,10 +187,11 @@ menu_key_cb(struct client *c, struct key_event *event) struct mouse_event *m = &event->m; u_int i; int count = menu->count, old = md->choice; - const struct menu_item *item; - struct cmdq_item *new_item; - struct cmd_parse_result *pr; const char *name; + const struct menu_item *item; + struct cmdq_state *state; + enum cmd_parse_status status; + char *error; if (KEYC_IS_MOUSE(event->key)) { if (md->flags & MENU_NOMOUSE) { @@ -267,26 +276,19 @@ chosen: return (1); } - pr = cmd_parse_from_string(item->command, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - new_item = NULL; - break; - case CMD_PARSE_ERROR: - new_item = cmdq_get_error(pr->error); - free(pr->error); - cmdq_append(c, new_item); - break; - case CMD_PARSE_SUCCESS: - if (md->item != NULL) - m = &md->item->shared->mouse; - else - m = NULL; - new_item = cmdq_get_command(pr->cmdlist, &md->fs, m, 0); - cmd_list_free(pr->cmdlist); - cmdq_append(c, new_item); - break; + if (md->item != NULL) + event = cmdq_get_event(md->item); + else + event = NULL; + state = cmdq_new_state(&md->fs, event, 0); + + status = cmd_parse_and_append(item->command, NULL, c, state, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_append(c, cmdq_get_error(error)); + free(error); } + cmdq_free_state(state); + return (1); } @@ -296,9 +298,15 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, void *data) { struct menu_data *md; + u_int i; + const char *name; if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) return (-1); + if (px + menu->width + 4 > c->tty.sx) + px = c->tty.sx - menu->width - 4; + if (py + menu->count + 2 > c->tty.sy) + py = c->tty.sy - menu->count - 2; md = xcalloc(1, sizeof *md); md->item = item; @@ -312,12 +320,23 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, md->py = py; md->menu = menu; - md->choice = -1; + if (md->flags & MENU_NOMOUSE) { + for (i = 0; i < menu->count; i++) { + name = menu->items[i].name; + if (name != NULL && *name != '-') + break; + } + if (i != menu->count) + md->choice = i; + else + md->choice = -1; + } else + md->choice = -1; md->cb = cb; md->data = data; - server_client_set_overlay(c, 0, menu_draw_cb, menu_key_cb, menu_free_cb, - md); + server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, + menu_key_cb, menu_free_cb, md); return (0); } diff --git a/mode-tree.c b/mode-tree.c index b9fa5f65..783ffcfa 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -256,8 +256,8 @@ mode_tree_expand_current(struct mode_tree_data *mtd) } } -void -mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag) +static int +mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found) { u_int i; @@ -266,15 +266,41 @@ mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag) break; } if (i != mtd->line_size) { - mtd->current = i; + *found = i; + return (1); + } + return (0); +} + +void +mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag) +{ + u_int found; + + if (!mode_tree_get_tag(mtd, tag, &found)) + return; + if (!mtd->line_list[found].item->expanded) { + mtd->line_list[found].item->expanded = 1; + mode_tree_build(mtd); + } +} + +int +mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag) +{ + u_int found; + + if (mode_tree_get_tag(mtd, tag, &found)) { + mtd->current = found; if (mtd->current > mtd->height - 1) mtd->offset = mtd->current - mtd->height + 1; else mtd->offset = 0; - } else { - mtd->current = 0; - mtd->offset = 0; + return (1); } + mtd->current = 0; + mtd->offset = 0; + return (0); } u_int @@ -847,6 +873,10 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x, mtm->itemdata = mti->itemdata; mtd->references++; + if (x >= (menu->width + 4) / 2) + x -= (menu->width + 4) / 2; + else + x = 0; if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback, mtm) != 0) menu_free(menu); @@ -1059,33 +1089,22 @@ void mode_tree_run_command(struct client *c, struct cmd_find_state *fs, const char *template, const char *name) { - struct cmdq_item *new_item; - char *command; - struct cmd_parse_result *pr; + struct cmdq_state *state; + char *command, *error; + enum cmd_parse_status status; command = cmd_template_replace(template, name, 1); - if (command == NULL || *command == '\0') { - free(command); - return; - } - - pr = cmd_parse_from_string(command, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - break; - case CMD_PARSE_ERROR: - if (c != NULL) { - *pr->error = toupper((u_char)*pr->error); - status_message_set(c, "%s", pr->error); + if (command != NULL && *command != '\0') { + state = cmdq_new_state(fs, NULL, 0); + status = cmd_parse_and_append(command, NULL, c, state, &error); + if (status == CMD_PARSE_ERROR) { + if (c != NULL) { + *error = toupper((u_char)*error); + status_message_set(c, "%s", error); + } + free(error); } - free(pr->error); - break; - case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, fs, NULL, 0); - cmdq_append(c, new_item); - cmd_list_free(pr->cmdlist); - break; + cmdq_free_state(state); } - free(command); } @@ -35,19 +35,19 @@ struct notify_entry { }; static void -notify_hook_formats(struct cmdq_item *item, struct session *s, struct window *w, - int pane) +notify_hook_formats(struct cmdq_state *state, struct session *s, + struct window *w, int pane) { if (s != NULL) { - cmdq_format(item, "hook_session", "$%u", s->id); - cmdq_format(item, "hook_session_name", "%s", s->name); + cmdq_add_format(state, "hook_session", "$%u", s->id); + cmdq_add_format(state, "hook_session_name", "%s", s->name); } if (w != NULL) { - cmdq_format(item, "hook_window", "@%u", w->id); - cmdq_format(item, "hook_window_name", "%s", w->name); + cmdq_add_format(state, "hook_window", "@%u", w->id); + cmdq_add_format(state, "hook_window_name", "%s", w->name); } if (pane != -1) - cmdq_format(item, "hook_pane", "%%%d", pane); + cmdq_add_format(state, "hook_pane", "%%%d", pane); } static void @@ -56,6 +56,7 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) struct cmd_find_state fs; struct options *oo; struct cmdq_item *new_item; + struct cmdq_state *new_state; struct session *s = ne->session; struct window *w = ne->window; struct options_entry *o; @@ -75,24 +76,32 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) else oo = fs.s->options; o = options_get(oo, ne->name); + if (o == NULL && fs.wp != NULL) { + oo = fs.wp->options; + o = options_get(oo, ne->name); + } + if (o == NULL && fs.wl != NULL) { + oo = fs.wl->window->options; + o = options_get(oo, ne->name); + } if (o == NULL) return; + new_state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); + cmdq_add_format(new_state, "hook", "%s", ne->name); + notify_hook_formats(new_state, s, w, ne->pane); + a = options_array_first(o); while (a != NULL) { cmdlist = options_array_item_value(a)->cmdlist; - if (cmdlist == NULL) { - a = options_array_next(a); - continue; + if (cmdlist != NULL) { + new_item = cmdq_get_command(cmdlist, new_state); + item = cmdq_insert_after(item, new_item); } - - new_item = cmdq_get_command(cmdlist, &fs, NULL, CMDQ_NOHOOKS); - cmdq_format(new_item, "hook", "%s", ne->name); - notify_hook_formats(new_item, s, w, ne->pane); - item = cmdq_insert_after(item, new_item); - a = options_array_next(a); } + + cmdq_free_state(new_state); } static enum cmd_retval @@ -148,7 +157,11 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c, struct session *s, struct window *w, struct window_pane *wp) { struct notify_entry *ne; - struct cmdq_item *new_item; + struct cmdq_item *item; + + item = cmdq_running(NULL); + if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS)) + return; ne = xcalloc(1, sizeof *ne); ne->name = xstrdup(name); @@ -173,24 +186,24 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c, if (ne->fs.s != NULL) /* cmd_find_valid_state needs session */ session_add_ref(ne->fs.s, __func__); - new_item = cmdq_get_callback(notify_callback, ne); - cmdq_append(NULL, new_item); + cmdq_append(NULL, cmdq_get_callback(notify_callback, ne)); } void notify_hook(struct cmdq_item *item, const char *name) { - struct notify_entry ne; + struct cmd_find_state *target = cmdq_get_target(item); + struct notify_entry ne; memset(&ne, 0, sizeof ne); ne.name = name; - cmd_find_copy_state(&ne.fs, &item->target); + cmd_find_copy_state(&ne.fs, target); - ne.client = item->client; - ne.session = item->target.s; - ne.window = item->target.w; - ne.pane = item->target.wp->id; + ne.client = cmdq_get_client(item); + ne.session = target->s; + ne.window = target->w; + ne.pane = target->wp->id; notify_insert_hook(item, &ne); } diff --git a/options-table.c b/options-table.c index be0d220d..d593eff6 100644 --- a/options-table.c +++ b/options-table.c @@ -139,7 +139,7 @@ static const char *options_table_status_format_default[] = { OPTIONS_TABLE_STATUS_FORMAT1, OPTIONS_TABLE_STATUS_FORMAT2, NULL }; -/* Helper for hook options. */ +/* Helpers for hook options. */ #define OPTIONS_TABLE_HOOK(hook_name, default_value) \ { .name = hook_name, \ .type = OPTIONS_TABLE_COMMAND, \ @@ -149,6 +149,24 @@ static const char *options_table_status_format_default[] = { .separator = "" \ } +#define OPTIONS_TABLE_PANE_HOOK(hook_name, default_value) \ + { .name = hook_name, \ + .type = OPTIONS_TABLE_COMMAND, \ + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, \ + .flags = OPTIONS_TABLE_IS_ARRAY|OPTIONS_TABLE_IS_HOOK, \ + .default_str = default_value, \ + .separator = "" \ + } + +#define OPTIONS_TABLE_WINDOW_HOOK(hook_name, default_value) \ + { .name = hook_name, \ + .type = OPTIONS_TABLE_COMMAND, \ + .scope = OPTIONS_TABLE_WINDOW, \ + .flags = OPTIONS_TABLE_IS_ARRAY|OPTIONS_TABLE_IS_HOOK, \ + .default_str = default_value, \ + .separator = "" \ + } + /* Top-level options. */ const struct options_table_entry options_table[] = { /* Server options. */ @@ -179,6 +197,12 @@ const struct options_table_entry options_table[] = { .separator = "," }, + { .name = "copy-command", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, @@ -236,9 +260,16 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, - .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" - ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" - ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT", + .default_str = "", + .separator = "," + }, + + { .name = "terminal-features", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .flags = OPTIONS_TABLE_IS_ARRAY, + .default_str = "xterm*:clipboard:ccolour:cstyle:title," + "screen*:title", .separator = "," }, @@ -615,19 +646,15 @@ const struct options_table_entry options_table[] = { }, { .name = "main-pane-height", - .type = OPTIONS_TABLE_NUMBER, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .minimum = 1, - .maximum = INT_MAX, - .default_num = 24 + .default_str = "24" }, { .name = "main-pane-width", - .type = OPTIONS_TABLE_NUMBER, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .minimum = 1, - .maximum = INT_MAX, - .default_num = 80 + .default_str = "80" }, { .name = "mode-keys", @@ -664,19 +691,15 @@ const struct options_table_entry options_table[] = { }, { .name = "other-pane-height", - .type = OPTIONS_TABLE_NUMBER, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 + .default_str = "0" }, { .name = "other-pane-width", - .type = OPTIONS_TABLE_NUMBER, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 + .default_str = "0" }, { .name = "pane-active-border-style", @@ -850,21 +873,21 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("client-detached", ""), OPTIONS_TABLE_HOOK("client-resized", ""), OPTIONS_TABLE_HOOK("client-session-changed", ""), - OPTIONS_TABLE_HOOK("pane-died", ""), - OPTIONS_TABLE_HOOK("pane-exited", ""), - OPTIONS_TABLE_HOOK("pane-focus-in", ""), - OPTIONS_TABLE_HOOK("pane-focus-out", ""), - OPTIONS_TABLE_HOOK("pane-mode-changed", ""), - OPTIONS_TABLE_HOOK("pane-set-clipboard", ""), + OPTIONS_TABLE_PANE_HOOK("pane-died", ""), + OPTIONS_TABLE_PANE_HOOK("pane-exited", ""), + OPTIONS_TABLE_PANE_HOOK("pane-focus-in", ""), + OPTIONS_TABLE_PANE_HOOK("pane-focus-out", ""), + OPTIONS_TABLE_PANE_HOOK("pane-mode-changed", ""), + OPTIONS_TABLE_PANE_HOOK("pane-set-clipboard", ""), OPTIONS_TABLE_HOOK("session-closed", ""), OPTIONS_TABLE_HOOK("session-created", ""), OPTIONS_TABLE_HOOK("session-renamed", ""), OPTIONS_TABLE_HOOK("session-window-changed", ""), - OPTIONS_TABLE_HOOK("window-layout-changed", ""), - OPTIONS_TABLE_HOOK("window-linked", ""), - OPTIONS_TABLE_HOOK("window-pane-changed", ""), - OPTIONS_TABLE_HOOK("window-renamed", ""), - OPTIONS_TABLE_HOOK("window-unlinked", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-layout-changed", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-linked", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-pane-changed", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-renamed", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-unlinked", ""), { .name = NULL } }; diff --git a/osdep-darwin.c b/osdep-darwin.c index d4a88028..6b2b1d72 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -61,7 +61,7 @@ osdep_get_name(int fd, __unused char *tty) size = sizeof kp; if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) return (NULL); - if (*kp.kp_proc.p_comm == '\0') + if (size != (sizeof kp) || *kp.kp_proc.p_comm == '\0') return (NULL); return (strdup(kp.kp_proc.p_comm)); diff --git a/popup.c b/popup.c new file mode 100644 index 00000000..5d39e599 --- /dev/null +++ b/popup.c @@ -0,0 +1,464 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com> + * + * 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 <sys/types.h> +#include <sys/wait.h> + +#include <signal.h> +#include <stdlib.h> +#include <string.h> + +#include "tmux.h" + +struct popup_data { + struct client *c; + struct cmdq_item *item; + int flags; + + char **lines; + u_int nlines; + + char *cmd; + struct cmd_find_state fs; + struct screen s; + + struct job *job; + struct input_ctx *ictx; + int status; + + u_int px; + u_int py; + u_int sx; + u_int sy; + + enum { OFF, MOVE, SIZE } dragging; + u_int dx; + u_int dy; + + u_int lx; + u_int ly; + u_int lb; +}; + +static void +popup_write_screen(struct client *c, struct popup_data *pd) +{ + struct cmdq_item *item = pd->item; + struct screen_write_ctx ctx; + char *copy, *next, *loop, *tmp; + struct format_tree *ft; + u_int i, y; + + ft = format_create(c, item, FORMAT_NONE, 0); + if (cmd_find_valid_state(&pd->fs)) + format_defaults(ft, c, pd->fs.s, pd->fs.wl, pd->fs.wp); + else + format_defaults(ft, c, NULL, NULL, NULL); + + screen_write_start(&ctx, NULL, &pd->s); + screen_write_clearscreen(&ctx, 8); + + y = 0; + for (i = 0; i < pd->nlines; i++) { + if (y == pd->sy - 2) + break; + copy = next = xstrdup(pd->lines[i]); + while ((loop = strsep(&next, "\n")) != NULL) { + if (y == pd->sy - 2) + break; + tmp = format_expand(ft, loop); + screen_write_cursormove(&ctx, 0, y, 0); + format_draw(&ctx, &grid_default_cell, pd->sx - 2, tmp, + NULL); + free(tmp); + y++; + } + free(copy); + } + + format_free(ft); + screen_write_cursormove(&ctx, 0, y, 0); + screen_write_stop(&ctx); +} + +static int +popup_mode_cb(struct client *c, u_int *cx, u_int *cy) +{ + struct popup_data *pd = c->overlay_data; + + if (pd->ictx == NULL) + return (0); + *cx = pd->px + 1 + pd->s.cx; + *cy = pd->py + 1 + pd->s.cy; + return (pd->s.mode); +} + +static int +popup_check_cb(struct client *c, u_int px, u_int py) +{ + struct popup_data *pd = c->overlay_data; + + if (px < pd->px || px > pd->px + pd->sx - 1) + return (1); + if (py < pd->py || py > pd->py + pd->sy - 1) + return (1); + return (0); +} + +static void +popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) +{ + struct popup_data *pd = c->overlay_data; + struct tty *tty = &c->tty; + struct screen s; + struct screen_write_ctx ctx; + u_int i, px = pd->px, py = pd->py; + + screen_init(&s, pd->sx, pd->sy, 0); + screen_write_start(&ctx, NULL, &s); + screen_write_clearscreen(&ctx, 8); + screen_write_box(&ctx, pd->sx, pd->sy); + screen_write_cursormove(&ctx, 1, 1, 0); + screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, pd->sy - 2); + screen_write_stop(&ctx); + + c->overlay_check = NULL; + for (i = 0; i < pd->sy; i++) + tty_draw_line(tty, NULL, &s, 0, i, pd->sx, px, py + i); + c->overlay_check = popup_check_cb; +} + +static void +popup_free_cb(struct client *c) +{ + struct popup_data *pd = c->overlay_data; + struct cmdq_item *item = pd->item; + u_int i; + + if (item != NULL) { + if (pd->ictx != NULL && + cmdq_get_client(item) != NULL && + cmdq_get_client(item)->session == NULL) + cmdq_get_client(item)->retval = pd->status; + cmdq_continue(item); + } + server_client_unref(pd->c); + + if (pd->job != NULL) + job_free(pd->job); + if (pd->ictx != NULL) + input_free(pd->ictx); + + for (i = 0; i < pd->nlines; i++) + free(pd->lines[i]); + free(pd->lines); + + screen_free(&pd->s); + free(pd->cmd); + free(pd); +} + +static void +popup_handle_drag(struct client *c, struct popup_data *pd, + struct mouse_event *m) +{ + u_int px, py; + + if (!MOUSE_DRAG(m->b)) + pd->dragging = OFF; + else if (pd->dragging == MOVE) { + if (m->x < pd->dx) + px = 0; + else if (m->x - pd->dx + pd->sx > c->tty.sx) + px = c->tty.sx - pd->sx; + else + px = m->x - pd->dx; + if (m->y < pd->dy) + py = 0; + else if (m->y - pd->dy + pd->sy > c->tty.sy) + py = c->tty.sy - pd->sy; + else + py = m->y - pd->dy; + pd->px = px; + pd->py = py; + pd->dx = m->x - pd->px; + pd->dy = m->y - pd->py; + server_redraw_client(c); + } else if (pd->dragging == SIZE) { + if (m->x < pd->px + 3) + return; + if (m->y < pd->py + 3) + return; + pd->sx = m->x - pd->px; + pd->sy = m->y - pd->py; + + screen_resize(&pd->s, pd->sx, pd->sy, 0); + if (pd->ictx == NULL) + popup_write_screen(c, pd); + else if (pd->job != NULL) + job_resize(pd->job, pd->sx - 2, pd->sy - 2); + server_redraw_client(c); + } +} + +static int +popup_key_cb(struct client *c, struct key_event *event) +{ + struct popup_data *pd = c->overlay_data; + struct mouse_event *m = &event->m; + struct cmd_find_state *fs = &pd->fs; + struct format_tree *ft; + const char *cmd, *buf; + size_t len; + struct cmdq_state *state; + enum cmd_parse_status status; + char *error; + + if (KEYC_IS_MOUSE(event->key)) { + if (pd->dragging != OFF) { + popup_handle_drag(c, pd, m); + goto out; + } + if (m->x < pd->px || + m->x > pd->px + pd->sx - 1 || + m->y < pd->py || + m->y > pd->py + pd->sy - 1) { + if (MOUSE_BUTTONS (m->b) == 1) + return (1); + return (0); + } + if ((m->b & MOUSE_MASK_META) || + m->x == pd->px || + m->x == pd->px + pd->sx - 1 || + m->y == pd->py || + m->y == pd->py + pd->sy - 1) { + if (!MOUSE_DRAG(m->b)) + goto out; + if (MOUSE_BUTTONS(m->lb) == 0) + pd->dragging = MOVE; + else if (MOUSE_BUTTONS(m->lb) == 2) + pd->dragging = SIZE; + pd->dx = m->lx - pd->px; + pd->dy = m->ly - pd->py; + goto out; + } + } + + if (pd->ictx != NULL && (pd->flags & POPUP_WRITEKEYS)) { + if (((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0 || + pd->job == NULL) && + (event->key == '\033' || event->key == '\003')) + return (1); + if (pd->job == NULL) + return (0); + if (KEYC_IS_MOUSE(event->key)) { + /* Must be inside, checked already. */ + if (!input_key_get_mouse(&pd->s, m, m->x - pd->px, + m->y - pd->py, &buf, &len)) + return (0); + bufferevent_write(job_get_event(pd->job), buf, len); + return (0); + } + input_key(NULL, &pd->s, job_get_event(pd->job), event->key); + return (0); + } + + if (pd->cmd == NULL) + return (1); + + ft = format_create(NULL, pd->item, FORMAT_NONE, 0); + if (cmd_find_valid_state(fs)) + format_defaults(ft, c, fs->s, fs->wl, fs->wp); + else + format_defaults(ft, c, NULL, NULL, NULL); + format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key)); + if (KEYC_IS_MOUSE(event->key)) { + format_add(ft, "popup_mouse", "1"); + format_add(ft, "popup_mouse_x", "%u", m->x - pd->px); + format_add(ft, "popup_mouse_y", "%u", m->y - pd->py); + } + cmd = format_expand(ft, pd->cmd); + format_free(ft); + + if (pd->item != NULL) + event = cmdq_get_event(pd->item); + else + event = NULL; + state = cmdq_new_state(&pd->fs, event, 0); + + status = cmd_parse_and_append(cmd, NULL, c, state, &error); + if (status == CMD_PARSE_ERROR) { + cmdq_append(c, cmdq_get_error(error)); + free(error); + } + cmdq_free_state(state); + + return (1); + +out: + pd->lx = m->x; + pd->ly = m->y; + pd->lb = m->b; + return (0); +} + +static void +popup_job_update_cb(struct job *job) +{ + struct popup_data *pd = job_get_data(job); + struct evbuffer *evb = job_get_event(job)->input; + struct screen *s = &pd->s; + void *data = EVBUFFER_DATA(evb); + size_t size = EVBUFFER_LENGTH(evb); + + if (size != 0) { + input_parse_screen(pd->ictx, s, data, size); + evbuffer_drain(evb, size); + pd->c->flags |= CLIENT_REDRAWOVERLAY; + } +} + +static void +popup_job_complete_cb(struct job *job) +{ + struct popup_data *pd = job_get_data(job); + int status; + + status = job_get_status(pd->job); + if (WIFEXITED(status)) + pd->status = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + pd->status = WTERMSIG(status); + else + pd->status = 0; + pd->job = NULL; + + if ((pd->flags & POPUP_CLOSEEXIT) || + ((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0)) + server_client_clear_overlay(pd->c); +} + +u_int +popup_height(u_int nlines, const char **lines) +{ + char *copy, *next, *loop; + u_int i, height = 0; + + for (i = 0; i < nlines; i++) { + copy = next = xstrdup(lines[i]); + while ((loop = strsep(&next, "\n")) != NULL) + height++; + free(copy); + } + + return (height); +} + +u_int +popup_width(struct cmdq_item *item, u_int nlines, const char **lines, + struct client *c, struct cmd_find_state *fs) +{ + char *copy, *next, *loop, *tmp; + struct format_tree *ft; + u_int i, width = 0, tmpwidth; + + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); + if (fs != NULL && cmd_find_valid_state(fs)) + format_defaults(ft, c, fs->s, fs->wl, fs->wp); + else + format_defaults(ft, c, NULL, NULL, NULL); + + for (i = 0; i < nlines; i++) { + copy = next = xstrdup(lines[i]); + while ((loop = strsep(&next, "\n")) != NULL) { + tmp = format_expand(ft, loop); + tmpwidth = format_width(tmp); + if (tmpwidth > width) + width = tmpwidth; + free(tmp); + } + free(copy); + } + + format_free(ft); + return (width); +} + +int +popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, + u_int sy, u_int nlines, const char **lines, const char *shellcmd, + const char *cmd, const char *cwd, struct client *c, + struct cmd_find_state *fs) +{ + struct popup_data *pd; + u_int i; + struct session *s; + int jobflags; + + if (sx < 3 || sy < 3) + return (-1); + if (c->tty.sx < sx || c->tty.sy < sy) + return (-1); + + pd = xcalloc(1, sizeof *pd); + pd->item = item; + pd->flags = flags; + + pd->c = c; + pd->c->references++; + + pd->status = 128 + SIGHUP; + + if (fs != NULL) + cmd_find_copy_state(&pd->fs, fs); + screen_init(&pd->s, sx - 2, sy - 2, 0); + + if (cmd != NULL) + pd->cmd = xstrdup(cmd); + + pd->px = px; + pd->py = py; + pd->sx = sx; + pd->sy = sy; + + pd->nlines = nlines; + if (pd->nlines != 0) + pd->lines = xreallocarray(NULL, pd->nlines, sizeof *pd->lines); + + for (i = 0; i < pd->nlines; i++) + pd->lines[i] = xstrdup(lines[i]); + popup_write_screen(c, pd); + + if (shellcmd != NULL) { + if (fs != NULL) + s = fs->s; + else + s = NULL; + jobflags = JOB_NOWAIT|JOB_PTY; + if (flags & POPUP_WRITEKEYS) + jobflags |= JOB_KEEPWRITE; + pd->job = job_run(shellcmd, s, cwd, popup_job_update_cb, + popup_job_complete_cb, NULL, pd, jobflags, pd->sx - 2, + pd->sy - 2); + pd->ictx = input_init(NULL, job_get_event(pd->job)); + } + + server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, + popup_draw_cb, popup_key_cb, popup_free_cb, pd); + return (0); +} diff --git a/regress/capture-pane-sgr0.sh b/regress/capture-pane-sgr0.sh index 79d96a38..0dd9cd82 100644 --- a/regress/capture-pane-sgr0.sh +++ b/regress/capture-pane-sgr0.sh @@ -13,11 +13,18 @@ $TMUX kill-server 2>/dev/null TMP=$(mktemp) trap "rm -f $TMP" 0 1 15 -$TMUX -f/dev/null new -d \ - "printf '\033[31;42;1mabc\033[0;31mdef'; $TMUX capturep -peS0 -E0 >$TMP" +$TMUX -f/dev/null new -d " + printf '\033[31;42;1mabc\033[0;31mdef\n' + printf '\033[m\033[100m bright bg \033[m' + $TMUX capturep -peS0 -E1 >>$TMP" + + sleep 1 -printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\033[39m\n'| \ - cmp - $TMP || exit 1 + +( + printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\033[39m\n' + printf '\033[100m bright bg \033[49m\n' +) | cmp - $TMP || exit 1 $TMUX has 2>/dev/null && exit 1 @@ -24,8 +24,7 @@ #include "tmux.h" static void -regsub_copy(char **buf, size_t *len, const char *text, size_t start, - size_t end) +regsub_copy(char **buf, size_t *len, const char *text, size_t start, size_t end) { size_t add = end - start; diff --git a/screen-redraw.c b/screen-redraw.c index e7f4f077..5ca6024d 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -434,20 +434,33 @@ screen_redraw_screen(struct client *c) return; flags = screen_redraw_update(c, c->flags); + if ((flags & CLIENT_ALLREDRAWFLAGS) == 0) + return; + screen_redraw_set_context(c, &ctx); + tty_update_mode(&c->tty, c->tty.mode, NULL); + tty_sync_start(&c->tty); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { + log_debug("%s: redrawing borders", c->name); if (ctx.pane_status != PANE_STATUS_OFF) screen_redraw_draw_pane_status(&ctx); screen_redraw_draw_borders(&ctx); } - if (flags & CLIENT_REDRAWWINDOW) + if (flags & CLIENT_REDRAWWINDOW) { + log_debug("%s: redrawing panes", c->name); screen_redraw_draw_panes(&ctx); + } if (ctx.statuslines != 0 && - (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) + (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) { + log_debug("%s: redrawing status", c->name); screen_redraw_draw_status(&ctx); - if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) + } + if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) { + log_debug("%s: redrawing overlay", c->name); c->overlay_draw(c, &ctx); + } + tty_reset(&c->tty); } @@ -461,8 +474,11 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) return; screen_redraw_set_context(c, &ctx); + tty_update_mode(&c->tty, c->tty.mode, NULL); + tty_sync_start(&c->tty); screen_redraw_draw_pane(&ctx, wp); + tty_reset(&c->tty); } @@ -482,6 +498,8 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j, u_int type, x = ctx->ox + i, y = ctx->oy + j; int flag, pane_status = ctx->pane_status; + if (c->overlay_check != NULL && !c->overlay_check(c, x, y)) + return; type = screen_redraw_check_cell(c, x, y, pane_status, &wp); if (type == CELL_INSIDE) return; diff --git a/screen-write.c b/screen-write.c index 43cb42b4..afa1e96a 100644 --- a/screen-write.c +++ b/screen-write.c @@ -23,12 +23,15 @@ #include "tmux.h" -static void screen_write_initctx(struct screen_write_ctx *, - struct tty_ctx *); static void screen_write_collect_clear(struct screen_write_ctx *, u_int, u_int); +static int screen_write_collect_clear_end(struct screen_write_ctx *, u_int, + u_int, u_int); +static int screen_write_collect_clear_start(struct screen_write_ctx *, u_int, + u_int, u_int); static void screen_write_collect_scroll(struct screen_write_ctx *); -static void screen_write_collect_flush(struct screen_write_ctx *, int); +static void screen_write_collect_flush(struct screen_write_ctx *, int, + const char *); static int screen_write_overwrite(struct screen_write_ctx *, struct grid_cell *, u_int); @@ -43,15 +46,18 @@ struct screen_write_collect_item { u_int x; int wrapped; + enum { TEXT, CLEAR_END, CLEAR_START } type; u_int used; - char data[256]; + u_int bg; struct grid_cell gc; TAILQ_ENTRY(screen_write_collect_item) entry; }; struct screen_write_collect_line { - TAILQ_HEAD(, screen_write_collect_item) items; + u_int bg; + char *data; + TAILQ_HEAD(, screen_write_collect_item) items; }; static void @@ -95,13 +101,58 @@ screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy) evtimer_add(&w->offset_timer, &tv); } +/* Set up context for TTY command. */ +static void +screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, + int sync) +{ + struct screen *s = ctx->s; + + memset(ttyctx, 0, sizeof *ttyctx); + + ttyctx->wp = ctx->wp; + + ttyctx->ocx = s->cx; + ttyctx->ocy = s->cy; + + ttyctx->orlower = s->rlower; + ttyctx->orupper = s->rupper; + + if (ctx->wp != NULL && + !ctx->sync && + (sync || ctx->wp != ctx->wp->window->active)) { + tty_write(tty_cmd_syncstart, ttyctx); + ctx->sync = 1; + } +} + +/* Make write list. */ +void +screen_write_make_list(struct screen *s) +{ + u_int y; + + s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list); + for (y = 0; y < screen_size_y(s); y++) + TAILQ_INIT(&s->write_list[y].items); +} + +/* Free write list. */ +void +screen_write_free_list(struct screen *s) +{ + u_int y; + + for (y = 0; y < screen_size_y(s); y++) + free(s->write_list[y].data); + free(s->write_list); +} + /* Initialize writing with a window. */ void screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) { - u_int y; - memset(ctx, 0, sizeof *ctx); ctx->wp = wp; @@ -110,9 +161,8 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, else ctx->s = s; - ctx->list = xcalloc(screen_size_y(ctx->s), sizeof *ctx->list); - for (y = 0; y < screen_size_y(ctx->s); y++) - TAILQ_INIT(&ctx->list[y].items); + if (ctx->s->write_list == NULL) + screen_write_make_list(ctx->s); ctx->item = xcalloc(1, sizeof *ctx->item); ctx->scrolled = 0; @@ -136,13 +186,16 @@ void screen_write_stop(struct screen_write_ctx *ctx) { screen_write_collect_end(ctx); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); log_debug("%s: %u cells (%u written, %u skipped)", __func__, ctx->cells, ctx->written, ctx->skipped); + if (ctx->wp != NULL) { + ctx->wp->written += ctx->written; + ctx->wp->skipped += ctx->skipped; + } free(ctx->item); - free(ctx->list); /* flush will have emptied */ } /* Reset screen state. */ @@ -409,21 +462,23 @@ screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom) /* Draw a menu on screen. */ void -screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice) +screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, + int choice, const struct grid_cell *choice_gc) { struct screen *s = ctx->s; - struct grid_cell gc; + struct grid_cell default_gc; + const struct grid_cell *gc = &default_gc; u_int cx, cy, i, j; const char *name; cx = s->cx; cy = s->cy; - memcpy(&gc, &grid_default_cell, sizeof gc); + memcpy(&default_gc, &grid_default_cell, sizeof default_gc); screen_write_box(ctx, menu->width + 4, menu->count + 2); screen_write_cursormove(ctx, cx + 2, cy, 0); - format_draw(ctx, &gc, menu->width, menu->title, NULL); + format_draw(ctx, &default_gc, menu->width, menu->title, NULL); for (i = 0; i < menu->count; i++) { name = menu->items[i].name; @@ -432,20 +487,19 @@ screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice) screen_write_hline(ctx, menu->width + 4, 1, 1); } else { if (choice >= 0 && i == (u_int)choice && *name != '-') - gc.attr |= GRID_ATTR_REVERSE; + gc = choice_gc; screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); for (j = 0; j < menu->width; j++) - screen_write_putc(ctx, &gc, ' '); + screen_write_putc(ctx, gc, ' '); screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); if (*name == '-') { name++; - gc.attr |= GRID_ATTR_DIM; - format_draw(ctx, &gc, menu->width, name, NULL); - gc.attr &= ~GRID_ATTR_DIM; + default_gc.attr |= GRID_ATTR_DIM; + format_draw(ctx, gc, menu->width, name, NULL); + default_gc.attr &= ~GRID_ATTR_DIM; } else - format_draw(ctx, &gc, menu->width, name, NULL); - if (choice >= 0 && i == (u_int)choice) - gc.attr &= ~GRID_ATTR_REVERSE; + format_draw(ctx, gc, menu->width, name, NULL); + gc = &default_gc; } } @@ -547,23 +601,6 @@ screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx, } } -/* Set up context for TTY command. */ -static void -screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) -{ - struct screen *s = ctx->s; - - memset(ttyctx, 0, sizeof *ttyctx); - - ttyctx->wp = ctx->wp; - - ttyctx->ocx = s->cx; - ttyctx->ocy = s->cy; - - ttyctx->orlower = s->rlower; - ttyctx->orupper = s->rupper; -} - /* Set a mode. */ void screen_write_mode_set(struct screen_write_ctx *ctx, int mode) @@ -722,7 +759,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) s->rupper = 0; s->rlower = screen_size_y(s) - 1; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1); tty_write(tty_cmd_alignmenttest, &ttyctx); @@ -746,12 +783,12 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (s->cx > screen_size_x(s) - 1) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.bg = bg; grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = nx; tty_write(tty_cmd_insertcharacter, &ttyctx); } @@ -774,12 +811,12 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (s->cx > screen_size_x(s) - 1) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.bg = bg; grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = nx; tty_write(tty_cmd_deletecharacter, &ttyctx); } @@ -802,12 +839,12 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (s->cx > screen_size_x(s) - 1) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.bg = bg; grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = nx; tty_write(tty_cmd_clearcharacter, &ttyctx); } @@ -829,12 +866,12 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; grid_view_insert_lines(gd, s->cy, ny, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = ny; tty_write(tty_cmd_insertline, &ttyctx); return; @@ -845,7 +882,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; if (s->cy < s->rupper || s->cy > s->rlower) @@ -853,7 +890,8 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) else grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); + ttyctx.num = ny; tty_write(tty_cmd_insertline, &ttyctx); } @@ -875,12 +913,12 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; grid_view_delete_lines(gd, s->cy, ny, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = ny; tty_write(tty_cmd_deleteline, &ttyctx); return; @@ -891,7 +929,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; if (s->cy < s->rupper || s->cy > s->rlower) @@ -899,7 +937,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) else grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = ny; tty_write(tty_cmd_deleteline, &ttyctx); } @@ -908,69 +946,75 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) void screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) { - struct screen *s = ctx->s; - struct grid_line *gl; - struct tty_ctx ttyctx; - u_int sx = screen_size_x(s); + struct screen *s = ctx->s; + struct grid_line *gl; + u_int sx = screen_size_x(s); gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) return; - screen_write_initctx(ctx, &ttyctx); - ttyctx.bg = bg; - grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); screen_write_collect_clear(ctx, s->cy, 1); - screen_write_collect_flush(ctx, 0); - tty_write(tty_cmd_clearline, &ttyctx); + ctx->s->write_list[s->cy].bg = 1 + bg; + ctx->item->used = 0; } /* Clear to end of line from cursor. */ void screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) { - struct screen *s = ctx->s; - struct grid_line *gl; - struct tty_ctx ttyctx; - u_int sx = screen_size_x(s); + struct screen *s = ctx->s; + struct grid_line *gl; + u_int sx = screen_size_x(s); + struct screen_write_collect_item *ci = ctx->item; + + if (s->cx == 0) { + screen_write_clearline(ctx, bg); + return; + } gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) return; - screen_write_initctx(ctx, &ttyctx); - ttyctx.bg = bg; - grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); - if (s->cx == 0) - screen_write_collect_clear(ctx, s->cy, 1); - screen_write_collect_flush(ctx, 0); - tty_write(tty_cmd_clearendofline, &ttyctx); + if (!screen_write_collect_clear_end(ctx, s->cy, s->cx, bg)) { + ci->x = s->cx; + ci->type = CLEAR_END; + ci->bg = bg; + TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); + ctx->item = xcalloc(1, sizeof *ctx->item); + } } /* Clear to start of line from cursor. */ void screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) { - struct screen *s = ctx->s; - struct tty_ctx ttyctx; - u_int sx = screen_size_x(s); + struct screen *s = ctx->s; + u_int sx = screen_size_x(s); + struct screen_write_collect_item *ci = ctx->item; - screen_write_initctx(ctx, &ttyctx); - ttyctx.bg = bg; + if (s->cx >= sx - 1) { + screen_write_clearline(ctx, bg); + return; + } if (s->cx > sx - 1) grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); else grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); - if (s->cx > sx - 1) - screen_write_collect_clear(ctx, s->cy, 1); - screen_write_collect_flush(ctx, 0); - tty_write(tty_cmd_clearstartofline, &ttyctx); + if (!screen_write_collect_clear_start(ctx, s->cy, s->cx, bg)) { + ci->x = s->cx; + ci->type = CLEAR_START; + ci->bg = bg; + TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); + ctx->item = xcalloc(1, sizeof *ctx->item); + } } /* Move cursor to px,py. */ @@ -1002,16 +1046,17 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) struct screen *s = ctx->s; struct tty_ctx ttyctx; - screen_write_initctx(ctx, &ttyctx); - ttyctx.bg = bg; - - if (s->cy == s->rupper) + if (s->cy == s->rupper) { grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); - else if (s->cy > 0) + screen_write_collect_flush(ctx, 0, __func__); + + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.bg = bg; + + tty_write(tty_cmd_reverseindex, &ttyctx); + } else if (s->cy > 0) screen_write_set_cursor(ctx, -1, s->cy - 1); - screen_write_collect_flush(ctx, 0); - tty_write(tty_cmd_reverseindex, &ttyctx); } /* Set scroll region. */ @@ -1028,7 +1073,7 @@ screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, if (rupper >= rlower) /* cannot be one line */ return; - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); /* Cursor moves to top-left. */ screen_write_set_cursor(ctx, 0, 0); @@ -1055,7 +1100,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) s->rupper, s->rlower); if (bg != ctx->bg) { - screen_write_collect_flush(ctx, 1); + screen_write_collect_flush(ctx, 1, __func__); ctx->bg = bg; } @@ -1081,7 +1126,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) lines = s->rlower - s->rupper + 1; if (bg != ctx->bg) { - screen_write_collect_flush(ctx, 1); + screen_write_collect_flush(ctx, 1, __func__); ctx->bg = bg; } @@ -1101,7 +1146,7 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) struct tty_ctx ttyctx; u_int i; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; if (lines == 0) @@ -1112,7 +1157,7 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) for (i = 0; i < lines; i++) grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = lines; tty_write(tty_cmd_scrolldown, &ttyctx); } @@ -1133,7 +1178,7 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) struct tty_ctx ttyctx; u_int sx = screen_size_x(s), sy = screen_size_y(s); - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; /* Scroll into history if it is enabled and clearing entire screen. */ @@ -1146,7 +1191,7 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) } screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); tty_write(tty_cmd_clearendofscreen, &ttyctx); } @@ -1158,7 +1203,7 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) struct tty_ctx ttyctx; u_int sx = screen_size_x(s); - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; if (s->cy > 0) @@ -1169,7 +1214,7 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); screen_write_collect_clear(ctx, 0, s->cy); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); tty_write(tty_cmd_clearstartofscreen, &ttyctx); } @@ -1181,7 +1226,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) struct tty_ctx ttyctx; u_int sx = screen_size_x(s), sy = screen_size_y(s); - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.bg = bg; /* Scroll into history if it is enabled. */ @@ -1201,25 +1246,112 @@ screen_write_clearhistory(struct screen_write_ctx *ctx) grid_clear_history(ctx->s->grid); } -/* Clear a collected line. */ +/* Clear to start of a collected line. */ +static int +screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x, + u_int bg) +{ + struct screen_write_collect_item *ci, *tmp; + size_t size = 0; + u_int items = 0; + int redundant = 0; + + if (TAILQ_EMPTY(&ctx->s->write_list[y].items)) + return (0); + TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) { + switch (ci->type) { + case CLEAR_START: + if (ci->x >= x) { + if (ci->bg == bg) + redundant = 1; + continue; + } + break; + case CLEAR_END: + if (ci->x <= x) + ci->x = x; + continue; + case TEXT: + if (ci->x > x) + continue; + break; + } + items++; + size += ci->used; + TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry); + free(ci); + } + ctx->skipped += size; + log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items, + size, y); + return (redundant); +} + +/* Clear to end of a collected line. */ +static int +screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x, + u_int bg) +{ + struct screen_write_collect_item *ci, *tmp; + size_t size = 0; + int redundant = 0; + u_int items = 0; + + if (TAILQ_EMPTY(&ctx->s->write_list[y].items)) + return (0); + TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) { + switch (ci->type) { + case CLEAR_START: + if (ci->x >= x) + ci->x = x; + continue; + case CLEAR_END: + if (ci->x <= x) { + if (ci->bg == bg) + redundant = 1; + continue; + } + break; + case TEXT: + if (ci->x < x) + continue; + break; + } + items++; + size += ci->used; + TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry); + free(ci); + } + ctx->skipped += size; + log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items, + size, y); + return (redundant); +} + +/* Clear collected lines. */ static void screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) { struct screen_write_collect_item *ci, *tmp; - u_int i; + struct screen_write_collect_line *cl; + u_int i, items; size_t size; for (i = y; i < y + n; i++) { - if (TAILQ_EMPTY(&ctx->list[i].items)) + if (TAILQ_EMPTY(&ctx->s->write_list[i].items)) continue; + items = 0; size = 0; - TAILQ_FOREACH_SAFE(ci, &ctx->list[i].items, entry, tmp) { + cl = &ctx->s->write_list[i]; + TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { + items++; size += ci->used; - TAILQ_REMOVE(&ctx->list[i].items, ci, entry); + TAILQ_REMOVE(&cl->items, ci, entry); free(ci); } ctx->skipped += size; - log_debug("%s: dropped %zu bytes (line %u)", __func__, size, i); + log_debug("%s: dropped %u items (%zu bytes) (line %u)", + __func__, items, size, y); } } @@ -1230,23 +1362,31 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx) struct screen *s = ctx->s; struct screen_write_collect_line *cl; u_int y; + char *saved; log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, s->rupper, s->rlower); screen_write_collect_clear(ctx, s->rupper, 1); + saved = ctx->s->write_list[s->rupper].data; for (y = s->rupper; y < s->rlower; y++) { - cl = &ctx->list[y + 1]; - TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); + cl = &ctx->s->write_list[y + 1]; + TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry); + ctx->s->write_list[y].bg = cl->bg; + ctx->s->write_list[y].data = cl->data; } + ctx->s->write_list[s->rlower].bg = 1 + 8; + ctx->s->write_list[s->rlower].data = saved; } /* Flush collected lines. */ static void -screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) +screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, + const char *from) { struct screen *s = ctx->s; struct screen_write_collect_item *ci, *tmp; + struct screen_write_collect_line *cl; u_int y, cx, cy, items = 0; struct tty_ctx ttyctx; size_t written = 0; @@ -1257,7 +1397,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) if (ctx->scrolled > s->rlower - s->rupper + 1) ctx->scrolled = s->rlower - s->rupper + 1; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 1); ttyctx.num = ctx->scrolled; ttyctx.bg = ctx->bg; tty_write(tty_cmd_scrollup, &ttyctx); @@ -1270,25 +1410,44 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) cx = s->cx; cy = s->cy; for (y = 0; y < screen_size_y(s); y++) { - TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { + cl = &ctx->s->write_list[y]; + if (cl->bg != 0) { + screen_write_set_cursor(ctx, 0, y); + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.bg = cl->bg - 1; + tty_write(tty_cmd_clearline, &ttyctx); + } + TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { screen_write_set_cursor(ctx, ci->x, y); - screen_write_initctx(ctx, &ttyctx); - ttyctx.cell = &ci->gc; - ttyctx.wrapped = ci->wrapped; - ttyctx.ptr = ci->data; - ttyctx.num = ci->used; - tty_write(tty_cmd_cells, &ttyctx); + if (ci->type == CLEAR_END) { + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.bg = ci->bg; + tty_write(tty_cmd_clearendofline, &ttyctx); + } else if (ci->type == CLEAR_START) { + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.bg = ci->bg; + tty_write(tty_cmd_clearstartofline, &ttyctx); + } else { + screen_write_initctx(ctx, &ttyctx, 0); + ttyctx.cell = &ci->gc; + ttyctx.wrapped = ci->wrapped; + ttyctx.ptr = cl->data + ci->x; + ttyctx.num = ci->used; + tty_write(tty_cmd_cells, &ttyctx); + } items++; written += ci->used; - TAILQ_REMOVE(&ctx->list[y].items, ci, entry); + TAILQ_REMOVE(&cl->items, ci, entry); free(ci); } + cl->bg = 0; } s->cx = cx; s->cy = cy; - log_debug("%s: flushed %u items (%zu bytes)", __func__, items, written); + log_debug("%s: flushed %u items (%zu bytes) (%s)", __func__, items, + written, from); ctx->written += written; } @@ -1298,19 +1457,19 @@ screen_write_collect_end(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct screen_write_collect_item *ci = ctx->item; + struct screen_write_collect_line *cl = &s->write_list[s->cy]; struct grid_cell gc; u_int xx; if (ci->used == 0) return; - ci->data[ci->used] = '\0'; ci->x = s->cx; - TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); + TAILQ_INSERT_TAIL(&cl->items, ci, entry); ctx->item = xcalloc(1, sizeof *ctx->item); - log_debug("%s: %u %s (at %u,%u)", __func__, ci->used, ci->data, s->cx, - s->cy); + log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used, + (int)ci->used, cl->data + ci->x, s->cx, s->cy); if (s->cx != 0) { for (xx = s->cx; xx > 0; xx--) { @@ -1326,7 +1485,8 @@ screen_write_collect_end(struct screen_write_ctx *ctx) } } - grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, ci->data, ci->used); + grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x, + ci->used); screen_write_set_cursor(ctx, s->cx + ci->used, -1); for (xx = s->cx; xx < screen_size_x(s); xx++) { @@ -1366,7 +1526,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx, collect = 0; if (!collect) { screen_write_collect_end(ctx); - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); screen_write_cell(ctx, gc); return; } @@ -1385,9 +1545,9 @@ screen_write_collect_add(struct screen_write_ctx *ctx, if (ci->used == 0) memcpy(&ci->gc, gc, sizeof ci->gc); - ci->data[ci->used++] = gc->data.data[0]; - if (ci->used == (sizeof ci->data) - 1) - screen_write_collect_end(ctx); + if (ctx->s->write_list[s->cy].data == NULL) + ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s)); + ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0]; } /* Write cell data. */ @@ -1411,11 +1571,11 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) /* If the width is zero, combine onto the previous character. */ if (width == 0) { - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { cx = s->cx; cy = s->cy; screen_write_set_cursor(ctx, xx, s->cy); - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); s->cx = cx; s->cy = cy; @@ -1424,7 +1584,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) } /* Flush any existing scrolling. */ - screen_write_collect_flush(ctx, 1); + screen_write_collect_flush(ctx, 1, __func__); /* If this character doesn't fit, ignore it. */ if ((~s->mode & MODE_WRAP) && @@ -1443,13 +1603,13 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); screen_write_linefeed(ctx, 1, 8); screen_write_set_cursor(ctx, 0, -1); - screen_write_collect_flush(ctx, 1); + screen_write_collect_flush(ctx, 1, __func__); } /* Sanity check cursor position. */ if (s->cx > sx - width || s->cy > sy - 1) return; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); /* Handle overwriting of UTF-8 characters. */ gl = grid_get_line(s->grid, s->grid->hsize + s->cy); @@ -1521,7 +1681,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) /* Create space for character in insert mode. */ if (s->mode & MODE_INSERT) { - screen_write_collect_flush(ctx, 0); + screen_write_collect_flush(ctx, 0, __func__); ttyctx.num = width; tty_write(tty_cmd_insertcharacter, &ttyctx); } @@ -1652,7 +1812,7 @@ screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) { struct tty_ctx ttyctx; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.ptr = str; ttyctx.num = len; @@ -1665,7 +1825,7 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) { struct tty_ctx ttyctx; - screen_write_initctx(ctx, &ttyctx); + screen_write_initctx(ctx, &ttyctx, 0); ttyctx.ptr = str; ttyctx.num = len; @@ -47,9 +47,8 @@ struct screen_title_entry { }; TAILQ_HEAD(screen_titles, screen_title_entry); -static void screen_resize_y(struct screen *, u_int); - -static void screen_reflow(struct screen *, u_int); +static void screen_resize_y(struct screen *, u_int, int, u_int *); +static void screen_reflow(struct screen *, u_int, u_int *, u_int *); /* Free titles stack. */ static void @@ -75,6 +74,8 @@ void screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) { s->grid = grid_create(sx, sy, hlimit); + s->saved_grid = NULL; + s->title = xstrdup(""); s->titles = NULL; @@ -83,6 +84,8 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->tabs = NULL; s->sel = NULL; + s->write_list = NULL; + screen_reinit(s); } @@ -98,6 +101,11 @@ screen_reinit(struct screen *s) s->mode = MODE_CURSOR | MODE_WRAP; + if (s->saved_grid != NULL) + screen_alternate_off(s, NULL, 0); + s->saved_cx = UINT_MAX; + s->saved_cy = UINT_MAX; + screen_reset_tabs(s); grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8); @@ -115,6 +123,11 @@ screen_free(struct screen *s) free(s->title); free(s->ccolour); + if (s->write_list != NULL) + screen_write_free_list(s); + + if (s->saved_grid != NULL) + grid_destroy(s->saved_grid); grid_destroy(s->grid); screen_free_titles(s); @@ -206,10 +219,28 @@ screen_pop_title(struct screen *s) } } -/* Resize screen. */ +/* Resize screen and return cursor position. */ void -screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) +screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, + int eat_empty, u_int *cx, u_int *cy) { + u_int tcx, tcy; + + if (s->write_list != NULL) + screen_write_free_list(s); + + if (cx == NULL) + cx = &tcx; + *cx = s->cx; + + if (cy == NULL) + cy = т + *cy = s->grid->hsize + s->cy; + + log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)", + __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy, + *cx, *cy); + if (sx < 1) sx = 1; if (sy < 1) @@ -222,14 +253,34 @@ screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) reflow = 0; if (sy != screen_size_y(s)) - screen_resize_y(s, sy); + screen_resize_y(s, sy, eat_empty, cy); if (reflow) - screen_reflow(s, sx); + screen_reflow(s, sx, cx, cy); + + if (*cy >= s->grid->hsize) { + s->cx = *cx; + s->cy = (*cy) - s->grid->hsize; + } else { + s->cx = 0; + s->cy = 0; + } + log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx, + s->cy, *cx, *cy); + + if (s->write_list != NULL) + screen_write_make_list(s); +} + +/* Resize screen. */ +void +screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) +{ + screen_resize_cursor(s, sx, sy, reflow, 1, NULL, NULL); } static void -screen_resize_y(struct screen *s, u_int sy) +screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy) { struct grid *gd = s->grid; u_int needed, available, oldy, i; @@ -254,14 +305,16 @@ screen_resize_y(struct screen *s, u_int sy) needed = oldy - sy; /* Delete as many lines as possible from the bottom. */ - available = oldy - 1 - s->cy; - if (available > 0) { - if (available > needed) - available = needed; - grid_view_delete_lines(gd, oldy - available, available, - 8); + if (eat_empty) { + available = oldy - 1 - s->cy; + if (available > 0) { + if (available > needed) + available = needed; + grid_view_delete_lines(gd, oldy - available, + available, 8); + } + needed -= available; } - needed -= available; /* * Now just increase the history size, if possible, to take @@ -276,8 +329,8 @@ screen_resize_y(struct screen *s, u_int sy) if (available > needed) available = needed; grid_view_delete_lines(gd, 0, available, 8); + (*cy) -= available; } - s->cy -= needed; } /* Resize line array. */ @@ -297,14 +350,13 @@ screen_resize_y(struct screen *s, u_int sy) available = needed; gd->hscrolled -= available; gd->hsize -= available; - s->cy += available; } else available = 0; needed -= available; /* Then fill the rest in with blanks. */ for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++) - memset(grid_get_line(gd, i), 0, sizeof(struct grid_line)); + grid_empty_line(gd, i, 8); } /* Set the new size, and reset the scroll region. */ @@ -472,32 +524,92 @@ screen_select_cell(struct screen *s, struct grid_cell *dst, /* Reflow wrapped lines. */ static void -screen_reflow(struct screen *s, u_int new_x) +screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy) { - u_int cx = s->cx, cy = s->grid->hsize + s->cy, wx, wy; - struct timeval start, tv; - - gettimeofday(&start, NULL); + u_int wx, wy; - grid_wrap_position(s->grid, cx, cy, &wx, &wy); - log_debug("%s: cursor %u,%u is %u,%u", __func__, cx, cy, wx, wy); + grid_wrap_position(s->grid, *cx, *cy, &wx, &wy); + log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx, wy); grid_reflow(s->grid, new_x); - grid_unwrap_position(s->grid, &cx, &cy, wx, wy); - log_debug("%s: new cursor is %u,%u", __func__, cx, cy); + grid_unwrap_position(s->grid, cx, cy, wx, wy); + log_debug("%s: new cursor is %u,%u", __func__, *cx,* cy); +} - if (cy >= s->grid->hsize) { - s->cx = cx; - s->cy = cy - s->grid->hsize; - } else { - s->cx = 0; - s->cy = 0; +/* + * Enter alternative screen mode. A copy of the visible screen is saved and the + * history is not updated. + */ +void +screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) +{ + u_int sx, sy; + + if (s->saved_grid != NULL) + return; + sx = screen_size_x(s); + sy = screen_size_y(s); + + s->saved_grid = grid_create(sx, sy, 0); + grid_duplicate_lines(s->saved_grid, 0, s->grid, screen_hsize(s), sy); + if (cursor) { + s->saved_cx = s->cx; + s->saved_cy = s->cy; + } + memcpy(&s->saved_cell, gc, sizeof s->saved_cell); + + grid_view_clear(s->grid, 0, 0, sx, sy, 8); + + s->saved_flags = s->grid->flags; + s->grid->flags &= ~GRID_HISTORY; +} + +/* Exit alternate screen mode and restore the copied grid. */ +void +screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) +{ + u_int sx, sy; + + /* + * Restore the cursor position and cell. This happens even if not + * currently in the alternate screen. + */ + if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) { + s->cx = s->saved_cx; + if (s->cx > screen_size_x(s) - 1) + s->cx = screen_size_x(s) - 1; + s->cy = s->saved_cy; + if (s->cy > screen_size_y(s) - 1) + s->cy = screen_size_y(s) - 1; + if (gc != NULL) + memcpy(gc, &s->saved_cell, sizeof *gc); } - gettimeofday(&tv, NULL); - timersub(&tv, &start, &tv); + if (s->saved_grid == NULL) + return; + sx = screen_size_x(s); + sy = screen_size_y(s); + + /* + * If the current size is bigger, temporarily resize to the old size + * before copying back. + */ + if (sy > s->saved_grid->sy) + screen_resize(s, sx, s->saved_grid->sy, 1); + + /* Restore the saved grid. */ + grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, sy); + + /* + * Turn history back on (so resize can use it) and then resize back to + * the current size. + */ + if (s->saved_flags & GRID_HISTORY) + s->grid->flags |= GRID_HISTORY; + if (sy > s->saved_grid->sy || sx != s->saved_grid->sx) + screen_resize(s, sx, sy, 1); - log_debug("%s: reflow took %llu.%06u seconds", __func__, - (unsigned long long)tv.tv_sec, (u_int)tv.tv_usec); + grid_destroy(s->saved_grid); + s->saved_grid = NULL; } diff --git a/server-client.c b/server-client.c index fe72317a..92b3a6ad 100644 --- a/server-client.c +++ b/server-client.c @@ -41,7 +41,6 @@ static void server_client_check_redraw(struct client *); static void server_client_set_title(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); -static void server_client_clear_overlay(struct client *); static void server_client_resize_event(int, short, void *); static void server_client_dispatch(struct imsg *, void *); @@ -79,8 +78,10 @@ server_client_overlay_timer(__unused int fd, __unused short events, void *data) /* Set an overlay on client. */ void -server_client_set_overlay(struct client *c, u_int delay, overlay_draw_cb drawcb, - overlay_key_cb keycb, overlay_free_cb freecb, void *data) +server_client_set_overlay(struct client *c, u_int delay, + overlay_check_cb checkcb, overlay_mode_cb modecb, + overlay_draw_cb drawcb, overlay_key_cb keycb, overlay_free_cb freecb, + void *data) { struct timeval tv; @@ -96,17 +97,21 @@ server_client_set_overlay(struct client *c, u_int delay, overlay_draw_cb drawcb, if (delay != 0) evtimer_add(&c->overlay_timer, &tv); + c->overlay_check = checkcb; + c->overlay_mode = modecb; c->overlay_draw = drawcb; c->overlay_key = keycb; c->overlay_free = freecb; c->overlay_data = data; - c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); + c->tty.flags |= TTY_FREEZE; + if (c->overlay_mode == NULL) + c->tty.flags |= TTY_NOCURSOR; server_redraw_client(c); } /* Clear overlay mode on client. */ -static void +void server_client_clear_overlay(struct client *c) { if (c->overlay_draw == NULL) @@ -118,8 +123,12 @@ server_client_clear_overlay(struct client *c) if (c->overlay_free != NULL) c->overlay_free(c); + c->overlay_check = NULL; + c->overlay_mode = NULL; c->overlay_draw = NULL; c->overlay_key = NULL; + c->overlay_free = NULL; + c->overlay_data = NULL; c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); server_redraw_client(c); @@ -199,7 +208,7 @@ server_client_create(int fd) c->fd = -1; c->cwd = NULL; - TAILQ_INIT(&c->queue); + c->queue = cmdq_new(); c->tty.fd = -1; c->title = NULL; @@ -285,7 +294,9 @@ server_client_lost(struct client *c) if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); - free(c->term); + + free(c->term_name); + free(c->term_type); status_free(c); @@ -344,8 +355,7 @@ server_client_free(__unused int fd, __unused short events, void *arg) log_debug("free client %p (%d references)", c, c->references); - if (!TAILQ_EMPTY(&c->queue)) - fatalx("queue not empty"); + cmdq_free(c->queue); if (c->references == 0) { free((void *)c->name); @@ -398,6 +408,8 @@ server_client_exec(struct client *c, const char *cmd) shell = options_get_string(s->options, "default-shell"); else shell = options_get_string(global_s_options, "default-shell"); + if (!checkshell(shell)) + shell = _PATH_BSHELL; shellsize = strlen(shell) + 1; msg = xmalloc(cmdsize + shellsize); @@ -417,7 +429,7 @@ server_client_check_mouse(struct client *c, struct key_event *event) struct winlink *wl; struct window_pane *wp; u_int x, y, b, sx, sy, px, py; - int flag; + int ignore = 0; key_code key; struct timeval tv; struct style_range *sr; @@ -427,6 +439,7 @@ server_client_check_mouse(struct client *c, struct key_event *event) UP, DRAG, WHEEL, + SECOND, DOUBLE, TRIPLE } type = NOTYPE; enum { NOWHERE, @@ -441,7 +454,12 @@ server_client_check_mouse(struct client *c, struct key_event *event) m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag); /* What type of event is this? */ - if ((m->sgr_type != ' ' && + if (event->key == KEYC_DOUBLECLICK) { + type = DOUBLE; + x = m->x, y = m->y, b = m->b; + ignore = 1; + log_debug("double-click at %u,%u", x, y); + } else if ((m->sgr_type != ' ' && MOUSE_DRAG(m->sgr_b) && MOUSE_BUTTONS(m->sgr_b) == 3) || (m->sgr_type == ' ' && @@ -475,11 +493,10 @@ server_client_check_mouse(struct client *c, struct key_event *event) evtimer_del(&c->click_timer); c->flags &= ~CLIENT_DOUBLECLICK; if (m->b == c->click_button) { - type = DOUBLE; + type = SECOND; x = m->x, y = m->y, b = m->b; - log_debug("double-click at %u,%u", x, y); - flag = CLIENT_TRIPLECLICK; - goto add_timer; + log_debug("second-click at %u,%u", x, y); + c->flags |= CLIENT_TRIPLECLICK; } } else if (c->flags & CLIENT_TRIPLECLICK) { evtimer_del(&c->click_timer); @@ -490,18 +507,18 @@ server_client_check_mouse(struct client *c, struct key_event *event) log_debug("triple-click at %u,%u", x, y); goto have_event; } + } else { + type = DOWN; + x = m->x, y = m->y, b = m->b; + log_debug("down at %u,%u", x, y); + c->flags |= CLIENT_DOUBLECLICK; } - type = DOWN; - x = m->x, y = m->y, b = m->b; - log_debug("down at %u,%u", x, y); - flag = CLIENT_DOUBLECLICK; - - add_timer: if (KEYC_CLICK_TIMEOUT != 0) { - c->flags |= flag; + memcpy(&c->click_event, m, sizeof c->click_event); c->click_button = m->b; + log_debug("click timer started"); tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000; tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L; evtimer_del(&c->click_timer); @@ -516,6 +533,7 @@ have_event: /* Save the session. */ m->s = s->id; m->w = -1; + m->ignore = ignore; /* Is this on the status line? */ m->statusat = status_at_line(c); @@ -587,10 +605,9 @@ have_event: wp = window_get_active_at(s->curw->window, px, py); if (wp != NULL) where = PANE; + else + return (KEYC_UNKNOWN); } - - if (where == NOWHERE) - return (KEYC_UNKNOWN); if (where == PANE) log_debug("mouse %u,%u on pane %%%u", x, y, wp->id); else if (where == BORDER) @@ -859,6 +876,52 @@ have_event: break; } break; + case SECOND: + switch (MOUSE_BUTTONS(b)) { + case 0: + if (where == PANE) + key = KEYC_SECONDCLICK1_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK1_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK1_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK1_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK1_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK1_BORDER; + break; + case 1: + if (where == PANE) + key = KEYC_SECONDCLICK2_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK2_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK2_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK2_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK2_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK2_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_SECONDCLICK3_PANE; + if (where == STATUS) + key = KEYC_SECONDCLICK3_STATUS; + if (where == STATUS_LEFT) + key = KEYC_SECONDCLICK3_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_SECONDCLICK3_STATUS_RIGHT; + if (where == STATUS_DEFAULT) + key = KEYC_SECONDCLICK3_STATUS_DEFAULT; + if (where == BORDER) + key = KEYC_SECONDCLICK3_BORDER; + break; + } + break; case DOUBLE: switch (MOUSE_BUTTONS(b)) { case 0: @@ -1018,7 +1081,7 @@ server_client_update_latest(struct client *c) static enum cmd_retval server_client_key_callback(struct cmdq_item *item, void *data) { - struct client *c = item->client; + struct client *c = cmdq_get_client(item); struct key_event *event = data; key_code key = event->key; struct mouse_event *m = &event->m; @@ -1045,7 +1108,7 @@ server_client_key_callback(struct cmdq_item *item, void *data) /* Check for mouse keys. */ m->valid = 0; - if (key == KEYC_MOUSE) { + if (key == KEYC_MOUSE || key == KEYC_DOUBLECLICK) { if (c->flags & CLIENT_READONLY) goto out; key = server_client_check_mouse(c, event); @@ -1160,7 +1223,7 @@ try_again: server_status_client(c); /* Execute the key binding. */ - key_bindings_dispatch(bd, item, c, m, &fs); + key_bindings_dispatch(bd, item, c, event, &fs); key_bindings_unref_table(table); goto out; } @@ -1382,7 +1445,7 @@ server_client_resize_event(__unused int fd, __unused short events, void *data) log_debug("%s: %%%u timer fired (was%s resized)", __func__, wp->id, (wp->flags & PANE_RESIZED) ? "" : " not"); - if (wp->saved_grid == NULL && (wp->flags & PANE_RESIZED)) { + if (wp->base.saved_grid == NULL && (wp->flags & PANE_RESIZED)) { log_debug("%s: %%%u deferring timer", __func__, wp->id); server_client_start_resize_timer(wp); } else if (!server_client_resize_force(wp)) { @@ -1474,38 +1537,57 @@ focused: static void server_client_reset_state(struct client *c) { + struct tty *tty = &c->tty; struct window *w = c->session->curw->window; struct window_pane *wp = w->active, *loop; - struct screen *s = wp->screen; + struct screen *s; struct options *oo = c->session->options; - int mode, cursor = 0; + int mode, cursor, flags; u_int cx = 0, cy = 0, ox, oy, sx, sy; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; - if (c->overlay_draw != NULL) - return; - mode = s->mode; - tty_region_off(&c->tty); - tty_margin_off(&c->tty); + /* Disable the block flag. */ + flags = (tty->flags & TTY_BLOCK); + tty->flags &= ~TTY_BLOCK; - /* Move cursor to pane cursor and offset. */ - cursor = 0; - tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); - if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx && - wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) { - cursor = 1; + /* Get mode from overlay if any, else from screen. */ + if (c->overlay_draw != NULL) { + s = NULL; + if (c->overlay_mode == NULL) + mode = 0; + else + mode = c->overlay_mode(c, &cx, &cy); + } else { + s = wp->screen; + mode = s->mode; + } + log_debug("%s: client %s mode %x", __func__, c->name, mode); - cx = wp->xoff + s->cx - ox; - cy = wp->yoff + s->cy - oy; + /* Reset region and margin. */ + tty_region_off(tty); + tty_margin_off(tty); - if (status_at_line(c) == 0) - cy += status_line_size(c); + /* Move cursor to pane cursor and offset. */ + if (c->overlay_draw == NULL) { + cursor = 0; + tty_window_offset(tty, &ox, &oy, &sx, &sy); + if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx && + wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) { + cursor = 1; + + cx = wp->xoff + s->cx - ox; + cy = wp->yoff + s->cy - oy; + + if (status_at_line(c) == 0) + cy += status_line_size(c); + } + if (!cursor) + mode &= ~MODE_CURSOR; } - if (!cursor) - mode &= ~MODE_CURSOR; - tty_cursor(&c->tty, cx, cy); + log_debug("%s: cursor to %u,%u", __func__, cx, cy); + tty_cursor(tty, cx, cy); /* * Set mouse mode if requested. To support dragging, always use button @@ -1513,21 +1595,27 @@ server_client_reset_state(struct client *c) */ if (options_get_number(oo, "mouse")) { mode &= ~ALL_MOUSE_MODES; - TAILQ_FOREACH(loop, &w->panes, entry) { - if (loop->screen->mode & MODE_MOUSE_ALL) - mode |= MODE_MOUSE_ALL; + if (c->overlay_draw == NULL) { + TAILQ_FOREACH(loop, &w->panes, entry) { + if (loop->screen->mode & MODE_MOUSE_ALL) + mode |= MODE_MOUSE_ALL; + } } if (~mode & MODE_MOUSE_ALL) mode |= MODE_MOUSE_BUTTON; } /* Clear bracketed paste mode if at the prompt. */ - if (c->prompt_string != NULL) + if (c->overlay_draw == NULL && c->prompt_string != NULL) mode &= ~MODE_BRACKETPASTE; /* Set the terminal mode and reset attributes. */ - tty_update_mode(&c->tty, mode, s); - tty_reset(&c->tty); + tty_update_mode(tty, mode, s); + tty_reset(tty); + + /* All writing must be done, send a sync end (if it was started). */ + tty_sync_end(tty); + tty->flags |= flags; } /* Repeat time callback. */ @@ -1547,8 +1635,22 @@ server_client_repeat_timer(__unused int fd, __unused short events, void *data) static void server_client_click_timer(__unused int fd, __unused short events, void *data) { - struct client *c = data; + struct client *c = data; + struct key_event *event; + log_debug("click timer expired"); + + if (c->flags & CLIENT_TRIPLECLICK) { + /* + * Waiting for a third click that hasn't happened, so this must + * have been a double click. + */ + event = xmalloc(sizeof *event); + event->key = KEYC_DOUBLECLICK; + memcpy(&event->m, &c->click_event, sizeof event->m); + if (!server_client_handle_key(c, event)) + free(event); + } c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK); } @@ -1588,8 +1690,11 @@ server_client_check_redraw(struct client *c) { struct session *s = c->session; struct tty *tty = &c->tty; + struct window *w = c->session->curw->window; struct window_pane *wp; - int needed, flags; + int needed, flags, mode = tty->mode, new_flags = 0; + int redraw; + u_int bit = 0; struct timeval tv = { .tv_usec = 1000 }; static struct event ev; size_t left; @@ -1597,11 +1702,12 @@ server_client_check_redraw(struct client *c) if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; if (c->flags & CLIENT_ALLREDRAWFLAGS) { - log_debug("%s: redraw%s%s%s%s", c->name, + log_debug("%s: redraw%s%s%s%s%s", c->name, (c->flags & CLIENT_REDRAWWINDOW) ? " window" : "", (c->flags & CLIENT_REDRAWSTATUS) ? " status" : "", (c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "", - (c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : ""); + (c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "", + (c->flags & CLIENT_REDRAWPANES) ? " panes" : ""); } /* @@ -1613,12 +1719,14 @@ server_client_check_redraw(struct client *c) if (c->flags & CLIENT_ALLREDRAWFLAGS) needed = 1; else { - TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { + TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->flags & PANE_REDRAW) { needed = 1; break; } } + if (needed) + new_flags |= CLIENT_REDRAWPANES; } if (needed && (left = EVBUFFER_LENGTH(tty->out)) != 0) { log_debug("%s: redraw deferred (%zu left)", c->name, left); @@ -1629,29 +1737,53 @@ server_client_check_redraw(struct client *c) evtimer_add(&ev, &tv); } - /* - * We may have got here for a single pane redraw, but force a - * full redraw next time in case other panes have been updated. - */ - c->flags |= CLIENT_ALLREDRAWFLAGS; + if (~c->flags & CLIENT_REDRAWWINDOW) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->flags & PANE_REDRAW) { + log_debug("%s: pane %%%u needs redraw", + c->name, wp->id); + c->redraw_panes |= (1 << bit); + } + if (++bit == 64) { + /* + * If more that 64 panes, give up and + * just redraw the window. + */ + new_flags &= CLIENT_REDRAWPANES; + new_flags |= CLIENT_REDRAWWINDOW; + break; + } + } + if (c->redraw_panes != 0) + c->flags |= CLIENT_REDRAWPANES; + } + c->flags |= new_flags; return; } else if (needed) log_debug("%s: redraw needed", c->name); flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR); - tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE)) | TTY_NOCURSOR; + tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE))|TTY_NOCURSOR; if (~c->flags & CLIENT_REDRAWWINDOW) { /* * If not redrawing the entire window, check whether each pane * needs to be redrawn. */ - TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { - if (wp->flags & PANE_REDRAW) { - tty_update_mode(tty, tty->mode, NULL); - screen_redraw_pane(c, wp); - } + TAILQ_FOREACH(wp, &w->panes, entry) { + redraw = 0; + if (wp->flags & PANE_REDRAW) + redraw = 1; + else if (c->flags & CLIENT_REDRAWPANES) + redraw = !!(c->redraw_panes & (1 << bit)); + bit++; + if (!redraw) + continue; + log_debug("%s: redrawing pane %%%u", __func__, wp->id); + screen_redraw_pane(c, wp); } + c->redraw_panes = 0; + c->flags &= ~CLIENT_REDRAWPANES; } if (c->flags & CLIENT_ALLREDRAWFLAGS) { @@ -1660,8 +1792,9 @@ server_client_check_redraw(struct client *c) screen_redraw_screen(c); } - tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags; - tty_update_mode(tty, tty->mode, NULL); + tty->flags = (tty->flags & ~TTY_NOCURSOR)|(flags & TTY_NOCURSOR); + tty_update_mode(tty, mode, NULL); + tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR))|flags; c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_STATUSFORCE); @@ -1720,6 +1853,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { + case MSG_IDENTIFY_FEATURES: case MSG_IDENTIFY_FLAGS: case MSG_IDENTIFY_TERM: case MSG_IDENTIFY_TTYNAME: @@ -1800,10 +1934,12 @@ server_client_dispatch(struct imsg *imsg, void *arg) static enum cmd_retval server_client_command_done(struct cmdq_item *item, __unused void *data) { - struct client *c = item->client; + struct client *c = cmdq_get_client(item); if (~c->flags & CLIENT_ATTACHED) c->flags |= CLIENT_EXIT; + else if (~c->flags & CLIENT_DETACHING) + tty_send_requests(&c->tty); return (CMD_RETURN_NORMAL); } @@ -1855,7 +1991,7 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg) } cmd_free_argv(argc, argv); - cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL, NULL, 0)); + cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL)); cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL)); cmd_list_free(pr->cmdlist); @@ -1876,7 +2012,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) { const char *data, *home; size_t datalen; - int flags; + int flags, feat; char *name; if (c->flags & CLIENT_IDENTIFIED) @@ -1886,6 +2022,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { + case MSG_IDENTIFY_FEATURES: + if (datalen != sizeof feat) + fatalx("bad MSG_IDENTIFY_FEATURES size"); + memcpy(&feat, data, sizeof feat); + c->term_features |= feat; + log_debug("client %p IDENTIFY_FEATURES %s", c, + tty_get_features(feat)); + break; case MSG_IDENTIFY_FLAGS: if (datalen != sizeof flags) fatalx("bad MSG_IDENTIFY_FLAGS size"); @@ -1896,7 +2040,10 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) case MSG_IDENTIFY_TERM: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TERM string"); - c->term = xstrdup(data); + if (*data == '\0') + c->term_name = xstrdup("unknown"); + else + c->term_name = xstrdup(data); log_debug("client %p IDENTIFY_TERM %s", c, data); break; case MSG_IDENTIFY_TTYNAME: @@ -1926,7 +2073,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); if (strchr(data, '=') != NULL) - environ_put(c->environ, data); + environ_put(c->environ, data, 0); log_debug("client %p IDENTIFY_ENVIRON %s", c, data); break; case MSG_IDENTIFY_CLIENTPID: @@ -1961,14 +2108,10 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) control_start(c); c->tty.fd = -1; } else if (c->fd != -1) { - if (tty_init(&c->tty, c, c->fd, c->term) != 0) { + if (tty_init(&c->tty, c, c->fd) != 0) { close(c->fd); c->fd = -1; } else { - if (c->flags & CLIENT_UTF8) - c->tty.flags |= TTY_UTF8; - if (c->flags & CLIENT_256COLOURS) - c->tty.term_flags |= TERM_256COLOURS; tty_resize(&c->tty); c->flags |= CLIENT_TERMINAL; } @@ -1992,7 +2135,7 @@ server_client_dispatch_shell(struct client *c) const char *shell; shell = options_get_string(global_s_options, "default-shell"); - if (*shell == '\0' || areshell(shell)) + if (!checkshell(shell)) shell = _PATH_BSHELL; proc_send(c->peer, MSG_SHELL, -1, shell, strlen(shell) + 1); diff --git a/server-fn.c b/server-fn.c index 2247f1c5..815d25e2 100644 --- a/server-fn.c +++ b/server-fn.c @@ -335,8 +335,8 @@ server_destroy_pane(struct window_pane *wp, int notify) tim); } else if (WIFSIGNALED(wp->status)) { screen_write_nputs(&ctx, -1, &gc, - "Pane is dead (signal %d, %s)", - WTERMSIG(wp->status), + "Pane is dead (signal %s, %s)", + sig2name(WTERMSIG(wp->status)), tim); } @@ -44,6 +44,7 @@ struct clients clients; struct tmuxproc *server_proc; static int server_fd = -1; +static int server_client_flags; static int server_exit; static struct event server_ev_accept; @@ -97,7 +98,7 @@ server_check_marked(void) /* Create server socket. */ static int -server_create_socket(char **cause) +server_create_socket(int flags, char **cause) { struct sockaddr_un sa; size_t size; @@ -116,7 +117,10 @@ server_create_socket(char **cause) if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) goto fail; - mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); + if (flags & CLIENT_DEFAULTSOCKET) + mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); + else + mask = umask(S_IXUSR|S_IRWXG|S_IRWXO); if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { saved_errno = errno; close(fd); @@ -145,8 +149,8 @@ fail: /* Fork new server. */ int -server_start(struct tmuxproc *client, struct event_base *base, int lockfd, - char *lockfile) +server_start(struct tmuxproc *client, int flags, struct event_base *base, + int lockfd, char *lockfile) { int pair[2]; sigset_t set, oldset; @@ -155,6 +159,7 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd, if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) fatal("socketpair failed"); + server_client_flags = flags; sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); @@ -192,7 +197,7 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd, gettimeofday(&start_time, NULL); - server_fd = server_create_socket(&cause); + server_fd = server_create_socket(flags, &cause); if (server_fd != -1) server_update_socket(); c = server_client_create(pair[1]); @@ -395,7 +400,7 @@ server_signal(int sig) break; case SIGUSR1: event_del(&server_ev_accept); - fd = server_create_socket(NULL); + fd = server_create_socket(server_client_flags, NULL); if (fd != -1) { close(server_fd); server_fd = fd; @@ -122,7 +122,6 @@ session_create(const char *prefix, const char *name, const char *cwd, s->cwd = xstrdup(cwd); - s->curw = NULL; TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); @@ -141,7 +140,6 @@ session_create(const char *prefix, const char *name, const char *cwd, s->name = xstrdup(name); s->id = next_session_id++; } else { - s->name = NULL; do { s->id = next_session_id++; free(s->name); @@ -231,11 +229,20 @@ session_destroy(struct session *s, int notify, const char *from) session_remove_ref(s, __func__); } -/* Check a session name is valid: not empty and no colons or periods. */ -int +/* Sanitize session name. */ +char * session_check_name(const char *name) { - return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); + char *copy, *cp, *new_name; + + copy = xstrdup(name); + for (cp = copy; *cp != '\0'; cp++) { + if (*cp == ':' || *cp == '.') + *cp = '_'; + } + utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); + free(copy); + return (new_name); } /* Lock session if it has timed out. */ @@ -555,6 +562,7 @@ session_group_remove(struct session *s) TAILQ_REMOVE(&sg->sessions, s, gentry); if (TAILQ_EMPTY(&sg->sessions)) { RB_REMOVE(session_groups, &session_groups, sg); + free((void *)sg->name); free(sg); } } @@ -54,10 +54,10 @@ spawn_log(const char *from, struct spawn_context *sc) struct session *s = sc->s; struct winlink *wl = sc->wl; struct window_pane *wp0 = sc->wp0; + const char *name = cmdq_get_name(sc->item); char tmp[128]; - const char *name; - log_debug("%s: %s, flags=%#x", from, sc->item->name, sc->flags); + log_debug("%s: %s, flags=%#x", from, name, sc->flags); if (wl != NULL && wp0 != NULL) xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id); @@ -68,18 +68,14 @@ spawn_log(const char *from, struct spawn_context *sc) else xsnprintf(tmp, sizeof tmp, "wl=none wp0=none"); log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx); - - name = sc->name; - if (name == NULL) - name = "none"; - log_debug("%s: name=%s", from, name); + log_debug("%s: name=%s", from, sc->name == NULL ? "none" : sc->name); } struct winlink * spawn_window(struct spawn_context *sc, char **cause) { struct cmdq_item *item = sc->item; - struct client *c = item->client; + struct client *c = cmdq_get_client(item); struct session *s = sc->s; struct window *w; struct window_pane *wp; @@ -155,7 +151,7 @@ spawn_window(struct spawn_context *sc, char **cause) xasprintf(cause, "couldn't add window %d", idx); return (NULL); } - default_window_size(sc->c, s, NULL, &sx, &sy, &xpixel, &ypixel, + default_window_size(sc->tc, s, NULL, &sx, &sy, &xpixel, &ypixel, -1); if ((w = window_create(sx, sy, xpixel, ypixel)) == NULL) { winlink_remove(&s->windows, sc->wl); @@ -165,7 +161,7 @@ spawn_window(struct spawn_context *sc, char **cause) if (s->curw == NULL) s->curw = sc->wl; sc->wl->session = s; - w->latest = sc->c; + w->latest = sc->tc; winlink_set_window(sc->wl, w); } else w = NULL; @@ -205,7 +201,8 @@ struct window_pane * spawn_pane(struct spawn_context *sc, char **cause) { struct cmdq_item *item = sc->item; - struct client *c = item->client; + struct cmd_find_state *target = cmdq_get_target(item); + struct client *c = cmdq_get_client(item); struct session *s = sc->s; struct window *w = sc->wl->window; struct window_pane *new_wp; @@ -228,9 +225,9 @@ spawn_pane(struct spawn_context *sc, char **cause) * the pane's stored one unless specified. */ if (sc->cwd != NULL) - cwd = format_single(item, sc->cwd, c, item->target.s, NULL, NULL); + cwd = format_single(item, sc->cwd, c, target->s, NULL, NULL); else if (~sc->flags & SPAWN_RESPAWN) - cwd = xstrdup(server_client_get_cwd(c, item->target.s)); + cwd = xstrdup(server_client_get_cwd(c, target->s)); else cwd = NULL; @@ -253,7 +250,8 @@ spawn_pane(struct spawn_context *sc, char **cause) } window_pane_reset_mode_all(sc->wp0); screen_reinit(&sc->wp0->base); - input_init(sc->wp0); + input_free(sc->wp0->ictx); + sc->wp0->ictx = NULL; new_wp = sc->wp0; new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN); } else if (sc->lc == NULL) { @@ -300,7 +298,7 @@ spawn_pane(struct spawn_context *sc, char **cause) child = environ_for_session(s, 0); if (sc->environ != NULL) environ_copy(sc->environ, child); - environ_set(child, "TMUX_PANE", "%%%u", new_wp->id); + environ_set(child, "TMUX_PANE", 0, "%%%u", new_wp->id); /* * Then the PATH environment variable. The session one is replaced from @@ -310,20 +308,20 @@ spawn_pane(struct spawn_context *sc, char **cause) if (c != NULL && c->session == NULL) { /* only unattached clients */ ee = environ_find(c->environ, "PATH"); if (ee != NULL) - environ_set(child, "PATH", "%s", ee->value); + environ_set(child, "PATH", 0, "%s", ee->value); } if (environ_find(child, "PATH") == NULL) - environ_set(child, "%s", _PATH_DEFPATH); + environ_set(child, "PATH", 0, "%s", _PATH_DEFPATH); /* Then the shell. If respawning, use the old one. */ if (~sc->flags & SPAWN_RESPAWN) { tmp = options_get_string(s->options, "default-shell"); - if (*tmp == '\0' || areshell(tmp)) + if (!checkshell(tmp)) tmp = _PATH_BSHELL; free(new_wp->shell); new_wp->shell = xstrdup(tmp); } - environ_set(child, "SHELL", "%s", new_wp->shell); + environ_set(child, "SHELL", 0, "%s", new_wp->shell); /* Log the arguments we are going to use. */ log_debug("%s: shell=%s", __func__, new_wp->shell); @@ -366,6 +364,7 @@ spawn_pane(struct spawn_context *sc, char **cause) window_remove_pane(w, new_wp); } sigprocmask(SIG_SETMASK, &oldset, NULL); + environ_free(child); return (NULL); } @@ -452,6 +451,8 @@ complete: sigprocmask(SIG_SETMASK, &oldset, NULL); window_pane_set_event(new_wp); + environ_free(child); + if (sc->flags & SPAWN_RESPAWN) return (new_wp); if ((~sc->flags & SPAWN_DETACHED) || w->active == NULL) { @@ -733,6 +733,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) if (c->prompt_mode == PROMPT_ENTRY) { switch (key) { case '\003': /* C-c */ + case '\007': /* C-g */ case '\010': /* C-h */ case '\011': /* Tab */ case '\025': /* C-u */ @@ -813,6 +814,9 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) case 'p': *new_key = '\031'; /* C-y */ return (1); + case 'q': + *new_key = '\003'; /* C-c */ + return (1); case 's': case KEYC_DC: case 'x': @@ -26,7 +26,7 @@ #include "tmux.h" /* Mask for bits not included in style. */ -#define STYLE_ATTR_MASK (~GRID_ATTR_CHARSET) +#define STYLE_ATTR_MASK (~0) /* Default style. */ static struct style style_default = { @@ -247,7 +247,7 @@ style_tostring(struct style *sy) colour_tostring(gc->bg)); comma = ","; } - if (gc->attr != 0 && gc->attr != GRID_ATTR_CHARSET) { + if (gc->attr != 0) { xsnprintf(s + off, sizeof s - off, "%s%s", comma, attributes_tostring(gc->attr)); comma = ","; @@ -28,6 +28,7 @@ .Op Fl f Ar file .Op Fl L Ar socket-name .Op Fl S Ar socket-path +.Op Fl T Ar features .Op Ar command Op Ar flags .Ek .Sh DESCRIPTION @@ -98,6 +99,8 @@ The options are as follows: Force .Nm to assume the terminal supports 256 colours. +This is equivalent to +.Fl T Ar 256 . .It Fl C Start in control mode (see the .Sx CONTROL MODE @@ -186,6 +189,14 @@ that is set does not contain .Qq UTF-8 or .Qq UTF8 . +This is equivalent to +.Fl T Ar UTF-8 . +.It Fl T Ar features +Set terminal features for the client. +This is a comma-separated list of features. +See the +.Ic terminal-features +option. .It Fl v Request verbose logging. Log messages will be saved into @@ -565,6 +576,18 @@ Environment variables may be set by using the syntax for example .Ql HOME=/home/user . Variables set during parsing are added to the global environment. +A hidden variable may be set with +.Ql %hidden , +for example: +.Bd -literal -offset indent +%hidden MYVAR=42 +.Ed +.Pp +Hidden variables are not passed to the environment of processes created +by tmux. +See the +.Sx GLOBAL AND SESSION ENVIRONMENT +section. .Pp Commands may be parsed conditionally by surrounding them with .Ql %if , @@ -1043,12 +1066,18 @@ List the syntax of .Ar command or - if omitted - of all commands supported by .Nm . -.It Ic list-sessions Op Fl F Ar format +.It Xo Ic list-sessions +.Op Fl F Ar format +.Op Fl f Ar filter +.Xc .D1 (alias: Ic ls ) List all sessions managed by the server. -For the meaning of the .Fl F -flag, see the +specifies the format of each line and +.Fl f +a filter. +Only sessions for which the filter is true are shown. +See the .Sx FORMATS section. .It Ic lock-client Op Fl t Ar target-client @@ -1413,6 +1442,10 @@ This mode is entered with the command, bound to .Ql \&[ by default. +Copied text can be pasted with the +.Ic paste-buffer +command, bound to +.Ql \&] . .It View mode, which is like copy mode but is entered when a command that produces output, such as @@ -1461,9 +1494,9 @@ The following commands are supported in copy mode: .It Li "clear-selection" Ta "Escape" Ta "C-g" .It Li "copy-end-of-line [<prefix>]" Ta "D" Ta "C-k" .It Li "copy-line [<prefix>]" Ta "" Ta "" -.It Li "copy-pipe <command> [<prefix>]" Ta "" Ta "" -.It Li "copy-pipe-no-clear <command> [<prefix>]" Ta "" Ta "" -.It Li "copy-pipe-and-cancel <command> [<prefix>]" Ta "" Ta "" +.It Li "copy-pipe [<command>] [<prefix>]" Ta "" Ta "" +.It Li "copy-pipe-no-clear [<command>] [<prefix>]" Ta "" Ta "" +.It Li "copy-pipe-and-cancel [<command>] [<prefix>]" Ta "" Ta "" .It Li "copy-selection [<prefix>]" Ta "" Ta "" .It Li "copy-selection-no-clear [<prefix>]" Ta "" Ta "" .It Li "copy-selection-and-cancel [<prefix>]" Ta "Enter" Ta "M-w" @@ -1501,6 +1534,7 @@ The following commands are supported in copy mode: .It Li "previous-space" Ta "B" Ta "" .It Li "previous-word" Ta "b" Ta "M-b" .It Li "rectangle-toggle" Ta "v" Ta "R" +.It Li "refresh-from-pane" Ta "r" Ta "r" .It Li "scroll-down" Ta "C-e" Ta "C-Down" .It Li "scroll-down-and-cancel" Ta "" Ta "" .It Li "scroll-up" Ta "C-y" Ta "C-Up" @@ -1591,6 +1625,7 @@ command is: .Bl -tag -width Ds .It Xo Ic copy-mode .Op Fl eHMqu +.Op Fl s Ar src-pane .Op Fl t Ar target-pane .Xc Enter copy mode. @@ -1604,6 +1639,11 @@ begins a mouse drag (only valid if bound to a mouse key binding, see hides the position indicator in the top right. .Fl q cancels copy mode and any other modes. +.Fl s +copies from +.Ar src-pane +instead of +.Ar target-pane . .Pp .Fl e specifies that scrolling to the bottom of the history (to the visible screen) @@ -1674,7 +1714,7 @@ from which the layout was originally defined. Commands related to windows and panes are as follows: .Bl -tag -width Ds .It Xo Ic break-pane -.Op Fl dP +.Op Fl adP .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar src-pane @@ -1685,6 +1725,10 @@ Break .Ar src-pane off from its containing window to make it the only pane in .Ar dst-window . +With +.Fl a , +the window is moved to the next index up (following windows +are moved if necessary). If .Fl d is given, the new window does not become the current window. @@ -1833,12 +1877,15 @@ The following keys may be used in tree mode: .It Li "<" Ta "Scroll list of previews left" .It Li ">" Ta "Scroll list of previews right" .It Li "C-s" Ta "Search by name" +.It Li "m" Ta "Set the marked pane" +.It Li "M" Ta "Clear the marked pane" .It Li "n" Ta "Repeat last search" .It Li "t" Ta "Toggle if item is tagged" .It Li "T" Ta "Tag no items" .It Li "C-t" Ta "Tag all items" .It Li "\&:" Ta "Run a command for each tagged item" .It Li "f" Ta "Enter a format to filter items" +.It Li "H" Ta "Jump to the starting pane" .It Li "O" Ta "Change sort field" .It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" @@ -2039,6 +2086,7 @@ is given, the newly linked window is not selected. .It Xo Ic list-panes .Op Fl as .Op Fl F Ar format +.Op Fl f Ar filter .Op Fl t Ar target .Xc .D1 (alias: Ic lsp ) @@ -2055,14 +2103,18 @@ is a session (or the current session). If neither is given, .Ar target is a window (or the current window). -For the meaning of the .Fl F -flag, see the +specifies the format of each line and +.Fl f +a filter. +Only panes for which the filter is true are shown. +See the .Sx FORMATS section. .It Xo Ic list-windows .Op Fl a .Op Fl F Ar format +.Op Fl f Ar filter .Op Fl t Ar target-session .Xc .D1 (alias: Ic lsw ) @@ -2071,25 +2123,23 @@ If is given, list all windows on the server. Otherwise, list windows in the current session or in .Ar target-session . -For the meaning of the .Fl F -flag, see the +specifies the format of each line and +.Fl f +a filter. +Only windows for which the filter is true are shown. +See the .Sx FORMATS section. .It Xo Ic move-pane -.Op Fl bdhv +.Op Fl bdfhv .Op Fl l Ar size .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc .D1 (alias: Ic movep ) -Like -.Ic join-pane , -but -.Ar src-pane -and -.Ar dst-pane -may belong to the same window. +Does the same as +.Ic join-pane . .It Xo Ic move-window .Op Fl ardk .Op Fl s Ar src-window @@ -2264,7 +2314,7 @@ Rename the current window, or the window at if specified, to .Ar new-name . .It Xo Ic resize-pane -.Op Fl DLMRUZ +.Op Fl DLMRTUZ .Op Fl t Ar target-pane .Op Fl x Ar width .Op Fl y Ar height @@ -2303,6 +2353,10 @@ and unzoomed (its normal position in the layout). .Fl M begins mouse resizing (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . +.Pp +.Fl T +trims all lines below the current cursor position and moves lines out of the +history to replace them. .It Xo Ic resize-window .Op Fl aADLRU .Op Fl t Ar target-window @@ -2661,7 +2715,7 @@ Commands related to key bindings are as follows: .Op Fl nr .Op Fl N Ar note .Op Fl T Ar key-table -.Ar key Ar command Op Ar arguments +.Ar key command Op Ar arguments .Xc .D1 (alias: Ic bind ) Bind key @@ -3049,6 +3103,10 @@ be set to .Ql screen , .Ql tmux or a derivative of them. +.It Ic copy-command Ar shell-command +Give the command to pipe to if the +.Ic copy-pipe +copy mode command is used without arguments. .It Ic escape-time Ar time Set the time in milliseconds for which .Nm @@ -3121,6 +3179,63 @@ disallowedWindowOps: 20,21,SetXprop Or changing this property from the .Xr xterm 1 interactive menu when required. +.It Ic terminal-features[] Ar string +Set terminal features for terminal types read from +.Xr terminfo 5 . +.Nm +has a set of named terminal features. +Each will apply appropriate changes to the +.Xr terminfo 5 +entry in use. +.Pp +.Nm +can detect features for a few common terminals; this option can be used to +easily tell tmux about features supported by terminals it cannot detect. +The +.Ic terminal-overrides +option allows individual +.Xr terminfo 5 +capabilities to be set instead, +.Ic terminal-features +is intended for classes of functionality supported in a standard way but not +reported by +.Xr terminfo 5 . +Care must be taken only to configure this with features the terminal actually +support. +.Pp +This is an array option where each entry is a colon-separated string made up +of a terminal type pattern (matched using +.Xr fnmatch 3 ) +followed by a list of terminal features. +The available features are: +.Bl -tag -width Ds +.It 256 +Supports 256 colours with the SGR escape sequences. +.It clipboard +Allows setting the system clipboard. +.It ccolour +Allows setting the cursor colour. +.It cstyle +Allows setting the cursor style. +.It margins +Supports DECSLRM margins. +.It overline +Supports the overline SGR attribute. +.It rectfill +Supports the DECFRA rectangle fill escape sequence. +.It RGB +Supports RGB colour with the SGR escape sequences. +.It sync +Supports synchronized updates. +.It title +Supports +.Xr xterm 1 +title setting. +.It usstyle +Allows underscore style and colour to be set. +.It UTF-8 +Is able to handle UTF-8 output. +.El .It Ic terminal-overrides[] Ar string Allow terminal descriptions read using .Xr terminfo 5 @@ -3291,6 +3406,9 @@ with .Fl np . .It Ic message-command-style Ar style Set status line message command style. +This is used for the command prompt with +.Xr vi 1 +keys when in command mode. For how to specify .Ar style , see the @@ -3298,6 +3416,7 @@ see the section. .It Ic message-style Ar style Set status line message style. +This is used for messages and for the command prompt. For how to specify .Ar style , see the @@ -3594,6 +3713,9 @@ Set the width or height of the main (left or top) pane in the or .Ic main-vertical layouts. +If suffixed by +.Ql % , +this is a percentage of the window size. .Pp .It Xo Ic mode-keys .Op Ic vi | emacs @@ -3647,6 +3769,9 @@ and .Ic other-pane-height options are set, the main pane will grow taller to make the other panes the specified height, but will never shrink to do so. +If suffixed by +.Ql % , +this is a percentage of the window size. .Pp .It Ic other-pane-width Ar width Like @@ -3852,6 +3977,7 @@ hook and there are a number of hooks not associated with commands. .Pp Hooks are stored as array options, members of the array are executed in order when the hook is triggered. +Like options different hooks may be global or belong to a session, window or pane. Hooks may be configured with the .Ic set-hook or @@ -3944,8 +4070,8 @@ Run when a window is unlinked from a session. Hooks are managed with these commands: .Bl -tag -width Ds .It Xo Ic set-hook -.Op Fl agRu -.Op Fl t Ar target-session +.Op Fl agpRuw +.Op Fl t Ar target-pane .Ar hook-name .Ar command .Xc @@ -3957,18 +4083,8 @@ unsets) hook .Ar hook-name to .Ar command . -If -.Fl g -is given, -.Em hook-name -is added to the global list of hooks, otherwise it is added to the session -hooks (for -.Ar target-session -with -.Fl t ) . -.Fl a -appends to a hook. -Like options, session hooks inherit from the global ones. +The flags are the same as for +.Ic set-option . .Pp With .Fl R , @@ -3976,12 +4092,12 @@ run .Ar hook-name immediately. .It Xo Ic show-hooks -.Op Fl g -.Op Fl t Ar target-session +.Op Fl gpw +.Op Fl t Ar target-pane .Xc -Shows the global list of hooks with -.Fl g , -otherwise the session hooks. +Shows hooks. +The flags are the same as for +.Ic show-options . .El .Sh MOUSE SUPPORT If the @@ -4007,10 +4123,19 @@ The following mouse events are available: .It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1" .It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2" .It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3" +.It Li "SecondClick1" Ta "SecondClick2" Ta "SecondClick3" .It Li "DoubleClick1" Ta "DoubleClick2" Ta "DoubleClick3" .It Li "TripleClick1" Ta "TripleClick2" Ta "TripleClick3" .El .Pp +The +.Ql SecondClick +events are fired for the second click of a double click, even if there may be a +third click which will fire +.Ql TripleClick +instead of +.Ql DoubleClick . +.Pp Each should be suffixed with a location, for example .Ql MouseDown1Status . .Pp @@ -4137,7 +4262,7 @@ specifies an .Xr fnmatch 3 or regular expression comparison. The first argument is the pattern and the second the string to compare. -An optional third argument specifies flags: +An optional argument specifies flags: .Ql r means the pattern is a regular expression instead of the default .Xr fnmatch 3 @@ -4164,6 +4289,38 @@ ignores case. For example: .Ql #{C/r:^Start} .Pp +Numeric operators may be performed by prefixing two comma-separated alternatives with an +.Ql e +and an operator. +An optional +.Ql f +flag may be given after the operator to use floating point numbers, otherwise integers are used. +This may be followed by a number giving the number of decimal places to use for the result. +The available operators are: +addition +.Ql + , +subtraction +.Ql - , +multiplication +.Ql * , +division +.Ql / , +and modulus +.Ql m +or +.Ql % +(note that +.Ql % +must be escaped as +.Ql %% +in formats which are also expanded by +.Xr strftime 3 ) . +For example, +.Ql #{e|*|f|4:5.5,3} +multiplies 5.5 by 3 for a result with four decimal places and +.Ql #{e|%%:7,3} +returns the modulus of 7 and 3. +.Pp A limit may be placed on the length of the resultant string by prefixing it by an .Ql = , @@ -4196,6 +4353,10 @@ gives .Ql #{t:window_activity} gives .Ql Sun Oct 25 09:25:02 2015 . +Adding +.Ql p ( +.Ql `t/p` ) +will use shorter but less accurate time format for times in the past. The .Ql b:\& and @@ -4302,6 +4463,8 @@ The following variables are available, where appropriate: .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_termname" Ta "" Ta "Terminal name of client" +.It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" +.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" @@ -4369,6 +4532,7 @@ The following variables are available, where appropriate: .It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" .It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" +.It Li "pane_skipped" Ta "" Ta "Bytes skipped as not visible in pane" .It Li "pane_start_command" Ta "" Ta "Command pane started with" .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" @@ -4376,7 +4540,11 @@ The following variables are available, where appropriate: .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" -.It Li "pid" Ta "" Ta "Server PID" +.It Li "pane_written" Ta "" Ta "Bytes written by pane (aside from redrawing)" +.It Li "pid" Ta "" Ta "Server PID" +.It Li "popup_key" Ta "" Ta "Key pressed in popup" +.It Li "popup_mouse_x" Ta "" Ta "Mouse X position in popup" +.It Li "popup_mouse_y" Ta "" Ta "Mouse Y position in popup" .It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated" .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" @@ -4403,7 +4571,9 @@ The following variables are available, where appropriate: .It Li "session_id" Ta "" Ta "Unique session ID" .It Li "session_last_attached" Ta "" Ta "Time session last attached" .It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" +.It Li "session_marked" Ta "" Ta "1 if this session contains the marked pane" .It Li "session_name" Ta "#S" Ta "Name of session" +.It Li "session_path" Ta "" Ta "Working directory of session" .It Li "session_stack" Ta "" Ta "Window indexes in most recent order" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" Ta "Server socket path" @@ -4492,7 +4662,8 @@ for the terminal default colour; or a hexadecimal RGB string such as Set the background colour. .It Ic none Set no attributes (turn off any active attributes). -.It Xo Ic bright +.It Xo Ic acs , +.Ic bright (or .Ic bold ) , .Ic dim , @@ -4512,6 +4683,8 @@ Set an attribute. Any of the attributes may be prefixed with .Ql no to unset. +.Ic acs +is the terminal alternate character set. .It Xo Ic align=left (or .Ic noalign ) , @@ -4667,10 +4840,16 @@ from inside, and the variable with the correct terminal setting of .Ql screen . .Pp +Variables in both session and global environments may be marked as hidden. +Hidden variables are not passed into the environment of new processes and +instead can only be used by tmux itself (for example in formats, see the +.Sx FORMATS +section). +.Pp Commands to alter and view the environment are: .Bl -tag -width Ds .It Xo Ic set-environment -.Op Fl gru +.Op Fl hgru .Op Fl t Ar target-session .Ar name Op Ar value .Xc @@ -4687,8 +4866,10 @@ flag unsets a variable. .Fl r indicates the variable is to be removed from the environment before starting a new process. +.Fl h +marks the variable as hidden. .It Xo Ic show-environment -.Op Fl gs +.Op Fl hgs .Op Fl t Ar target-session .Op Ar variable .Xc @@ -4705,6 +4886,8 @@ Variables removed from the environment are prefixed with If .Fl s is used, the output is formatted as a set of Bourne shell commands. +.Fl h +shows hidden variables (omitted by default). .El .Sh STATUS LINE .Nm @@ -4836,7 +5019,7 @@ on the value of the option: .Bl -column "FunctionXXXXXXXXXXXXXXXXXXXXXXXXX" "viXXXX" "emacsX" -offset indent .It Sy "Function" Ta Sy "vi" Ta Sy "emacs" -.It Li "Cancel command prompt" Ta "Escape" Ta "Escape" +.It Li "Cancel command prompt" Ta "q" Ta "Escape" .It Li "Delete from cursor to start of word" Ta "" Ta "C-w" .It Li "Delete entire command" Ta "d" Ta "C-u" .It Li "Delete from cursor to end" Ta "D" Ta "C-k" @@ -4914,10 +5097,11 @@ give the position of the menu. Both may be a row or column number, or one of the following special values: .Bl -column "XXXXX" "XXXX" -offset indent .It Sy "Value" Ta Sy "Flag" Ta Sy "Meaning" +.It Li "C" Ta "Both" Ta "The centre of the terminal" .It Li "R" Ta Fl x Ta "The right side of the terminal" .It Li "P" Ta "Both" Ta "The bottom left of the pane" .It Li "M" Ta "Both" Ta "The mouse position" -.It Li "W" Ta Fl x Ta "The window position on the status line" +.It Li "W" Ta "Both" Ta "The window position on the status line" .It Li "S" Ta Fl y Ta "The line above or below the status line" .El .Pp @@ -4965,6 +5149,97 @@ lists the format variables and their values. .Fl I forwards any input read from stdin to the empty pane given by .Ar target-pane . +.It Xo Ic display-popup +.Op Fl CEK +.Op Fl c Ar target-client +.Op Fl d Ar start-directory +.Op Fl h Ar height +.Op Fl R Ar shell-command +.Op Fl t Ar target-pane +.Op Fl w Ar width +.Op Fl x Ar position +.Op Fl y Ar position +.Op Ar command Ar line Ar ... +.Xc +.D1 (alias: Ic popup ) +Display a popup on +.Ar target-client . +A popup is a rectangular box drawn over the top of any panes. +Panes are not updated while a popup is present. +The popup content may be given in two ways: +.Bl -enum -offset Ds +.It +A set of lines as arguments. +Each line is a format which is expanded using +.Ar target-pane +as the target. +If a line contains newlines it is split into multiple lines. +Lines may use styles, see the +.Sx STYLES +section. +.It +A shell command given by +.Fl R +which is run and any output shown in the pane. +.El +.Pp +The first argument, +.Ar command , +is a +.Nm +command which is run when a key is pressed. +The key is available in the +.Ql popup_key +format. +After +.Ar command +is run, the popup is closed. +It may be empty to discard any key presses. +If +.Fl K +is given together with +.Fl R , +key presses are instead passed to the +.Fl R +shell command. +.Fl E +closes the popup automatically when +.Ar shell-command +exits. +Two +.Fl E +closes the popup only if +.Ar shell-command +exited with success. +With +.Fl K , +.Ql Escape +and +.Ql C-c +close the popup unless +.Fl E +is also given. +.Pp +.Fl x +and +.Fl y +give the position of the popup, they have the same meaning as for the +.Ic display-menu +command. +.Fl w +and +.Fl h +give the width and height - both may be a percentage (followed by +.Ql % ) . +If omitted, without +.Fl R +they are calculated from the given lines and with +.Fl R +they use half the terminal size. +.Pp +The +.Fl C +flag closes any popup on the client. .El .Sh BUFFERS .Nm @@ -5083,12 +5358,16 @@ Delete the buffer named or the most recently added automatically named buffer if not specified. .It Xo Ic list-buffers .Op Fl F Ar format +.Op Fl f Ar filter .Xc .D1 (alias: Ic lsb ) List the global buffers. -For the meaning of the .Fl F -flag, see the +specifies the format of each line and +.Fl f +a filter. +Only buffers for which the filter is true are shown. +See the .Sx FORMATS section. .It Xo Ic load-buffer @@ -5198,8 +5477,9 @@ Lock each client individually by running the command specified by the option. .It Xo Ic run-shell .Op Fl b +.Op Fl d Ar delay .Op Fl t Ar target-pane -.Ar shell-command +.Op Ar shell-command .Xc .D1 (alias: Ic run ) Execute @@ -5212,8 +5492,12 @@ 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 d +waits for +.Ar delay +seconds before starting the command. +After the command finishes, any output to stdout is displayed in view mode (in +the pane specified by .Fl t or the current pane if omitted). If the command doesn't return success, the exit status is also displayed. @@ -5267,9 +5551,16 @@ The server crashed or otherwise exited without telling the client the reason. .Sh TERMINFO EXTENSIONS .Nm understands some unofficial extensions to -.Xr terminfo 5 : +.Xr terminfo 5 . +It is not normally necessary to set these manually, instead the +.Ic terminal-features +option should be used. .Bl -tag -width Ds -.It Em Cs , Cr +.It Em \&AX +An existing extension that tells +.Nm +the terminal supports default colours. +.It Em \&Cs , Cr Set the cursor colour. The first takes a single string argument and is used to set the colour; the second takes no arguments and restores the default cursor colour. @@ -5279,35 +5570,32 @@ to change the cursor colour from inside .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed +.It Em \&Cmg, \&Clmg, \&Dsmg , \&Enmg +Set, clear, disable or enable DECSLRM margins. +These are set automatically if the terminal reports it is +.Em VT420 +compatible. +.It Em \&Dsbp , \&Enbp +Disable and enable bracketed paste. +These are set automatically if the +.Em XT +capability is present. +.It Em \&Dsfcs , \&Enfcs +Disable and enable focus reporting. +These are set automatically if the +.Em XT +capability is present. .It Em \&Smol Enable the overline attribute. -The capability is usually SGR 53 and can be added to -.Ic terminal-overrides -as: -.Bd -literal -offset indent -Smol=\eE[53m -.Ed .It Em \&Smulx Set a styled underscore. The single parameter is one of: 0 for no underscore, 1 for normal underscore, 2 for double underscore, 3 for curly underscore, 4 for dotted underscore and 5 for dashed underscore. -The capability can typically be added to -.Ic terminal-overrides -as: -.Bd -literal -offset indent -Smulx=\eE[4::%p1%dm -.Ed .It Em \&Setulc Set the underscore colour. The argument is (red * 65536) + (green * 256) + blue where each is between 0 and 255. -The capability can typically be added to -.Ic terminal-overrides -as: -.Bd -literal -offset indent -Setulc=\eE[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m -.Ed .It Em \&Ss , Se Set or reset the cursor style. If set, a sequence such as this may be used @@ -5319,6 +5607,8 @@ $ printf '\e033[4 q' If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Sync +Start (parameter is 1) or end (parameter is 2) a synchronized update. .It Em \&Tc Indicate that the terminal supports the .Ql direct colour @@ -5333,6 +5623,11 @@ capabilities to the .Nm .Xr terminfo 5 entry). +.Pp +This is equivalent to the +.Em RGB +.Xr terminfo 5 +capability. .It Em \&Ms Store the current buffer in the host terminal's selection (clipboard). See the @@ -5340,6 +5635,11 @@ See the option above and the .Xr xterm 1 man page. +.It Em \&XT +This is an existing extension capability that tmux uses to mean that the +terminal supports the +.Xr xterm 1 +title set sequences and to automatically set some of the capabilities above. .El .Sh CONTROL MODE .Nm @@ -5385,7 +5685,7 @@ A notification will never occur inside an output block. .Pp The following notifications are defined: .Bl -tag -width Ds -.It Ic %client-session-changed Ar client Ar session-id Ar name +.It Ic %client-session-changed Ar client session-id name The client is now attached to the session with ID .Ar session-id , which is named @@ -26,6 +26,7 @@ #include <langinfo.h> #include <locale.h> #include <pwd.h> +#include <signal.h> #include <stdlib.h> #include <string.h> #include <time.h> @@ -46,15 +47,15 @@ const char *shell_command; static __dead void usage(void); static char *make_label(const char *, char **); +static int areshell(const char *); static const char *getshell(void); -static int checkshell(const char *); static __dead void usage(void) { fprintf(stderr, "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" - " [-S socket-path] [command [flags]]\n", + " [-S socket-path] [-T features] [command [flags]]\n", getprogname()); exit(1); } @@ -76,7 +77,7 @@ getshell(void) return (_PATH_BSHELL); } -static int +int checkshell(const char *shell) { if (shell == NULL || *shell != '/') @@ -88,7 +89,7 @@ checkshell(const char *shell) return (1); } -int +static int areshell(const char *shell) { const char *progname, *ptr; @@ -106,33 +107,103 @@ areshell(const char *shell) } static char * +expand_path(const char *path, const char *home) +{ + char *expanded, *name; + const char *end; + struct environ_entry *value; + + if (strncmp(path, "~/", 2) == 0) { + if (home == NULL) + return (NULL); + xasprintf(&expanded, "%s%s", home, path + 1); + return (expanded); + } + + if (*path == '$') { + end = strchr(path, '/'); + if (end == NULL) + name = xstrdup(path + 1); + else + name = xstrndup(path + 1, end - path - 1); + value = environ_find(global_environ, name); + free(name); + if (value == NULL) + return (NULL); + if (end == NULL) + end = ""; + xasprintf(&expanded, "%s%s", value->value, end); + return (expanded); + } + + return (xstrdup(path)); +} + +void +expand_paths(const char *s, char ***paths, u_int *n) +{ + const char *home = find_home(); + char *copy, *next, *tmp, resolved[PATH_MAX], *expanded; + u_int i; + + *paths = NULL; + *n = 0; + + copy = tmp = xstrdup(s); + while ((next = strsep(&tmp, ":")) != NULL) { + expanded = expand_path(next, home); + if (expanded == NULL) { + log_debug("%s: invalid path: %s", __func__, next); + continue; + } + if (realpath(expanded, resolved) == NULL) { + log_debug("%s: realpath(\"%s\") failed: %s", __func__, + expanded, strerror(errno)); + free(expanded); + continue; + } + free(expanded); + for (i = 0; i < *n; i++) { + if (strcmp(resolved, (*paths)[i]) == 0) + break; + } + if (i != *n) { + log_debug("%s: duplicate path: %s", __func__, resolved); + continue; + } + *paths = xreallocarray(*paths, (*n) + 1, sizeof *paths); + (*paths)[(*n)++] = xstrdup(resolved); + } + free(copy); +} + +static char * make_label(const char *label, char **cause) { - char *base, resolved[PATH_MAX], *path, *s; - struct stat sb; - uid_t uid; + char **paths, *path, *base; + u_int i, n; + struct stat sb; + uid_t uid; *cause = NULL; - if (label == NULL) label = "default"; uid = getuid(); - if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') - xasprintf(&base, "%s/tmux-%ld", s, (long)uid); - else - xasprintf(&base, "%s/tmux-%ld", _PATH_TMP, (long)uid); - if (realpath(base, resolved) == NULL && - strlcpy(resolved, base, sizeof resolved) >= sizeof resolved) { - errno = ERANGE; - free(base); - goto fail; + expand_paths(TMUX_SOCK, &paths, &n); + if (n == 0) { + xasprintf(cause, "no suitable socket path"); + return (NULL); } - free(base); + path = paths[0]; /* can only have one socket! */ + for (i = 1; i < n; i++) + free(paths[i]); + free(paths); - if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST) + xasprintf(&base, "%s/tmux-%ld", path, (long)uid); + if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) goto fail; - if (lstat(resolved, &sb) != 0) + if (lstat(base, &sb) != 0) goto fail; if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; @@ -142,11 +213,13 @@ make_label(const char *label, char **cause) errno = EACCES; goto fail; } - xasprintf(&path, "%s/%s", resolved, label); + xasprintf(&path, "%s/%s", base, label); + free(base); return (path); fail: - xasprintf(cause, "error creating %s (%s)", resolved, strerror(errno)); + xasprintf(cause, "error creating %s (%s)", base, strerror(errno)); + free(base); return (NULL); } @@ -165,6 +238,19 @@ setblocking(int fd, int state) } const char * +sig2name(int signo) +{ + static char s[11]; + +#ifdef HAVE_SYS_SIGNAME + if (signo > 0 && signo < NSIG) + return (sys_signame[signo]); +#endif + xsnprintf(s, sizeof s, "%d", signo); + return (s); +} + +const char * find_cwd(void) { char resolved1[PATH_MAX], resolved2[PATH_MAX]; @@ -219,9 +305,11 @@ getversion(void) int main(int argc, char **argv) { - char *path, *label, *cause, **var; + char *path = NULL, *label = NULL; + char *cause, **var; const char *s, *shell, *cwd; - int opt, flags, keys; + int opt, flags = 0, keys; + int feat = 0; const struct options_table_entry *oe; if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && @@ -238,14 +326,11 @@ main(int argc, char **argv) if (**argv == '-') flags = CLIENT_LOGIN; - else - flags = 0; - label = path = NULL; - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUvV")) != -1) { + while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:T:uUvV")) != -1) { switch (opt) { case '2': - flags |= CLIENT_256COLOURS; + tty_add_features(&feat, "256", ":,"); break; case 'c': shell_command = optarg; @@ -275,6 +360,9 @@ main(int argc, char **argv) free(path); path = xstrdup(optarg); break; + case 'T': + tty_add_features(&feat, optarg, ":,"); + break; case 'u': flags |= CLIENT_UTF8; break; @@ -321,9 +409,9 @@ main(int argc, char **argv) global_environ = environ_create(); for (var = environ; *var != NULL; var++) - environ_put(global_environ, *var); + environ_put(global_environ, *var, 0); if ((cwd = find_cwd()) != NULL) - environ_set(global_environ, "PWD", "%s", cwd); + environ_set(global_environ, "PWD", 0, "%s", cwd); global_options = options_create(NULL); global_s_options = options_create(NULL); @@ -368,16 +456,19 @@ main(int argc, char **argv) path[strcspn(path, ",")] = '\0'; } } - if (path == NULL && (path = make_label(label, &cause)) == NULL) { - if (cause != NULL) { - fprintf(stderr, "%s\n", cause); - free(cause); + if (path == NULL) { + if ((path = make_label(label, &cause)) == NULL) { + if (cause != NULL) { + fprintf(stderr, "%s\n", cause); + free(cause); + } + exit(1); } - exit(1); + flags |= CLIENT_DEFAULTSOCKET; } socket_path = path; free(label); /* Pass control to the client. */ - exit(client_main(osdep_event_init(), argc, argv, flags)); + exit(client_main(osdep_event_init(), argc, argv, flags, feat)); } @@ -41,9 +41,12 @@ extern char **environ; struct args; struct args_value; struct client; +struct cmd; struct cmd_find_state; struct cmdq_item; struct cmdq_list; +struct cmdq_state; +struct cmds; struct environ; struct format_job_tree; struct format_tree; @@ -52,8 +55,8 @@ struct job; struct mode_tree_data; struct mouse_event; struct options; -struct options_entry; struct options_array_item; +struct options_entry; struct session; struct tmuxpeer; struct tmuxproc; @@ -62,10 +65,13 @@ struct winlink; /* Client-server protocol version. */ #define PROTOCOL_VERSION 8 -/* Default configuration files. */ +/* Default configuration files and socket paths. */ #ifndef TMUX_CONF #define TMUX_CONF "/etc/tmux.conf:~/.tmux.conf" #endif +#ifndef TMUX_SOCK +#define TMUX_SOCK "$TMUX_TMPDIR:" _PATH_TMP +#endif /* Minimum layout cell size, NOT including border lines. */ #define PANE_MINIMUM 1 @@ -78,7 +84,7 @@ struct winlink; #define NAME_INTERVAL 500000 /* Maximum size of data to hold from a pane. */ -#define READ_SIZE 4096 +#define READ_SIZE 8192 /* Default pixel cell sizes. */ #define DEFAULT_XPIXEL 16 @@ -104,24 +110,24 @@ struct winlink; #define VISUAL_BOTH 2 /* Special key codes. */ -#define KEYC_NONE 0xffff00000000ULL -#define KEYC_UNKNOWN 0xfffe00000000ULL -#define KEYC_BASE 0x000010000000ULL -#define KEYC_USER 0x000020000000ULL +#define KEYC_NONE 0x00ff000000000ULL +#define KEYC_UNKNOWN 0x00fe000000000ULL +#define KEYC_BASE 0x0001000000000ULL +#define KEYC_USER 0x0002000000000ULL + +/* Key modifier bits. */ +#define KEYC_ESCAPE 0x0100000000000ULL +#define KEYC_CTRL 0x0200000000000ULL +#define KEYC_SHIFT 0x0400000000000ULL +#define KEYC_XTERM 0x0800000000000ULL +#define KEYC_LITERAL 0x1000000000000ULL /* Available user keys. */ #define KEYC_NUSER 1000 -/* Key modifier bits. */ -#define KEYC_ESCAPE 0x200000000000ULL -#define KEYC_CTRL 0x400000000000ULL -#define KEYC_SHIFT 0x800000000000ULL -#define KEYC_XTERM 0x1000000000000ULL -#define KEYC_LITERAL 0x2000000000000ULL - /* Mask to obtain key w/o modifiers. */ -#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM|KEYC_LITERAL) -#define KEYC_MASK_KEY (~KEYC_MASK_MOD) +#define KEYC_MASK_MOD 0xff00000000000ULL +#define KEYC_MASK_KEY 0x00fffffffffffULL /* Is this a mouse key? */ #define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ @@ -168,6 +174,7 @@ enum { /* Mouse keys. */ KEYC_MOUSE, /* unclassified mouse event */ KEYC_DRAGGING, /* dragging in progress */ + KEYC_DOUBLECLICK, /* double click complete */ KEYC_MOUSE_KEY(MOUSEMOVE), KEYC_MOUSE_KEY(MOUSEDOWN1), KEYC_MOUSE_KEY(MOUSEDOWN2), @@ -183,6 +190,9 @@ enum { KEYC_MOUSE_KEY(MOUSEDRAGEND3), KEYC_MOUSE_KEY(WHEELUP), KEYC_MOUSE_KEY(WHEELDOWN), + KEYC_MOUSE_KEY(SECONDCLICK1), + KEYC_MOUSE_KEY(SECONDCLICK2), + KEYC_MOUSE_KEY(SECONDCLICK3), KEYC_MOUSE_KEY(DOUBLECLICK1), KEYC_MOUSE_KEY(DOUBLECLICK2), KEYC_MOUSE_KEY(DOUBLECLICK3), @@ -249,6 +259,8 @@ enum tty_code_code { TTYC_BOLD, TTYC_CIVIS, TTYC_CLEAR, + TTYC_CLMG, + TTYC_CMG, TTYC_CNORM, TTYC_COLORS, TTYC_CR, @@ -269,12 +281,18 @@ enum tty_code_code { TTYC_DIM, TTYC_DL, TTYC_DL1, + TTYC_DSBP, + TTYC_DSFCS, + TTYC_DSMG, TTYC_E3, TTYC_ECH, TTYC_ED, TTYC_EL, TTYC_EL1, TTYC_ENACS, + TTYC_ENBP, + TTYC_ENFCS, + TTYC_ENMG, TTYC_FSL, TTYC_HOME, TTYC_HPA, @@ -440,19 +458,20 @@ enum tty_code_code { TTYC_SITM, TTYC_SMACS, TTYC_SMCUP, - TTYC_SMOL, TTYC_SMKX, + TTYC_SMOL, TTYC_SMSO, - TTYC_SMULX, TTYC_SMUL, + TTYC_SMULX, TTYC_SMXX, TTYC_SS, + TTYC_SYNC, TTYC_TC, TTYC_TSL, TTYC_U8, TTYC_VPA, TTYC_XENL, - TTYC_XT, + TTYC_XT }; /* Message codes. */ @@ -468,6 +487,7 @@ enum msgtype { MSG_IDENTIFY_DONE, MSG_IDENTIFY_CLIENTPID, MSG_IDENTIFY_CWD, + MSG_IDENTIFY_FEATURES, MSG_COMMAND = 200, MSG_DETACH, @@ -562,6 +582,7 @@ struct msg_write_close { #define ALL_MODES 0xffffff #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) +#define MOTION_MOUSE_MODES (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) /* * A single UTF-8 character. UTF8_SIZE must be big enough to hold @@ -754,9 +775,16 @@ struct screen { int mode; - bitstr_t *tabs; + u_int saved_cx; + u_int saved_cy; + struct grid *saved_grid; + struct grid_cell saved_cell; + int saved_flags; + bitstr_t *tabs; struct screen_sel *sel; + + struct screen_write_collect_line *write_list; }; /* Screen write context. */ @@ -765,9 +793,9 @@ struct screen_write_collect_line; struct screen_write_ctx { struct window_pane *wp; struct screen *s; + int sync; struct screen_write_collect_item *item; - struct screen_write_collect_line *list; u_int scrolled; u_int bg; @@ -810,7 +838,6 @@ struct menu { u_int width; }; typedef void (*menu_choice_cb)(struct menu *, u_int, key_code, void *); -#define MENU_NOMOUSE 0x1 /* * Window mode. Windows can be in several modes and this is used to call the @@ -836,11 +863,11 @@ struct window_mode { void (*formats)(struct window_mode_entry *, struct format_tree *); }; -#define WINDOW_MODE_TIMEOUT 180 /* Active window mode. */ struct window_mode_entry { struct window_pane *wp; + struct window_pane *swp; const struct window_mode *mode; void *data; @@ -898,7 +925,6 @@ struct window_pane { int fd; struct bufferevent *event; - u_int disabled; struct event resize_timer; @@ -918,19 +944,14 @@ struct window_pane { struct screen status_screen; size_t status_size; - /* Saved in alternative screen mode. */ - u_int saved_cx; - u_int saved_cy; - struct grid *saved_grid; - struct grid_cell saved_cell; - TAILQ_HEAD (, window_mode_entry) modes; - struct event modetimer; - time_t modelast; char *searchstr; int searchregex; + size_t written; + size_t skipped; + TAILQ_ENTRY(window_pane) entry; RB_ENTRY(window_pane) tree_entry; }; @@ -1048,6 +1069,9 @@ struct environ_entry { char *name; char *value; + int flags; +#define ENVIRON_HIDDEN 0x1 + RB_ENTRY(environ_entry) entry; }; @@ -1120,6 +1144,7 @@ RB_HEAD(sessions, session); /* Mouse input. */ struct mouse_event { int valid; + int ignore; key_code key; @@ -1165,7 +1190,8 @@ struct tty_key { struct tty_code; struct tty_term { char *name; - u_int references; + struct tty *tty; + int features; char acs[UCHAR_MAX + 1][2]; @@ -1176,6 +1202,7 @@ struct tty_term { #define TERM_DECSLRM 0x4 #define TERM_DECFRA 0x8 #define TERM_RGBCOLOURS 0x10 +#define TERM_VT100LIKE 0x20 int flags; LIST_ENTRY(tty_term) entry; @@ -1228,18 +1255,17 @@ struct tty { #define TTY_NOCURSOR 0x1 #define TTY_FREEZE 0x2 #define TTY_TIMER 0x4 -#define TTY_UTF8 0x8 +/* 0x8 unused */ #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 #define TTY_FOCUS 0x40 #define TTY_BLOCK 0x80 #define TTY_HAVEDA 0x100 -#define TTY_HAVEDSR 0x200 +#define TTY_HAVEXDA 0x200 +#define TTY_SYNCING 0x400 int flags; struct tty_term *term; - char *term_name; - int term_flags; u_int mouse_last_x; u_int mouse_last_y; @@ -1333,27 +1359,11 @@ struct cmd_find_state { #define CMD_FIND_EXACT_WINDOW 0x20 #define CMD_FIND_CANFAIL 0x40 -/* Command and list of commands. */ -struct cmd { - const struct cmd_entry *entry; - struct args *args; - u_int group; - - char *file; - u_int line; - - char *alias; - int argc; - char **argv; - - TAILQ_ENTRY(cmd) qentry; -}; -TAILQ_HEAD(cmds, cmd); - +/* List of commands. */ struct cmd_list { - int references; - u_int group; - struct cmds list; + int references; + u_int group; + struct cmds *list; }; /* Command return values. */ @@ -1381,6 +1391,7 @@ struct cmd_parse_input { #define CMD_PARSE_PARSEONLY 0x2 #define CMD_PARSE_NOALIAS 0x4 #define CMD_PARSE_VERBOSE 0x8 +#define CMD_PARSE_ONEGROUP 0x10 const char *file; u_int line; @@ -1390,59 +1401,13 @@ struct cmd_parse_input { struct cmd_find_state fs; }; -/* Command queue item type. */ -enum cmdq_type { - CMDQ_COMMAND, - CMDQ_CALLBACK, -}; - -/* Command queue item shared state. */ -struct cmdq_shared { - int references; - - int flags; -#define CMDQ_SHARED_REPEAT 0x1 -#define CMDQ_SHARED_CONTROL 0x2 - - struct format_tree *formats; - - struct mouse_event mouse; - struct cmd_find_state current; -}; +/* Command queue flags. */ +#define CMDQ_STATE_REPEAT 0x1 +#define CMDQ_STATE_CONTROL 0x2 +#define CMDQ_STATE_NOHOOKS 0x4 -/* Command queue item. */ +/* Command queue callback. */ typedef enum cmd_retval (*cmdq_cb) (struct cmdq_item *, void *); -struct cmdq_item { - char *name; - struct cmdq_list *queue; - struct cmdq_item *next; - - struct client *client; - - enum cmdq_type type; - u_int group; - - u_int number; - time_t time; - - int flags; -#define CMDQ_FIRED 0x1 -#define CMDQ_WAITING 0x2 -#define CMDQ_NOHOOKS 0x4 - - struct cmdq_shared *shared; - struct cmd_find_state source; - struct cmd_find_state target; - - struct cmd_list *cmdlist; - struct cmd *cmd; - - cmdq_cb cb; - void *data; - - TAILQ_ENTRY(cmdq_item) entry; -}; -TAILQ_HEAD(cmdq_list, cmdq_item); /* Command definition flag. */ struct cmd_entry_flag { @@ -1469,6 +1434,9 @@ struct cmd_entry { #define CMD_STARTSERVER 0x1 #define CMD_READONLY 0x2 #define CMD_AFTERHOOK 0x4 +#define CMD_CLIENT_CFLAG 0x8 +#define CMD_CLIENT_TFLAG 0x10 +#define CMD_CLIENT_CANFAIL 0x20 int flags; enum cmd_retval (*exec)(struct cmd *, struct cmdq_item *); @@ -1517,13 +1485,15 @@ RB_HEAD(client_files, client_file); /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); +typedef int (*overlay_check_cb)(struct client *, u_int, u_int); +typedef int (*overlay_mode_cb)(struct client *, u_int *, u_int *); typedef void (*overlay_draw_cb)(struct client *, struct screen_redraw_ctx *); typedef int (*overlay_key_cb)(struct client *, struct key_event *); typedef void (*overlay_free_cb)(struct client *); struct client { const char *name; struct tmuxpeer *peer; - struct cmdq_list queue; + struct cmdq_list *queue; pid_t pid; int fd; @@ -1539,7 +1509,10 @@ struct client { char *title; const char *cwd; - char *term; + char *term_name; + int term_features; + char *term_type; + char *ttyname; struct tty tty; @@ -1551,6 +1524,7 @@ struct client { struct event click_timer; u_int click_button; + struct mouse_event click_event; struct status_line status; @@ -1571,7 +1545,7 @@ struct client { #define CLIENT_CONTROLCONTROL 0x4000 #define CLIENT_FOCUSED 0x8000 #define CLIENT_UTF8 0x10000 -#define CLIENT_256COLOURS 0x20000 +/* 0x20000 unused */ #define CLIENT_IDENTIFIED 0x40000 #define CLIENT_STATUSFORCE 0x80000 #define CLIENT_DOUBLECLICK 0x100000 @@ -1581,12 +1555,16 @@ struct client { #define CLIENT_REDRAWSTATUSALWAYS 0x1000000 #define CLIENT_REDRAWOVERLAY 0x2000000 #define CLIENT_CONTROL_NOOUTPUT 0x4000000 +#define CLIENT_DEFAULTSOCKET 0x8000000 +#define CLIENT_STARTSERVER 0x10000000 +#define CLIENT_REDRAWPANES 0x20000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUSALWAYS| \ CLIENT_REDRAWBORDERS| \ - CLIENT_REDRAWOVERLAY) + CLIENT_REDRAWOVERLAY| \ + CLIENT_REDRAWPANES) #define CLIENT_UNATTACHEDFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ @@ -1598,6 +1576,8 @@ struct client { int flags; struct key_table *keytable; + uint64_t redraw_panes; + char *message_string; struct event message_timer; u_int message_next; @@ -1629,6 +1609,8 @@ struct client { u_int pan_ox; u_int pan_oy; + overlay_check_cb overlay_check; + overlay_mode_cb overlay_mode; overlay_draw_cb overlay_draw; overlay_key_cb overlay_key; overlay_free_cb overlay_free; @@ -1730,7 +1712,7 @@ struct spawn_context { struct session *s; struct winlink *wl; - struct client *c; + struct client *tc; struct window_pane *wp0; struct layout_cell *lc; @@ -1769,11 +1751,14 @@ extern const char *socket_path; extern const char *shell_command; extern int ptm_fd; extern const char *shell_command; -int areshell(const char *); +int checkshell(const char *); void setblocking(int, int); +const char *sig2name(int); const char *find_cwd(void); const char *find_home(void); const char *getversion(void); +void expand_paths(const char *, char ***, u_int *); + /* proc.c */ struct imsg; @@ -1826,11 +1811,13 @@ char *paste_make_sample(struct paste_buffer *); #define FORMAT_PANE 0x80000000U #define FORMAT_WINDOW 0x40000000U struct format_tree; +struct format_modifier; const char *format_skip(const char *, const char *); int format_true(const char *); struct format_tree *format_create(struct client *, struct cmdq_item *, int, int); void format_free(struct format_tree *); +void format_merge(struct format_tree *, struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); void format_each(struct format_tree *, void (*)(const char *, @@ -1840,6 +1827,7 @@ char *format_expand(struct format_tree *, const char *); char *format_single(struct cmdq_item *, const char *, struct client *, struct session *, struct winlink *, struct window_pane *); +char *format_single_from_target(struct cmdq_item *, const char *); void format_defaults(struct format_tree *, struct client *, struct session *, struct winlink *, struct window_pane *); void format_defaults_window(struct format_tree *, struct window *); @@ -1926,9 +1914,13 @@ typedef void (*job_update_cb) (struct job *); typedef void (*job_complete_cb) (struct job *); typedef void (*job_free_cb) (void *); #define JOB_NOWAIT 0x1 +#define JOB_KEEPWRITE 0x2 +#define JOB_PTY 0x4 struct job *job_run(const char *, struct session *, const char *, - job_update_cb, job_complete_cb, job_free_cb, void *, int); + job_update_cb, job_complete_cb, job_free_cb, void *, int, + int, int); void job_free(struct job *); +void job_resize(struct job *, u_int, u_int); void job_check_died(pid_t, int); int job_get_status(struct job *); void *job_get_data(struct job *); @@ -1944,10 +1936,10 @@ struct environ_entry *environ_first(struct environ *); struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); -void printflike(3, 4) environ_set(struct environ *, const char *, const char *, - ...); +void printflike(4, 5) environ_set(struct environ *, const char *, int, + const char *, ...); void environ_clear(struct environ *, const char *); -void environ_put(struct environ *, const char *); +void environ_put(struct environ *, const char *, int); void environ_unset(struct environ *, const char *); void environ_update(struct options *, struct environ *, struct environ *); void environ_push(struct environ *); @@ -1977,19 +1969,22 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); -int tty_init(struct tty *, struct client *, int, char *); +int tty_init(struct tty *, struct client *, int); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); void tty_start_tty(struct tty *); +void tty_send_requests(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); void tty_draw_line(struct tty *, struct window_pane *, struct screen *, u_int, u_int, u_int, u_int, u_int); +void tty_sync_start(struct tty *); +void tty_sync_end(struct tty *); int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); -void tty_set_flags(struct tty *, int); +void tty_update_features(struct tty *); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); @@ -2013,11 +2008,14 @@ void tty_cmd_scrolldown(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); +void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); /* tty-term.c */ extern struct tty_terms tty_terms; u_int tty_term_ncodes(void); -struct tty_term *tty_term_find(char *, int, char **); +void tty_term_apply(struct tty_term *, const char *, int); +void tty_term_apply_overrides(struct tty_term *); +struct tty_term *tty_term_create(struct tty *, char *, int *, int, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); @@ -2034,6 +2032,12 @@ int tty_term_number(struct tty_term *, enum tty_code_code); int tty_term_flag(struct tty_term *, enum tty_code_code); const char *tty_term_describe(struct tty_term *, enum tty_code_code); +/* tty-features.c */ +void tty_add_features(int *, const char *, const char *); +const char *tty_get_features(int); +int tty_apply_features(struct tty_term *, int); +void tty_default_features(int *, const char *, u_int); + /* tty-acs.c */ int tty_acs_needed(struct tty *); const char *tty_acs_get(struct tty *, u_char); @@ -2055,6 +2059,10 @@ const char *args_first_value(struct args *, u_char, struct args_value **); const char *args_next_value(struct args_value **); long long args_strtonum(struct args *, u_char, long long, long long, char **); +long long args_percentage(struct args *, u_char, long long, + long long, long long, char **); +long long args_string_percentage(const char *, long long, long long, + long long, char **); /* cmd-find.c */ int cmd_find_target(struct cmd_find_state *, struct cmdq_item *, @@ -2085,6 +2093,7 @@ int cmd_find_from_mouse(struct cmd_find_state *, int cmd_find_from_nothing(struct cmd_find_state *, int); /* cmd.c */ +extern const struct cmd_entry *cmd_table[]; void printflike(3, 4) cmd_log_argv(int, char **, const char *, ...); void cmd_prepend_argv(int *, char ***, char *); void cmd_append_argv(int *, char ***, char *); @@ -2094,16 +2103,28 @@ char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); char *cmd_get_alias(const char *); +const struct cmd_entry *cmd_get_entry(struct cmd *); +struct args *cmd_get_args(struct cmd *); +u_int cmd_get_group(struct cmd *); +void cmd_get_source(struct cmd *, const char **, u_int *); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); void cmd_free(struct cmd *); char *cmd_print(struct cmd *); +struct cmd_list *cmd_list_new(void); +void cmd_list_append(struct cmd_list *, struct cmd *); +void cmd_list_move(struct cmd_list *, struct cmd_list *); +void cmd_list_free(struct cmd_list *); +char *cmd_list_print(struct cmd_list *, int); +struct cmd *cmd_list_first(struct cmd_list *); +struct cmd *cmd_list_next(struct cmd *); +int cmd_list_all_have(struct cmd_list *, int); +int cmd_list_any_have(struct cmd_list *, int); int cmd_mouse_at(struct window_pane *, struct mouse_event *, u_int *, u_int *, int); struct winlink *cmd_mouse_window(struct mouse_event *, struct session **); struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **, struct winlink **); char *cmd_template_replace(const char *, const char *, int); -extern const struct cmd_entry *cmd_table[]; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int, @@ -2114,21 +2135,38 @@ void cmd_parse_empty(struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_string(const char *, struct cmd_parse_input *); +enum cmd_parse_status cmd_parse_and_insert(const char *, + struct cmd_parse_input *, struct cmdq_item *, + struct cmdq_state *, char **); +enum cmd_parse_status cmd_parse_and_append(const char *, + struct cmd_parse_input *, struct client *, + struct cmdq_state *, char **); struct cmd_parse_result *cmd_parse_from_buffer(const void *, size_t, struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_arguments(int, char **, struct cmd_parse_input *); -/* cmd-list.c */ -struct cmd_list *cmd_list_new(void); -void cmd_list_append(struct cmd_list *, struct cmd *); -void cmd_list_move(struct cmd_list *, struct cmd_list *); -void cmd_list_free(struct cmd_list *); -char *cmd_list_print(struct cmd_list *, int); - /* cmd-queue.c */ -struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, - struct mouse_event *, int); +struct cmdq_state *cmdq_new_state(struct cmd_find_state *, struct key_event *, + int); +struct cmdq_state *cmdq_link_state(struct cmdq_state *); +struct cmdq_state *cmdq_copy_state(struct cmdq_state *); +void cmdq_free_state(struct cmdq_state *); +void printflike(3, 4) cmdq_add_format(struct cmdq_state *, const char *, + const char *, ...); +void cmdq_merge_formats(struct cmdq_item *, struct format_tree *); +struct cmdq_list *cmdq_new(void); +void cmdq_free(struct cmdq_list *); +const char *cmdq_get_name(struct cmdq_item *); +struct client *cmdq_get_client(struct cmdq_item *); +struct client *cmdq_get_target_client(struct cmdq_item *); +struct cmdq_state *cmdq_get_state(struct cmdq_item *); +struct cmd_find_state *cmdq_get_target(struct cmdq_item *); +struct cmd_find_state *cmdq_get_source(struct cmdq_item *); +struct key_event *cmdq_get_event(struct cmdq_item *); +struct cmd_find_state *cmdq_get_current(struct cmdq_item *); +int cmdq_get_flags(struct cmdq_item *); +struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmdq_state *); #define cmdq_get_callback(cb, data) cmdq_get_callback1(#cb, cb, data) struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *); struct cmdq_item *cmdq_get_error(const char *); @@ -2137,9 +2175,8 @@ struct cmdq_item *cmdq_append(struct client *, struct cmdq_item *); void cmdq_insert_hook(struct session *, struct cmdq_item *, struct cmd_find_state *, const char *, ...); void cmdq_continue(struct cmdq_item *); -void printflike(3, 4) cmdq_format(struct cmdq_item *, const char *, - const char *, ...); u_int cmdq_next(struct client *); +struct cmdq_item *cmdq_running(struct client *); void cmdq_guard(struct cmdq_item *, const char *, int); void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...); @@ -2148,7 +2185,7 @@ void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...); void cmd_wait_for_flush(void); /* client.c */ -int client_main(struct event_base *, int, char **, int); +int client_main(struct event_base *, int, char **, int, int); /* key-bindings.c */ struct key_table *key_bindings_get_table(const char *, int); @@ -2164,7 +2201,7 @@ void key_bindings_remove(const char *, key_code); void key_bindings_remove_table(const char *); void key_bindings_init(void); struct cmdq_item *key_bindings_dispatch(struct key_binding *, - struct cmdq_item *, struct client *, struct mouse_event *, + struct cmdq_item *, struct client *, struct key_event *, struct cmd_find_state *); /* key-string.c */ @@ -2203,14 +2240,16 @@ void server_clear_marked(void); int server_is_marked(struct session *, struct winlink *, struct window_pane *); int server_check_marked(void); -int server_start(struct tmuxproc *, struct event_base *, int, char *); +int server_start(struct tmuxproc *, int, struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); /* server-client.c */ u_int server_client_how_many(void); -void server_client_set_overlay(struct client *, u_int, overlay_draw_cb, - overlay_key_cb, overlay_free_cb, void *); +void server_client_set_overlay(struct client *, u_int, overlay_check_cb, + overlay_mode_cb, overlay_draw_cb, overlay_key_cb, + overlay_free_cb, void *); +void server_client_clear_overlay(struct client *); void server_client_set_key_table(struct client *, const char *); const char *server_client_get_key_table(struct client *); int server_client_check_nested(struct client *); @@ -2282,15 +2321,21 @@ void recalculate_size(struct window *); void recalculate_sizes(void); /* input.c */ -void input_init(struct window_pane *); -void input_free(struct window_pane *); -void input_reset(struct window_pane *, int); -struct evbuffer *input_pending(struct window_pane *); -void input_parse(struct window_pane *); +struct input_ctx *input_init(struct window_pane *, struct bufferevent *); +void input_free(struct input_ctx *); +void input_reset(struct input_ctx *, int); +struct evbuffer *input_pending(struct input_ctx *); +void input_parse_pane(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); +void input_parse_screen(struct input_ctx *, struct screen *, u_char *, + size_t); /* input-key.c */ -int input_key(struct window_pane *, key_code, struct mouse_event *); +int input_key_pane(struct window_pane *, key_code, struct mouse_event *); +int input_key(struct window_pane *, struct screen *, struct bufferevent *, + key_code); +int input_key_get_mouse(struct screen *, struct mouse_event *, u_int, + u_int, const char **, size_t *); /* xterm-keys.c */ char *xterm_keys_lookup(key_code); @@ -2311,11 +2356,13 @@ int attributes_fromstring(const char *); /* grid.c */ extern const struct grid_cell grid_default_cell; +void grid_empty_line(struct grid *, u_int, u_int); int grid_cells_equal(const struct grid_cell *, const struct grid_cell *); struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); int grid_compare(struct grid *, struct grid *); void grid_collect_history(struct grid *); +void grid_remove_history(struct grid *, u_int ); void grid_scroll_history(struct grid *, u_int); void grid_scroll_history_region(struct grid *, u_int, u_int, u_int); void grid_clear_history(struct grid *); @@ -2360,6 +2407,8 @@ void grid_view_delete_cells(struct grid *, u_int, u_int, u_int, u_int); char *grid_view_string_cells(struct grid *, u_int, u_int, u_int); /* screen-write.c */ +void screen_write_make_list(struct screen *); +void screen_write_free_list(struct screen *); void screen_write_start(struct screen_write_ctx *, struct window_pane *, struct screen *); void screen_write_stop(struct screen_write_ctx *); @@ -2379,7 +2428,8 @@ void screen_write_fast_copy(struct screen_write_ctx *, struct screen *, u_int, u_int, u_int, u_int); void screen_write_hline(struct screen_write_ctx *, u_int, int, int); void screen_write_vline(struct screen_write_ctx *, u_int, int, int); -void screen_write_menu(struct screen_write_ctx *, struct menu *, int); +void screen_write_menu(struct screen_write_ctx *, struct menu *, int, + const struct grid_cell *); void screen_write_box(struct screen_write_ctx *, u_int, u_int); void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int, u_int); @@ -2433,6 +2483,8 @@ void screen_set_path(struct screen *, const char *); void screen_push_title(struct screen *); void screen_pop_title(struct screen *); void screen_resize(struct screen *, u_int, u_int, int); +void screen_resize_cursor(struct screen *, u_int, u_int, int, int, u_int *, + u_int *); void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int, u_int, int, struct grid_cell *); void screen_clear_selection(struct screen *); @@ -2440,6 +2492,8 @@ void screen_hide_selection(struct screen *); int screen_check_selection(struct screen *, u_int, u_int); void screen_select_cell(struct screen *, struct grid_cell *, const struct grid_cell *); +void screen_alternate_on(struct screen *, struct grid_cell *, int); +void screen_alternate_off(struct screen *, struct grid_cell *, int); /* window.c */ extern struct windows windows; @@ -2509,8 +2563,8 @@ void window_pane_unset_palette(struct window_pane *, u_int); void window_pane_reset_palette(struct window_pane *); int window_pane_get_palette(struct window_pane *, int); int window_pane_set_mode(struct window_pane *, - const struct window_mode *, struct cmd_find_state *, - struct args *); + struct window_pane *, const struct window_mode *, + struct cmd_find_state *, struct args *); void window_pane_reset_mode(struct window_pane *); void window_pane_reset_mode_all(struct window_pane *); int window_pane_key(struct window_pane *, struct client *, @@ -2585,7 +2639,8 @@ typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code); u_int mode_tree_count_tagged(struct mode_tree_data *); void *mode_tree_get_current(struct mode_tree_data *); void mode_tree_expand_current(struct mode_tree_data *); -void mode_tree_set_current(struct mode_tree_data *, uint64_t); +void mode_tree_expand(struct mode_tree_data *, uint64_t); +int mode_tree_set_current(struct mode_tree_data *, uint64_t); void mode_tree_each_tagged(struct mode_tree_data *, mode_tree_each_cb, struct client *, key_code, int); void mode_tree_down(struct mode_tree_data *, int); @@ -2627,6 +2682,8 @@ void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); void window_copy_pageup(struct window_pane *, int); void window_copy_start_drag(struct client *, struct mouse_event *); +char *window_copy_get_word(struct window_pane *, u_int, u_int); +char *window_copy_get_line(struct window_pane *, u_int); /* names.c */ void check_window_name(struct window *); @@ -2665,7 +2722,7 @@ struct session *session_create(const char *, const char *, const char *, void session_destroy(struct session *, int, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); -int session_check_name(const char *); +char *session_check_name(const char *); void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); @@ -2727,6 +2784,7 @@ __dead void printflike(1, 2) fatal(const char *, ...); __dead void printflike(1, 2) fatalx(const char *, ...); /* menu.c */ +#define MENU_NOMOUSE 0x1 struct menu *menu_create(const char *); void menu_add_items(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, @@ -2734,12 +2792,22 @@ void menu_add_items(struct menu *, const struct menu_item *, void menu_add_item(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); - void menu_free(struct menu *); int menu_display(struct menu *, int, struct cmdq_item *, u_int, u_int, struct client *, struct cmd_find_state *, menu_choice_cb, void *); +/* popup.c */ +#define POPUP_WRITEKEYS 0x1 +#define POPUP_CLOSEEXIT 0x2 +#define POPUP_CLOSEEXITZERO 0x4 +u_int popup_width(struct cmdq_item *, u_int, const char **, + struct client *, struct cmd_find_state *); +u_int popup_height(u_int, const char **); +int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, + u_int, u_int, const char **, const char *, const char *, + const char *, struct client *, struct cmd_find_state *); + /* style.c */ int style_parse(struct style *,const struct grid_cell *, const char *); @@ -99,7 +99,7 @@ tty_acs_needed(struct tty *tty) tty_term_number(tty->term, TTYC_U8) == 0) return (1); - if (tty->flags & TTY_UTF8) + if (tty->client->flags & CLIENT_UTF8) return (0); return (1); } diff --git a/tty-features.c b/tty-features.c new file mode 100644 index 00000000..30d3d1a0 --- /dev/null +++ b/tty-features.c @@ -0,0 +1,348 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com> + * + * 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 <sys/types.h> + +#include <stdlib.h> +#include <string.h> + +#include "tmux.h" + +/* + * Still hardcoded: + * - mouse (under kmous capability); + * - default colours (under AX or op capabilities); + * - AIX colours (under colors >= 16); + * - alternate escape (if terminal is VT100-like). + * + * Also: + * - DECFRA uses a flag instead of capabilities; + * - UTF-8 is a separate flag on the client; needed for unattached clients. + */ + +/* A named terminal feature. */ +struct tty_feature { + const char *name; + const char **capabilities; + int flags; +}; + +/* Terminal has xterm(1) title setting. */ +static const char *tty_feature_title_capabilities[] = { + "tsl=\\E]0;", /* should be using TS really */ + "fsl=\\a", + NULL +}; +static const struct tty_feature tty_feature_title = { + "title", + tty_feature_title_capabilities, + 0 +}; + +/* Terminal can set the clipboard with OSC 52. */ +static const char *tty_feature_clipboard_capabilities[] = { + "Ms=\\E]52;%p1%s;%p2%s\\a", + NULL +}; +static const struct tty_feature tty_feature_clipboard = { + "clipboard", + tty_feature_clipboard_capabilities, + 0 +}; + +/* + * Terminal supports RGB colour. This replaces setab and setaf also since + * terminals with RGB have versions that do not allow setting colours from the + * 256 palette. + */ +static const char *tty_feature_rgb_capabilities[] = { + "AX", + "setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm", + "setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm", + "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + NULL +}; +static const struct tty_feature tty_feature_rgb = { + "RGB", + tty_feature_rgb_capabilities, + TERM_256COLOURS|TERM_RGBCOLOURS +}; + +/* Terminal supports 256 colours. */ +static const char *tty_feature_256_capabilities[] = { + "AX", + "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + NULL +}; +static const struct tty_feature tty_feature_256 = { + "256", + tty_feature_256_capabilities, + TERM_256COLOURS +}; + +/* Terminal supports overline. */ +static const char *tty_feature_overline_capabilities[] = { + "Smol=\\E[53m", + NULL +}; +static const struct tty_feature tty_feature_overline = { + "overline", + tty_feature_overline_capabilities, + 0 +}; + +/* Terminal supports underscore styles. */ +static const char *tty_feature_usstyle_capabilities[] = { + "Smulx=\E[4::%p1%dm", + "Setulc=\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m", + NULL +}; +static const struct tty_feature tty_feature_usstyle = { + "usstyle", + tty_feature_usstyle_capabilities, + 0 +}; + +/* Terminal supports bracketed paste. */ +static const char *tty_feature_bpaste_capabilities[] = { + "Enbp=\\E[?2004h", + "Dsbp=\\E[?2004l", + NULL +}; +static const struct tty_feature tty_feature_bpaste = { + "bpaste", + tty_feature_bpaste_capabilities, + 0 +}; + +/* Terminal supports focus reporting. */ +static const char *tty_feature_focus_capabilities[] = { + "Enfcs=\\E[?1004h", + "Dsfcs=\\E[?1004l", + NULL +}; +static const struct tty_feature tty_feature_focus = { + "focus", + tty_feature_focus_capabilities, + 0 +}; + +/* Terminal supports cursor styles. */ +static const char *tty_feature_cstyle_capabilities[] = { + "Ss=\\E[%p1%d q", + "Se=\\E[2 q", + NULL +}; +static const struct tty_feature tty_feature_cstyle = { + "cstyle", + tty_feature_cstyle_capabilities, + 0 +}; + +/* Terminal supports cursor colours. */ +static const char *tty_feature_ccolour_capabilities[] = { + "Cs=\\E]12;%p1%s\\a", + "Cr=\\E]112\\a", + NULL +}; +static const struct tty_feature tty_feature_ccolour = { + "ccolour", + tty_feature_ccolour_capabilities, + 0 +}; + +/* Terminal supports strikethrough. */ +static const char *tty_feature_strikethrough_capabilities[] = { + "smxx=\\E[9m", + NULL +}; +static const struct tty_feature tty_feature_strikethrough = { + "strikethrough", + tty_feature_strikethrough_capabilities, + 0 +}; + +/* Terminal supports synchronized updates. */ +static const char *tty_feature_sync_capabilities[] = { + "Sync=\\EP=%p1%ds\\E\\\\", + NULL +}; +static const struct tty_feature tty_feature_sync = { + "sync", + tty_feature_sync_capabilities, + 0 +}; + +/* Terminal supports DECSLRM margins. */ +static const char *tty_feature_margins_capabilities[] = { + "Enmg=\\E[?69h", + "Dsmg=\\E[?69l", + "Clmg=\\E[s", + "Cmg=\\E[%i%p1%d;%p2%ds", + NULL +}; +static const struct tty_feature tty_feature_margins = { + "margins", + tty_feature_margins_capabilities, + TERM_DECSLRM +}; + +/* Terminal supports DECFRA rectangle fill. */ +static const struct tty_feature tty_feature_rectfill = { + "rectfill", + NULL, + TERM_DECFRA +}; + +/* Available terminal features. */ +static const struct tty_feature *tty_features[] = { + &tty_feature_256, + &tty_feature_bpaste, + &tty_feature_ccolour, + &tty_feature_clipboard, + &tty_feature_cstyle, + &tty_feature_focus, + &tty_feature_margins, + &tty_feature_overline, + &tty_feature_rectfill, + &tty_feature_rgb, + &tty_feature_strikethrough, + &tty_feature_sync, + &tty_feature_title, + &tty_feature_usstyle +}; + +void +tty_add_features(int *feat, const char *s, const char *separators) +{ + const struct tty_feature *tf; + char *next, *loop, *copy; + u_int i; + + log_debug("adding terminal features %s", s); + + loop = copy = xstrdup(s); + while ((next = strsep(&loop, separators)) != NULL) { + for (i = 0; i < nitems(tty_features); i++) { + tf = tty_features[i]; + if (strcasecmp(tf->name, next) == 0) + break; + } + if (i == nitems(tty_features)) { + log_debug("unknown terminal feature: %s", next); + break; + } + if (~(*feat) & (1 << i)) { + log_debug("adding terminal feature: %s", tf->name); + (*feat) |= (1 << i); + } + } + free(copy); +} + +const char * +tty_get_features(int feat) +{ + const struct tty_feature *tf; + static char s[512]; + u_int i; + + *s = '\0'; + for (i = 0; i < nitems(tty_features); i++) { + if (~feat & (1 << i)) + continue; + tf = tty_features[i]; + + strlcat(s, tf->name, sizeof s); + strlcat(s, ",", sizeof s); + } + if (*s != '\0') + s[strlen(s) - 1] = '\0'; + return (s); +} + +int +tty_apply_features(struct tty_term *term, int feat) +{ + const struct tty_feature *tf; + const char **capability; + u_int i; + + if (feat == 0) + return (0); + log_debug("applying terminal features: %s", tty_get_features(feat)); + + for (i = 0; i < nitems(tty_features); i++) { + if ((term->features & (1 << i)) || (~feat & (1 << i))) + continue; + tf = tty_features[i]; + + log_debug("applying terminal feature: %s", tf->name); + if (tf->capabilities != NULL) { + capability = tf->capabilities; + while (*capability != NULL) { + log_debug("adding capability: %s", *capability); + tty_term_apply(term, *capability, 1); + capability++; + } + } + term->flags |= tf->flags; + } + if ((term->features | feat) == term->features) + return (0); + term->features |= feat; + return (1); +} + +void +tty_default_features(int *feat, const char *name, u_int version) +{ + static struct { + const char *name; + u_int version; + const char *features; + } table[] = { +#define TTY_FEATURES_BASE_MODERN_XTERM "256,RGB,bpaste,clipboard,strikethrough,title" + { .name = "mintty", + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,margins,overline" + }, + { .name = "tmux", + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,overline,usstyle" + }, + { .name = "rxvt-unicode", + .features = "256,bpaste,ccolour,cstyle,title" + }, + { .name = "iTerm2", + .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,margins,sync" + }, + { .name = "XTerm", + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,margins,rectfill" + } + }; + u_int i; + + for (i = 0; i < nitems(table); i++) { + if (strcmp(table[i].name, name) != 0) + continue; + if (version != 0 && version < table[i].version) + continue; + tty_add_features(feat, table[i].features, ","); + } +} @@ -52,7 +52,7 @@ static int tty_keys_clipboard(struct tty *, const char *, size_t, size_t *); static int tty_keys_device_attributes(struct tty *, const char *, size_t, size_t *); -static int tty_keys_device_status_report(struct tty *, const char *, +static int tty_keys_extended_device_attributes(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ @@ -61,6 +61,9 @@ struct tty_default_key_raw { key_code key; }; static const struct tty_default_key_raw tty_default_raw_keys[] = { + /* Application escape. */ + { "\033O[", '\033' }, + /* * Numeric keypad. Just use the vt100 escape sequences here and always * put the terminal into keypad_xmit mode. Translation of numbers @@ -609,8 +612,8 @@ tty_keys_next(struct tty *tty) goto partial_key; } - /* Is this a device status report response? */ - switch (tty_keys_device_status_report(tty, buf, len, &size)) { + /* Is this an extended device attributes response? */ + switch (tty_keys_extended_device_attributes(tty, buf, len, &size)) { case 0: /* yes */ key = KEYC_UNKNOWN; goto complete_key; @@ -933,7 +936,7 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, *size = 0; - /* First three bytes are always \033]52;. */ + /* First five bytes are always \033]52;. */ if (buf[0] != '\033') return (-1); if (len == 1) @@ -1007,8 +1010,8 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, } /* - * Handle device attributes input. Returns 0 for success, -1 for failure, 1 for - * partial. + * Handle secondary device attributes input. Returns 0 for success, -1 for + * failure, 1 for partial. */ static int tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, @@ -1017,7 +1020,6 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, struct client *c = tty->client; u_int i, n = 0; char tmp[64], *endptr, p[32] = { 0 }, *cp, *next; - int flags = 0; *size = 0; if (tty->flags & TTY_HAVEDA) @@ -1032,15 +1034,17 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, return (-1); if (len == 2) return (1); - if (buf[2] != '?') + if (buf[2] != '>') return (-1); if (len == 3) return (1); /* Copy the rest up to a 'c'. */ - for (i = 0; i < (sizeof tmp) - 1 && buf[3 + i] != 'c'; i++) { + for (i = 0; i < (sizeof tmp) - 1; i++) { if (3 + i == len) return (1); + if (buf[3 + i] == 'c') + break; tmp[i] = buf[3 + i]; } if (i == (sizeof tmp) - 1) @@ -1048,7 +1052,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, tmp[i] = '\0'; *size = 4 + i; - /* Convert version numbers. */ + /* Convert all arguments to numbers. */ cp = tmp; while ((next = strsep(&cp, ";")) != NULL) { p[n] = strtoul(next, &endptr, 10); @@ -1057,73 +1061,95 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, n++; } - /* Set terminal flags. */ + /* Add terminal features. */ switch (p[0]) { - case 64: /* VT420 */ - flags |= (TERM_DECFRA|TERM_DECSLRM); + case 41: /* VT420 */ + tty_add_features(&c->term_features, + "margins," + "rectfill", + ","); + break; + case 'M': /* mintty */ + tty_default_features(&c->term_features, "mintty", 0); + break; + case 'T': /* tmux */ + tty_default_features(&c->term_features, "tmux", 0); + break; + case 'U': /* rxvt-unicode */ + tty_default_features(&c->term_features, "rxvt-unicode", 0); break; } - for (i = 1; i < n; i++) - log_debug("%s: DA feature: %d", c->name, p[i]); - log_debug("%s: received DA %.*s", c->name, (int)*size, buf); + log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); - tty_set_flags(tty, flags); + tty_update_features(tty); tty->flags |= TTY_HAVEDA; return (0); } /* - * Handle device status report input. Returns 0 for success, -1 for failure, 1 - * for partial. + * Handle extended device attributes input. Returns 0 for success, -1 for + * failure, 1 for partial. */ static int -tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, - size_t *size) +tty_keys_extended_device_attributes(struct tty *tty, const char *buf, + size_t len, size_t *size) { struct client *c = tty->client; u_int i; - char tmp[64]; - int flags = 0; + char tmp[128]; *size = 0; - if (tty->flags & TTY_HAVEDSR) + if (tty->flags & TTY_HAVEXDA) return (-1); - /* First three bytes are always \033[. */ + /* First four bytes are always \033P>|. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); - if (buf[1] != '[') + if (buf[1] != 'P') return (-1); if (len == 2) return (1); - if (buf[2] != 'I' && buf[2] != 'T') + if (buf[2] != '>') return (-1); if (len == 3) return (1); + if (buf[3] != '|') + return (-1); + if (len == 4) + return (1); - /* Copy the rest up to a 'n'. */ - for (i = 0; i < (sizeof tmp) - 1 && buf[2 + i] != 'n'; i++) { - if (2 + i == len) + /* Copy the rest up to a '\033\\'. */ + for (i = 0; i < (sizeof tmp) - 1; i++) { + if (4 + i == len) return (1); - tmp[i] = buf[2 + i]; + if (buf[4 + i - 1] == '\033' && buf[4 + i] == '\\') + break; + tmp[i] = buf[4 + i]; } if (i == (sizeof tmp) - 1) return (-1); - tmp[i] = '\0'; - *size = 3 + i; - - /* Set terminal flags. */ - if (strncmp(tmp, "ITERM2 ", 7) == 0) - flags |= (TERM_DECSLRM|TERM_256COLOURS|TERM_RGBCOLOURS); - if (strncmp(tmp, "TMUX ", 5) == 0) - flags |= (TERM_256COLOURS|TERM_RGBCOLOURS); - log_debug("%s: received DSR %.*s", c->name, (int)*size, buf); - - tty_set_flags(tty, flags); - tty->flags |= TTY_HAVEDSR; + tmp[i - 1] = '\0'; + *size = 5 + i; + + /* Add terminal features. */ + if (strncmp(tmp, "iTerm2 ", 7) == 0) + tty_default_features(&c->term_features, "iTerm2", 0); + else if (strncmp(tmp, "tmux ", 5) == 0) + tty_default_features(&c->term_features, "tmux", 0); + else if (strncmp(tmp, "XTerm(", 6) == 0) + tty_default_features(&c->term_features, "xterm", 0); + else if (strncmp(tmp, "mintty ", 7) == 0) + tty_default_features(&c->term_features, "mintty", 0); + log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); + + free(c->term_type); + c->term_type = xstrdup(tmp); + + tty_update_features(tty); + tty->flags |= TTY_HAVEXDA; return (0); } @@ -30,7 +30,6 @@ #include "tmux.h" -static void tty_term_override(struct tty_term *, const char *); static char *tty_term_strip(const char *); struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms); @@ -65,6 +64,8 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_BOLD] = { TTYCODE_STRING, "bold" }, [TTYC_CIVIS] = { TTYCODE_STRING, "civis" }, [TTYC_CLEAR] = { TTYCODE_STRING, "clear" }, + [TTYC_CLMG] = { TTYCODE_STRING, "Clmg" }, + [TTYC_CMG] = { TTYCODE_STRING, "Cmg" }, [TTYC_CNORM] = { TTYCODE_STRING, "cnorm" }, [TTYC_COLORS] = { TTYCODE_NUMBER, "colors" }, [TTYC_CR] = { TTYCODE_STRING, "Cr" }, @@ -85,12 +86,18 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DSFCS] = { TTYCODE_STRING, "Dsfcs" }, + [TTYC_DSBP] = { TTYCODE_STRING, "Dsbp" }, + [TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" }, [TTYC_E3] = { TTYCODE_STRING, "E3" }, [TTYC_ECH] = { TTYCODE_STRING, "ech" }, [TTYC_ED] = { TTYCODE_STRING, "ed" }, [TTYC_EL1] = { TTYCODE_STRING, "el1" }, [TTYC_EL] = { TTYCODE_STRING, "el" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, + [TTYC_ENBP] = { TTYCODE_STRING, "Enbp" }, + [TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" }, + [TTYC_ENMG] = { TTYCODE_STRING, "Enmg" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, [TTYC_HOME] = { TTYCODE_STRING, "home" }, [TTYC_HPA] = { TTYCODE_STRING, "hpa" }, @@ -241,8 +248,8 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_OP] = { TTYCODE_STRING, "op" }, [TTYC_REV] = { TTYCODE_STRING, "rev" }, [TTYC_RGB] = { TTYCODE_FLAG, "RGB" }, - [TTYC_RI] = { TTYCODE_STRING, "ri" }, [TTYC_RIN] = { TTYCODE_STRING, "rin" }, + [TTYC_RI] = { TTYCODE_STRING, "ri" }, [TTYC_RMACS] = { TTYCODE_STRING, "rmacs" }, [TTYC_RMCUP] = { TTYCODE_STRING, "rmcup" }, [TTYC_RMKX] = { TTYCODE_STRING, "rmkx" }, @@ -263,12 +270,13 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SMXX] = { TTYCODE_STRING, "smxx" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, + [TTYC_SYNC] = { TTYCODE_STRING, "Sync" }, [TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, [TTYC_U8] = { TTYCODE_NUMBER, "U8" }, [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, - [TTYC_XT] = { TTYCODE_FLAG, "XT" }, + [TTYC_XT] = { TTYCODE_FLAG, "XT" } }; u_int @@ -337,22 +345,18 @@ tty_term_override_next(const char *s, size_t *offset) return (value); } -static void -tty_term_override(struct tty_term *term, const char *override) +void +tty_term_apply(struct tty_term *term, const char *capabilities, int quiet) { const struct tty_term_code_entry *ent; struct tty_code *code; size_t offset = 0; char *cp, *value, *s; - const char *errstr; + const char *errstr, *name = term->name; u_int i; int n, remove; - s = tty_term_override_next(override, &offset); - if (s == NULL || fnmatch(s, term->name, 0) != 0) - return; - - while ((s = tty_term_override_next(override, &offset)) != NULL) { + while ((s = tty_term_override_next(capabilities, &offset)) != NULL) { if (*s == '\0') continue; value = NULL; @@ -371,12 +375,14 @@ tty_term_override(struct tty_term *term, const char *override) } else value = xstrdup(""); - if (remove) - log_debug("%s override: %s@", term->name, s); - else if (*value == '\0') - log_debug("%s override: %s", term->name, s); - else - log_debug("%s override: %s=%s", term->name, s, value); + if (!quiet) { + if (remove) + log_debug("%s override: %s@", name, s); + else if (*value == '\0') + log_debug("%s override: %s", name, s); + else + log_debug("%s override: %s=%s", name, s, value); + } for (i = 0; i < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; @@ -415,8 +421,32 @@ tty_term_override(struct tty_term *term, const char *override) } } +void +tty_term_apply_overrides(struct tty_term *term) +{ + struct options_entry *o; + struct options_array_item *a; + union options_value *ov; + const char *s; + size_t offset; + char *first; + + o = options_get_only(global_options, "terminal-overrides"); + a = options_array_first(o); + while (a != NULL) { + ov = options_array_item_value(a); + s = ov->string; + + offset = 0; + first = tty_term_override_next(s, &offset); + if (first != NULL && fnmatch(first, term->name, 0) == 0) + tty_term_apply(term, s + offset, 0); + a = options_array_next(a); + } +} + struct tty_term * -tty_term_find(char *name, int fd, char **cause) +tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) { struct tty_term *term; const struct tty_term_code_entry *ent; @@ -427,19 +457,14 @@ tty_term_find(char *name, int fd, char **cause) u_int i; int n, error; const char *s, *acs; + size_t offset; + char *first; - LIST_FOREACH(term, &tty_terms, entry) { - if (strcmp(term->name, name) == 0) { - term->references++; - return (term); - } - } - log_debug("new term: %s", name); + log_debug("adding term %s", name); - term = xmalloc(sizeof *term); + term = xcalloc(1, sizeof *term); + term->tty = tty; term->name = xstrdup(name); - term->references = 1; - term->flags = 0; term->codes = xcalloc(tty_term_ncodes(), sizeof *term->codes); LIST_INSERT_HEAD(&tty_terms, term, entry); @@ -497,12 +522,17 @@ tty_term_find(char *name, int fd, char **cause) } } - /* Apply terminal overrides. */ - o = options_get_only(global_options, "terminal-overrides"); + /* Apply terminal features. */ + o = options_get_only(global_options, "terminal-features"); a = options_array_first(o); while (a != NULL) { ov = options_array_item_value(a); - tty_term_override(term, ov->string); + s = ov->string; + + offset = 0; + first = tty_term_override_next(s, &offset); + if (first != NULL && fnmatch(first, term->name, 0) == 0) + tty_add_features(feat, s + offset, ":"); a = options_array_next(a); } @@ -512,6 +542,9 @@ tty_term_find(char *name, int fd, char **cause) del_curterm(cur_term); #endif + /* Apply overrides so any capabilities used for features are changed. */ + tty_term_apply_overrides(term); + /* These are always required. */ if (!tty_term_has(term, TTYC_CLEAR)) { xasprintf(cause, "terminal does not support clear"); @@ -522,21 +555,33 @@ tty_term_find(char *name, int fd, char **cause) goto error; } - /* These can be emulated so one of the two is required. */ - if (!tty_term_has(term, TTYC_CUD1) && !tty_term_has(term, TTYC_CUD)) { - xasprintf(cause, "terminal does not support cud1 or cud"); - goto error; + /* + * If TERM has XT or clear starts with CSI then it is safe to assume + * the terminal is derived from the VT100. This controls whether device + * attributes requests are sent to get more information. + * + * This is a bit of a hack but there aren't that many alternatives. + * Worst case tmux will just fall back to using whatever terminfo(5) + * says without trying to correct anything that is missing. + * + * Also add few features that VT100-like terminals should either + * support or safely ignore. + */ + s = tty_term_string(term, TTYC_CLEAR); + if (tty_term_flag(term, TTYC_XT) || strncmp(s, "\033[", 2) == 0) { + term->flags |= TERM_VT100LIKE; + tty_add_features(feat, "bpaste,focus,title", ","); } - /* Set flag if terminal has 256 colours. */ - if (tty_term_number(term, TTYC_COLORS) >= 256) - term->flags |= TERM_256COLOURS; + /* Add RGB feature if terminal has RGB colours. */ + if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) && + (!tty_term_has(term, TTYC_SETRGBF) || + !tty_term_has(term, TTYC_SETRGBB))) + tty_add_features(feat, "RGB", ","); - /* Set flag if terminal has RGB colours. */ - if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) || - (tty_term_has(term, TTYC_SETRGBF) && - tty_term_has(term, TTYC_SETRGBB))) - term->flags |= TERM_RGBCOLOURS; + /* Apply the features and overrides again. */ + tty_apply_features(term, *feat); + tty_term_apply_overrides(term); /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 @@ -560,18 +605,6 @@ tty_term_find(char *name, int fd, char **cause) for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) term->acs[(u_char) acs[0]][0] = acs[1]; - /* On terminals with xterm titles (XT), fill in tsl and fsl. */ - if (tty_term_flag(term, TTYC_XT) && - !tty_term_has(term, TTYC_TSL) && - !tty_term_has(term, TTYC_FSL)) { - code = &term->codes[TTYC_TSL]; - code->value.string = xstrdup("\033]0;"); - code->type = TTYCODE_STRING; - code = &term->codes[TTYC_FSL]; - code->value.string = xstrdup("\007"); - code->type = TTYCODE_STRING; - } - /* Log the capabilities. */ for (i = 0; i < tty_term_ncodes(); i++) log_debug("%s%s", name, tty_term_describe(term, i)); @@ -588,10 +621,7 @@ tty_term_free(struct tty_term *term) { u_int i; - if (--term->references != 0) - return; - - LIST_REMOVE(term, entry); + log_debug("removing term %s", term->name); for (i = 0; i < tty_term_ncodes(); i++) { if (term->codes[i].type == TTYCODE_STRING) @@ -599,6 +629,7 @@ tty_term_free(struct tty_term *term) } free(term->codes); + LIST_REMOVE(term, entry); free(term->name); free(term); } @@ -74,7 +74,7 @@ static void tty_default_attributes(struct tty *, struct window_pane *, u_int); #define tty_use_margin(tty) \ - ((tty->term->flags|tty->term_flags) & TERM_DECSLRM) + (tty->term->flags & TERM_DECSLRM) #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) @@ -96,27 +96,19 @@ tty_create_log(void) } int -tty_init(struct tty *tty, struct client *c, int fd, char *term) +tty_init(struct tty *tty, struct client *c, int fd) { if (!isatty(fd)) return (-1); memset(tty, 0, sizeof *tty); - if (term == NULL || *term == '\0') - tty->term_name = xstrdup("unknown"); - else - tty->term_name = xstrdup(term); - tty->fd = fd; tty->client = c; tty->cstyle = 0; tty->ccolour = xstrdup(""); - tty->flags = 0; - tty->term_flags = 0; - return (0); } @@ -256,7 +248,10 @@ tty_write_callback(__unused int fd, __unused short events, void *data) int tty_open(struct tty *tty, char **cause) { - tty->term = tty_term_find(tty->term_name, tty->fd, cause); + struct client *c = tty->client; + + tty->term = tty_term_create(tty, c->term_name, &c->term_features, + tty->fd, cause); if (tty->term == NULL) { tty_close(tty); return (-1); @@ -292,7 +287,7 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data) struct client *c = tty->client; log_debug("%s: start timer fired", c->name); - tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); + tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); } void @@ -335,17 +330,12 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1006l\033[?1005l"); } - if (tty_term_flag(tty->term, TTYC_XT)) { - if (options_get_number(global_options, "focus-events")) { - tty->flags |= TTY_FOCUS; - tty_puts(tty, "\033[?1004h"); - } - if (~tty->flags & TTY_HAVEDA) - tty_puts(tty, "\033[c"); - if (~tty->flags & TTY_HAVEDSR) - tty_puts(tty, "\033[1337n"); - } else - tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); + if (options_get_number(global_options, "focus-events")) { + tty->flags |= TTY_FOCUS; + tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); + } + if (tty->term->flags & TERM_VT100LIKE) + tty_puts(tty, "\033[?7727h"); evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); @@ -362,6 +352,21 @@ tty_start_tty(struct tty *tty) } void +tty_send_requests(struct tty *tty) +{ + if (~tty->flags & TTY_STARTED) + return; + + if (tty->term->flags & TERM_VT100LIKE) { + if (~tty->flags & TTY_HAVEDA) + tty_puts(tty, "\033[>c"); + if (~tty->flags & TTY_HAVEXDA) + tty_puts(tty, "\033[>q"); + } else + tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); +} + +void tty_stop_tty(struct tty *tty) { struct winsize ws; @@ -401,7 +406,7 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); } if (tty->mode & MODE_BRACKETPASTE) - tty_raw(tty, "\033[?2004l"); + tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP)); if (*tty->ccolour != '\0') tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); @@ -411,15 +416,15 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, "\033[?1006l\033[?1005l"); } - if (tty_term_flag(tty->term, TTYC_XT)) { - if (tty->flags & TTY_FOCUS) { - tty->flags &= ~TTY_FOCUS; - tty_raw(tty, "\033[?1004l"); - } + if (tty->flags & TTY_FOCUS) { + tty->flags &= ~TTY_FOCUS; + tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); } + if (tty->term->flags & TERM_VT100LIKE) + tty_raw(tty, "\033[?7727l"); if (tty_use_margin(tty)) - tty_raw(tty, "\033[?69l"); /* DECLRMM */ + tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); setblocking(tty->fd, 1); @@ -454,18 +459,19 @@ void tty_free(struct tty *tty) { tty_close(tty); - free(tty->ccolour); - free(tty->term_name); } void -tty_set_flags(struct tty *tty, int flags) +tty_update_features(struct tty *tty) { - tty->term_flags |= flags; + struct client *c = tty->client; + + if (tty_apply_features(tty->term, c->term_features)) + tty_term_apply_overrides(tty->term); if (tty_use_margin(tty)) - tty_puts(tty, "\033[?69h"); /* DECLRMM */ + tty_putcode(tty, TTYC_ENMG); } void @@ -658,7 +664,8 @@ tty_force_cursor_colour(struct tty *tty, const char *ccolour) void tty_update_mode(struct tty *tty, int mode, struct screen *s) { - int changed; + struct client *c = tty->client; + int changed; if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) tty_force_cursor_colour(tty, s->ccolour); @@ -667,6 +674,9 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; + if (changed != 0) + log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); + if (changed & MODE_BLINKING) { if (tty_term_has(tty->term, TTYC_CVVIS)) tty_putcode(tty, TTYC_CVVIS); @@ -690,34 +700,37 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } tty->cstyle = s->cstyle; } - if (changed & ALL_MOUSE_MODES) { - if (mode & ALL_MOUSE_MODES) { - /* - * Enable the SGR (1006) extension unconditionally, as - * it is safe from misinterpretation. - */ - tty_puts(tty, "\033[?1006h"); - if (mode & MODE_MOUSE_ALL) - tty_puts(tty, "\033[?1003h"); - else if (mode & MODE_MOUSE_BUTTON) - tty_puts(tty, "\033[?1002h"); - else if (mode & MODE_MOUSE_STANDARD) - tty_puts(tty, "\033[?1000h"); - } else { - if (tty->mode & MODE_MOUSE_ALL) - tty_puts(tty, "\033[?1003l"); - else if (tty->mode & MODE_MOUSE_BUTTON) - tty_puts(tty, "\033[?1002l"); - else if (tty->mode & MODE_MOUSE_STANDARD) - tty_puts(tty, "\033[?1000l"); + if ((changed & ALL_MOUSE_MODES) && + tty_term_has(tty->term, TTYC_KMOUS)) { + if ((mode & ALL_MOUSE_MODES) == 0) tty_puts(tty, "\033[?1006l"); - } + if ((changed & MODE_MOUSE_STANDARD) && + (~mode & MODE_MOUSE_STANDARD)) + tty_puts(tty, "\033[?1000l"); + if ((changed & MODE_MOUSE_BUTTON) && + (~mode & MODE_MOUSE_BUTTON)) + tty_puts(tty, "\033[?1002l"); + if ((changed & MODE_MOUSE_ALL) && + (~mode & MODE_MOUSE_ALL)) + tty_puts(tty, "\033[?1003l"); + + if (mode & ALL_MOUSE_MODES) + tty_puts(tty, "\033[?1006h"); + if ((changed & MODE_MOUSE_STANDARD) && + (mode & MODE_MOUSE_STANDARD)) + tty_puts(tty, "\033[?1000h"); + if ((changed & MODE_MOUSE_BUTTON) && + (mode & MODE_MOUSE_BUTTON)) + tty_puts(tty, "\033[?1002h"); + if ((changed & MODE_MOUSE_ALL) && + (mode & MODE_MOUSE_ALL)) + tty_puts(tty, "\033[?1003h"); } if (changed & MODE_BRACKETPASTE) { if (mode & MODE_BRACKETPASTE) - tty_puts(tty, "\033[?2004h"); + tty_putcode(tty, TTYC_ENBP); else - tty_puts(tty, "\033[?2004l"); + tty_putcode(tty, TTYC_DSBP); } tty->mode = mode; } @@ -918,6 +931,7 @@ tty_fake_bce(const struct tty *tty, struct window_pane *wp, u_int bg) static void tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) { + struct client *c = tty->client; struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; u_int i; @@ -927,6 +941,7 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) * likely to be followed by some more scrolling. */ if (tty_large_region(tty, ctx)) { + log_debug("%s: %s, large redraw of %%%u", __func__, c->name, wp->id); wp->flags |= PANE_REDRAW; return; } @@ -1151,8 +1166,7 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, * background colour isn't default (because it doesn't work * after SGR 0). */ - if (((tty->term->flags|tty->term_flags) & TERM_DECFRA) && - !COLOUR_DEFAULT(bg)) { + if ((tty->term->flags & TERM_DECFRA) && !COLOUR_DEFAULT(bg)) { xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", py + 1, px + 1, py + ny, px + nx); tty_puts(tty, tmp); @@ -1231,7 +1245,7 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) return (gc); /* UTF-8 terminal and a UTF-8 character - fine. */ - if (tty->flags & TTY_UTF8) + if (tty->client->flags & CLIENT_UTF8) return (gc); /* Replace by the right number of underscores. */ @@ -1244,6 +1258,16 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) return (&new); } +static int +tty_check_overlay(struct tty *tty, u_int px, u_int py) +{ + struct client *c = tty->client; + + if (c->overlay_check == NULL) + return (1); + return (c->overlay_check(c, px, py)); +} + void tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, u_int px, u_int py, u_int nx, u_int atx, u_int aty) @@ -1323,7 +1347,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, grid_view_get_cell(gd, px + i, py, &gc); gcp = tty_check_codeset(tty, &gc); if (len != 0 && - ((gcp->attr & GRID_ATTR_CHARSET) || + (!tty_check_overlay(tty, atx + ux + width, aty) || + (gcp->attr & GRID_ATTR_CHARSET) || gcp->flags != last.flags || gcp->attr != last.attr || gcp->fg != last.fg || @@ -1352,7 +1377,9 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, screen_select_cell(s, &last, gcp); else memcpy(&last, gcp, sizeof last); - if (ux + gcp->data.width > nx) { + if (!tty_check_overlay(tty, atx + ux, aty)) + ux += gcp->data.width; + else if (ux + gcp->data.width > nx) { tty_attributes(tty, &last, wp); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.width; j++) { @@ -1366,7 +1393,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.size; j++) tty_putc(tty, gcp->data.data[j]); - ux += gc.data.width; + ux += gcp->data.width; } else { memcpy(buf + len, gcp->data.data, gcp->data.size); len += gcp->data.size; @@ -1397,6 +1424,36 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, tty_update_mode(tty, tty->mode, s); } +void +tty_sync_start(struct tty *tty) +{ + if (tty->flags & TTY_BLOCK) + return; + if (tty->flags & TTY_SYNCING) + return; + tty->flags |= TTY_SYNCING; + + if (tty_term_has(tty->term, TTYC_SYNC)) { + log_debug("%s sync start", tty->client->name); + tty_putcode1(tty, TTYC_SYNC, 1); + } +} + +void +tty_sync_end(struct tty *tty) +{ + if (tty->flags & TTY_BLOCK) + return; + if (~tty->flags & TTY_SYNCING) + return; + tty->flags &= ~TTY_SYNCING; + + if (tty_term_has(tty->term, TTYC_SYNC)) { + log_debug("%s sync end", tty->client->name); + tty_putcode1(tty, TTYC_SYNC, 2); + } +} + static int tty_client_ready(struct client *c, struct window_pane *wp) { @@ -1428,6 +1485,15 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), TAILQ_FOREACH(c, &clients, entry) { if (!tty_client_ready(c, wp)) continue; + if (c->flags & CLIENT_REDRAWPANES) { + /* + * Redraw is already deferred to redraw another pane - + * redraw this one also when that happens. + */ + log_debug("adding %%%u to deferred redraw", wp->id); + wp->flags |= PANE_REDRAW; + break; + } ctx->bigger = tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); @@ -1687,7 +1753,10 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) for (i = 0; i < ctx->num; i++) tty_putc(tty, '\n'); } else { - tty_cursor(tty, 0, tty->cy); + if (tty->cy == UINT_MAX) + tty_cursor(tty, 0, 0); + else + tty_cursor(tty, 0, tty->cy); tty_putcode1(tty, TTYC_INDN, ctx->num); } } @@ -1890,6 +1959,12 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) tty_invalidate(tty); } +void +tty_cmd_syncstart(struct tty *tty, __unused const struct tty_ctx *ctx) +{ + tty_sync_start(tty); +} + static void tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) { @@ -1952,7 +2027,7 @@ tty_invalidate(struct tty *tty) if (tty->flags & TTY_STARTED) { if (tty_use_margin(tty)) - tty_puts(tty, "\033[?69h"); /* DECLRMM */ + tty_putcode(tty, TTYC_ENMG); tty_putcode(tty, TTYC_SGR0); tty->mode = ALL_MODES; @@ -1999,8 +2074,12 @@ tty_region(struct tty *tty, u_int rupper, u_int rlower) * flag so further output causes a line feed). As a workaround, do an * explicit move to 0 first. */ - if (tty->cx >= tty->sx) - tty_cursor(tty, 0, tty->cy); + if (tty->cx >= tty->sx) { + if (tty->cy == UINT_MAX) + tty_cursor(tty, 0, 0); + else + tty_cursor(tty, 0, tty->cy); + } tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower); tty->cx = tty->cy = UINT_MAX; @@ -2025,8 +2104,6 @@ tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) static void tty_margin(struct tty *tty, u_int rleft, u_int rright) { - char s[64]; - if (!tty_use_margin(tty)) return; if (tty->rleft == rleft && tty->rright == rright) @@ -2038,10 +2115,9 @@ tty_margin(struct tty *tty, u_int rleft, u_int rright) tty->rright = rright; if (rleft == 0 && rright == tty->sx - 1) - snprintf(s, sizeof s, "\033[s"); + tty_putcode(tty, TTYC_CLMG); else - snprintf(s, sizeof s, "\033[%u;%us", rleft + 1, rright + 1); - tty_puts(tty, s); + tty_putcode2(tty, TTYC_CMG, rleft, rright); tty->cx = tty->cy = UINT_MAX; } @@ -2080,6 +2156,9 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy) u_int thisx, thisy; int change; + if (tty->flags & TTY_BLOCK) + return; + if (cx > tty->sx - 1) cx = tty->sx - 1; @@ -2392,14 +2471,14 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Is this a 24-bit colour? */ if (gc->fg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ - if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS) + if (tty->term->flags & TERM_RGBCOLOURS) return; colour_split_rgb(gc->fg, &r, &g, &b); gc->fg = colour_find_rgb(r, g, b); } /* How many colours does this terminal have? */ - if ((tty->term->flags|tty->term_flags) & TERM_256COLOURS) + if (tty->term->flags & TERM_256COLOURS) colours = 256; else colours = tty_term_number(tty->term, TTYC_COLORS); @@ -2441,14 +2520,14 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Is this a 24-bit colour? */ if (gc->bg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ - if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS) + if (tty->term->flags & TERM_RGBCOLOURS) return; colour_split_rgb(gc->bg, &r, &g, &b); gc->bg = colour_find_rgb(r, g, b); } /* How many colours does this terminal have? */ - if ((tty->term->flags|tty->term_flags) & TERM_256COLOURS) + if (tty->term->flags & TERM_256COLOURS) colours = 256; else colours = tty_term_number(tty->term, TTYC_COLORS); @@ -2509,7 +2588,7 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) /* Is this an aixterm bright colour? */ if (gc->fg >= 90 && gc->fg <= 97) { - if (tty->term_flags & TERM_256COLOURS) { + if (tty->term->flags & TERM_256COLOURS) { xsnprintf(s, sizeof s, "\033[%dm", gc->fg); tty_puts(tty, s); } else @@ -2541,7 +2620,7 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) /* Is this an aixterm bright colour? */ if (gc->bg >= 90 && gc->bg <= 97) { - if (tty->term_flags & TERM_256COLOURS) { + if (tty->term->flags & TERM_256COLOURS) { xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10); tty_puts(tty, s); } else @@ -2586,67 +2665,25 @@ static int tty_try_colour(struct tty *tty, int colour, const char *type) { u_char r, g, b; - char s[32]; if (colour & COLOUR_FLAG_256) { - /* - * If the user has specified -2 to the client (meaning - * TERM_256COLOURS is set), setaf and setab may not work (or - * they may not want to use them), so send the usual sequence. - * - * Also if RGB is set, setaf and setab do not support the 256 - * colour palette so use the sequences directly there too. - */ - if ((tty->term_flags & TERM_256COLOURS) || - tty_term_has(tty->term, TTYC_RGB)) - goto fallback_256; - - /* - * If the terminfo entry has 256 colours and setaf and setab - * exist, assume that they work correctly. - */ - if (tty->term->flags & TERM_256COLOURS) { - if (*type == '3') { - if (!tty_term_has(tty->term, TTYC_SETAF)) - goto fallback_256; - tty_putcode1(tty, TTYC_SETAF, colour & 0xff); - } else { - if (!tty_term_has(tty->term, TTYC_SETAB)) - goto fallback_256; - tty_putcode1(tty, TTYC_SETAB, colour & 0xff); - } - return (0); - } - goto fallback_256; + if (*type == '3' && tty_term_has(tty->term, TTYC_SETAF)) + tty_putcode1(tty, TTYC_SETAF, colour & 0xff); + else if (tty_term_has(tty->term, TTYC_SETAB)) + tty_putcode1(tty, TTYC_SETAB, colour & 0xff); + return (0); } if (colour & COLOUR_FLAG_RGB) { colour_split_rgb(colour & 0xffffff, &r, &g, &b); - if (*type == '3') { - if (!tty_term_has(tty->term, TTYC_SETRGBF)) - goto fallback_rgb; + if (*type == '3' && tty_term_has(tty->term, TTYC_SETRGBF)) tty_putcode3(tty, TTYC_SETRGBF, r, g, b); - } else { - if (!tty_term_has(tty->term, TTYC_SETRGBB)) - goto fallback_rgb; + else if (tty_term_has(tty->term, TTYC_SETRGBB)) tty_putcode3(tty, TTYC_SETRGBB, r, g, b); - } return (0); } return (-1); - -fallback_256: - xsnprintf(s, sizeof s, "\033[%s;5;%dm", type, colour & 0xff); - log_debug("%s: 256 colour fallback: %s", tty->client->name, s); - tty_puts(tty, s); - return (0); - -fallback_rgb: - xsnprintf(s, sizeof s, "\033[%s;2;%d;%d;%dm", type, r, g, b); - log_debug("%s: RGB colour fallback: %s", tty->client->name, s); - tty_puts(tty, s); - return (0); } static void diff --git a/window-buffer.c b/window-buffer.c index a1939b0f..cf707abe 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -36,7 +36,7 @@ static void window_buffer_key(struct window_mode_entry *, #define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'" #define WINDOW_BUFFER_DEFAULT_FORMAT \ - "#{buffer_size} bytes (#{t:buffer_created})" + "#{t/p:buffer_created}: #{buffer_sample}" static const struct menu_item window_buffer_menu_items[] = { { "Paste", 'p', NULL }, @@ -347,9 +347,8 @@ window_buffer_do_paste(void *modedata, void *itemdata, struct client *c, { struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item = itemdata; - struct paste_buffer *pb; - if ((pb = paste_get_name(item->name)) != NULL) + if (paste_get_name(item->name) != NULL) mode_tree_run_command(c, NULL, data->command, item->name); } diff --git a/window-client.c b/window-client.c index 4688cbf3..cd424dd7 100644 --- a/window-client.c +++ b/window-client.c @@ -37,8 +37,7 @@ static void window_client_key(struct window_mode_entry *, #define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'" #define WINDOW_CLIENT_DEFAULT_FORMAT \ - "session #{session_name} " \ - "(#{client_width}x#{client_height}, #{t:client_activity})" + "#{t/p:client_activity}: session #{session_name}" static const struct menu_item window_client_menu_items[] = { { "Detach", 'd', NULL }, diff --git a/window-copy.c b/window-copy.c index 2a47e9b0..a803f3b3 100644 --- a/window-copy.c +++ b/window-copy.c @@ -22,6 +22,7 @@ #include <regex.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include "tmux.h" @@ -205,6 +206,8 @@ struct window_copy_mode_data { struct screen *backing; int backing_written; /* backing display started */ + int viewmode; /* view mode entered */ + u_int oy; /* number of lines scrolled up */ u_int selx; /* beginning of selection */ @@ -295,6 +298,48 @@ window_copy_scroll_timer(__unused int fd, __unused short events, void *arg) } } +static struct screen * +window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, + u_int *cy, int trim) +{ + struct screen *dst; + u_int sy; + const struct grid_line *gl; + + dst = xcalloc(1, sizeof *dst); + + sy = screen_hsize(src) + screen_size_y(src); + if (trim) { + while (sy > screen_hsize(src)) { + gl = grid_peek_line(src->grid, sy - 1); + if (gl->cellused != 0) + break; + sy--; + } + } + log_debug("%s: target screen is %ux%u, source %ux%u", __func__, + screen_size_x(src), sy, screen_size_x(hint), + screen_hsize(src) + screen_size_y(src)); + screen_init(dst, screen_size_x(src), sy, screen_hlimit(src)); + grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy); + + dst->grid->sy = sy - screen_hsize(src); + dst->grid->hsize = screen_hsize(src); + dst->grid->hscrolled = src->grid->hscrolled; + if (src->cy > dst->grid->sy - 1) { + dst->cx = 0; + dst->cy = dst->grid->sy - 1; + } else { + dst->cx = src->cx; + dst->cy = src->cy; + } + + screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1, + 0, cx, cy); + + return (dst); +} + static struct window_copy_mode_data * window_copy_common_init(struct window_mode_entry *wme) { @@ -320,6 +365,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->searchmark = NULL; data->searchx = data->searchy = data->searcho = -1; data->timeout = 0; + data->viewmode = 0; data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; @@ -336,19 +382,25 @@ static struct screen * window_copy_init(struct window_mode_entry *wme, __unused struct cmd_find_state *fs, struct args *args) { - struct window_pane *wp = wme->wp; + struct window_pane *wp = wme->swp; struct window_copy_mode_data *data; + struct screen *base = &wp->base; struct screen_write_ctx ctx; - u_int i; + u_int i, cx, cy; data = window_copy_common_init(wme); + data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy, + wme->swp != wme->wp); - if (wp->fd != -1 && wp->disabled++ == 0) - bufferevent_disable(wp->event, EV_READ|EV_WRITE); - - data->backing = &wp->base; - data->cx = data->backing->cx; - data->cy = data->backing->cy; + if (cy < screen_hsize(data->backing)) { + data->cx = cx; + data->cy = 0; + data->oy = screen_hsize(data->backing) - cy; + } else { + data->cx = data->backing->cx; + data->cy = data->backing->cy; + data->oy = 0; + } data->scroll_exit = args_has(args, 'e'); data->hide_position = args_has(args, 'H'); @@ -375,6 +427,7 @@ window_copy_view_init(struct window_mode_entry *wme, struct screen *s; data = window_copy_common_init(wme); + data->viewmode = 1; data->backing = s = xmalloc(sizeof *data->backing); screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX); @@ -385,23 +438,17 @@ window_copy_view_init(struct window_mode_entry *wme, static void window_copy_free(struct window_mode_entry *wme) { - struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; evtimer_del(&data->dragtimer); - if (wp->fd != -1 && --wp->disabled == 0) - bufferevent_enable(wp->event, EV_READ|EV_WRITE); - free(data->searchmark); free(data->searchstr); - if (data->backing != &wp->base) { - screen_free(data->backing); - free(data->backing); - } - screen_free(&data->screen); + screen_free(data->backing); + free(data->backing); + screen_free(&data->screen); free(data); } @@ -425,9 +472,6 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) struct grid_cell gc; u_int old_hsize, old_cy; - if (backing == &wp->base) - return; - memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); @@ -661,31 +705,18 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) } static void -window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) +window_copy_size_changed(struct window_mode_entry *wme) { - struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; - struct screen_write_ctx ctx; - int search; - - screen_resize(s, sx, sy, 1); - if (data->backing != &wp->base) - screen_resize(data->backing, sx, sy, 1); - - if (data->cy > sy - 1) - data->cy = sy - 1; - if (data->cx > sx) - data->cx = sx; - if (data->oy > screen_hsize(data->backing)) - data->oy = screen_hsize(data->backing); + struct screen_write_ctx ctx; + int search = (data->searchmark != NULL); - search = (data->searchmark != NULL); window_copy_clear_selection(wme); window_copy_clear_marks(wme); screen_write_start(&ctx, NULL, s); - window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1); + window_copy_write_lines(wme, &ctx, 0, screen_size_y(s)); screen_write_stop(&ctx); if (search && !data->timeout) @@ -693,7 +724,25 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; +} + +static void +window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) +{ + struct window_copy_mode_data *data = wme->data; + struct screen *s = &data->screen; + + screen_resize(s, sx, sy, 0); + screen_resize_cursor(data->backing, sx, sy, 1, 0, NULL, NULL); + + if (data->cy > sy - 1) + data->cy = sy - 1; + if (data->cx > sx) + data->cx = sx; + if (data->oy > screen_hsize(data->backing)) + data->oy = screen_hsize(data->backing); + window_copy_size_changed(wme); window_copy_redraw_screen(wme); } @@ -1043,14 +1092,15 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; + struct screen *s = data->backing; u_int oy; - oy = screen_hsize(data->backing) + data->cy - data->oy; + oy = screen_hsize(s) + data->cy - data->oy; if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) window_copy_other_end(wme); data->cy = screen_size_y(&data->screen) - 1; - data->cx = window_copy_find_length(wme, data->cy); + data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy); data->oy = 0; if (data->searchmark != NULL && !data->timeout) @@ -1690,11 +1740,10 @@ window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs) if (cs->args->argc == 3) prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp); - if (s != NULL && *cs->args->argv[1] != '\0') { + if (s != NULL && cs->args->argc > 1 && *cs->args->argv[1] != '\0') command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); - window_copy_copy_pipe(wme, s, prefix, command); - free(command); - } + window_copy_copy_pipe(wme, s, prefix, command); + free(command); free(prefix); return (WINDOW_COPY_CMD_NOTHING); @@ -1984,6 +2033,25 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) return (action); } +static enum window_copy_cmd_action +window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_pane *wp = wme->swp; + struct window_copy_mode_data *data = wme->data; + + if (data->viewmode) + return (WINDOW_COPY_CMD_NOTHING); + + screen_free(data->backing); + free(data->backing); + data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, + NULL, wme->swp != wme->wp); + + window_copy_size_changed(wme); + return (WINDOW_COPY_CMD_REDRAW); +} + static const struct { const char *command; int minargs; @@ -2009,11 +2077,11 @@ static const struct { window_copy_cmd_copy_end_of_line }, { "copy-line", 0, 1, 0, window_copy_cmd_copy_line }, - { "copy-pipe-no-clear", 1, 2, 0, + { "copy-pipe-no-clear", 0, 2, 0, window_copy_cmd_copy_pipe_no_clear }, - { "copy-pipe", 1, 2, 0, + { "copy-pipe", 0, 2, 0, window_copy_cmd_copy_pipe }, - { "copy-pipe-and-cancel", 1, 2, 0, + { "copy-pipe-and-cancel", 0, 2, 0, window_copy_cmd_copy_pipe_and_cancel }, { "copy-selection-no-clear", 0, 1, 0, window_copy_cmd_copy_selection_no_clear }, @@ -2089,6 +2157,8 @@ static const struct { window_copy_cmd_previous_word }, { "rectangle-toggle", 0, 0, 0, window_copy_cmd_rectangle_toggle }, + { "refresh-from-pane", 0, 0, 0, + window_copy_cmd_refresh_from_pane }, { "scroll-down", 0, 0, 1, window_copy_cmd_scroll_down }, { "scroll-down-and-cancel", 0, 0, 0, @@ -2932,6 +3002,7 @@ window_copy_write_line(struct window_mode_entry *wme, struct grid_cell gc; char hdr[512]; size_t size = 0; + u_int hsize = screen_hsize(data->backing); style_apply(&gc, oo, "mode-style"); gc.flags |= GRID_FLAG_NOPALETTE; @@ -2940,23 +3011,20 @@ window_copy_write_line(struct window_mode_entry *wme, if (data->searchmark == NULL) { if (data->timeout) { size = xsnprintf(hdr, sizeof hdr, - "(timed out) [%u/%u]", data->oy, - screen_hsize(data->backing)); + "(timed out) [%u/%u]", data->oy, hsize); } else { size = xsnprintf(hdr, sizeof hdr, - "[%u/%u]", data->oy, - screen_hsize(data->backing)); + "[%u/%u]", data->oy, hsize); } } else { if (data->searchthis == -1) { size = xsnprintf(hdr, sizeof hdr, "(%u results) [%d/%u]", data->searchcount, - data->oy, screen_hsize(data->backing)); + data->oy, hsize); } else { size = xsnprintf(hdr, sizeof hdr, "(%u/%u results) [%d/%u]", data->searchthis, - data->searchcount, data->oy, - screen_hsize(data->backing)); + data->searchcount, data->oy, hsize); } } if (size > screen_size_x(s)) @@ -2968,8 +3036,7 @@ window_copy_write_line(struct window_mode_entry *wme, if (size < screen_size_x(s)) { screen_write_cursormove(ctx, 0, py, 0); - screen_write_copy(ctx, data->backing, 0, - (screen_hsize(data->backing) - data->oy) + py, + screen_write_copy(ctx, data->backing, 0, hsize - data->oy + py, screen_size_x(s) - size, 1, data->searchmark, &gc); } @@ -3415,8 +3482,13 @@ window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, if (buf == NULL) return; - job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); - bufferevent_write(job_get_event(job), buf, len); + if (cmd == NULL || *cmd == '\0') + cmd = options_get_string(global_options, "copy-command"); + if (cmd != NULL && *cmd != '\0') { + job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT, + -1, -1); + bufferevent_write(job_get_event(job), buf, len); + } window_copy_copy_buffer(wme, prefix, buf, len); } @@ -4120,7 +4192,6 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, data->oy >= screen_hsize(data->backing) - 1)) goto out; - py--; py = screen_hsize(data->backing) + data->cy - data->oy; @@ -4329,7 +4400,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) data->selflag = SEL_CHAR; switch (data->selflag) { case SEL_WORD: - if (data->ws) { + if (data->ws != NULL) { window_copy_update_cursor(wme, x, y); window_copy_cursor_previous_word_pos(wme, data->ws, 0, &x, &y); diff --git a/window-tree.c b/window-tree.c index 009e93c6..d245a715 100644 --- a/window-tree.c +++ b/window-tree.c @@ -37,12 +37,14 @@ static void window_tree_key(struct window_mode_entry *, #define WINDOW_TREE_DEFAULT_FORMAT \ "#{?pane_format," \ - "#{pane_current_command} \"#{pane_title}\"" \ + "#{?pane_marked,#[reverse],}" \ + "#{pane_current_command}#{?pane_active,*,}#{?pane_marked,M,}" \ + "#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \ "," \ "#{?window_format," \ - "#{window_name}#{window_flags} " \ - "(#{window_panes} panes)" \ - "#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \ + "#{?window_marked_flag,#[reverse],}" \ + "#{window_name}#{window_flags}" \ + "#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \ "," \ "#{session_windows} windows" \ "#{?session_grouped, " \ @@ -54,8 +56,9 @@ static void window_tree_key(struct window_mode_entry *, "}" static const struct menu_item window_tree_menu_items[] = { - { "Select", 'E', NULL }, - { "Expand", 'R', NULL }, + { "Select", '\r', NULL }, + { "Expand", KEYC_RIGHT, NULL }, + { "Mark", 'm', NULL }, { "", KEYC_NONE, NULL }, { "Tag", 't', NULL }, { "Tag All", '\024', NULL }, @@ -833,7 +836,7 @@ window_tree_search(__unused void *modedata, void *itemdata, const char *ss) return (0); retval = (strstr(cmd, ss) != NULL); free(cmd); - return retval; + return (retval); } return (0); } @@ -1170,7 +1173,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, struct window_tree_modedata *data = wme->data; struct window_tree_itemdata *item, *new_item; char *name, *prompt = NULL; - struct cmd_find_state fs; + struct cmd_find_state fs, *fsp = &data->fs; int finished; u_int tagged, x, y, idx; struct session *ns; @@ -1192,6 +1195,21 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, case '>': data->offset++; break; + case 'H': + mode_tree_expand(data->data, (uint64_t)fsp->s); + mode_tree_expand(data->data, (uint64_t)fsp->wl); + if (!mode_tree_set_current(data->data, (uint64_t)wme->wp)) + mode_tree_set_current(data->data, (uint64_t)fsp->wl); + break; + case 'm': + window_tree_pull_item(item, &ns, &nwl, &nwp); + server_set_marked(ns, nwl, nwp); + mode_tree_build(data->data); + break; + case 'M': + server_clear_marked(); + mode_tree_build(data->data); + break; case 'x': window_tree_pull_item(item, &ns, &nwl, &nwp); switch (item->type) { @@ -423,8 +423,8 @@ window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) ypixel = DEFAULT_YPIXEL; log_debug("%s: @%u resize %ux%u (%ux%u)", __func__, w->id, sx, sy, - xpixel == -1 ? w->xpixel : xpixel, - ypixel == -1 ? w->ypixel : ypixel); + xpixel == -1 ? w->xpixel : (u_int)xpixel, + ypixel == -1 ? w->ypixel : (u_int)ypixel); w->sx = sx; w->sy = sy; if (xpixel != -1) @@ -886,10 +886,6 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->pipe_off = 0; wp->pipe_event = NULL; - wp->saved_grid = NULL; - wp->saved_cx = UINT_MAX; - wp->saved_cy = UINT_MAX; - screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; @@ -898,8 +894,6 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) if (gethostname(host, sizeof host) == 0) screen_set_title(&wp->base, host); - input_init(wp); - return (wp); } @@ -916,14 +910,12 @@ window_pane_destroy(struct window_pane *wp) bufferevent_free(wp->event); close(wp->fd); } - - input_free(wp); + if (wp->ictx != NULL) + input_free(wp->ictx); screen_free(&wp->status_screen); screen_free(&wp->base); - if (wp->saved_grid != NULL) - grid_destroy(wp->saved_grid); if (wp->pipe_fd != -1) { bufferevent_free(wp->pipe_event); @@ -959,7 +951,7 @@ window_pane_read_callback(__unused struct bufferevent *bufev, void *data) } log_debug("%%%u has %zu bytes", wp->id, size); - input_parse(wp); + input_parse_pane(wp); wp->pipe_off = EVBUFFER_LENGTH(evb); } @@ -984,6 +976,7 @@ window_pane_set_event(struct window_pane *wp) wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, window_pane_error_callback, wp); + wp->ictx = input_init(wp, wp->event); bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); bufferevent_enable(wp->event, EV_READ|EV_WRITE); @@ -1000,7 +993,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wp->sy = sy; log_debug("%s: %%%u resize %ux%u", __func__, wp->id, sx, sy); - screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL); + screen_resize(&wp->base, sx, sy, wp->base.saved_grid == NULL); wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) @@ -1009,90 +1002,23 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wp->flags |= (PANE_RESIZE|PANE_RESIZED); } -/* - * Enter alternative screen mode. A copy of the visible screen is saved and the - * history is not updated - */ void window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, int cursor) { - struct screen *s = &wp->base; - u_int sx, sy; - - if (wp->saved_grid != NULL) - return; if (!options_get_number(wp->options, "alternate-screen")) return; - sx = screen_size_x(s); - sy = screen_size_y(s); - - wp->saved_grid = grid_create(sx, sy, 0); - grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy); - if (cursor) { - wp->saved_cx = s->cx; - wp->saved_cy = s->cy; - } - memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell); - - grid_view_clear(s->grid, 0, 0, sx, sy, 8); - - wp->base.grid->flags &= ~GRID_HISTORY; - + screen_alternate_on(&wp->base, gc, cursor); wp->flags |= PANE_REDRAW; } -/* Exit alternate screen mode and restore the copied grid. */ void window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, int cursor) { - struct screen *s = &wp->base; - u_int sx, sy; - if (!options_get_number(wp->options, "alternate-screen")) return; - - /* - * Restore the cursor position and cell. This happens even if not - * currently in the alternate screen. - */ - if (cursor && wp->saved_cx != UINT_MAX && wp->saved_cy != UINT_MAX) { - s->cx = wp->saved_cx; - if (s->cx > screen_size_x(s) - 1) - s->cx = screen_size_x(s) - 1; - s->cy = wp->saved_cy; - if (s->cy > screen_size_y(s) - 1) - s->cy = screen_size_y(s) - 1; - memcpy(gc, &wp->saved_cell, sizeof *gc); - } - - if (wp->saved_grid == NULL) - return; - sx = screen_size_x(s); - sy = screen_size_y(s); - - /* - * If the current size is bigger, temporarily resize to the old size - * before copying back. - */ - if (sy > wp->saved_grid->sy) - screen_resize(s, sx, wp->saved_grid->sy, 1); - - /* Restore the saved grid. */ - grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); - - /* - * Turn history back on (so resize can use it) and then resize back to - * the current size. - */ - wp->base.grid->flags |= GRID_HISTORY; - if (sy > wp->saved_grid->sy || sx != wp->saved_grid->sx) - screen_resize(s, sx, sy, 1); - - grid_destroy(wp->saved_grid); - wp->saved_grid = NULL; - + screen_alternate_off(&wp->base, gc, cursor); wp->flags |= PANE_REDRAW; } @@ -1150,40 +1076,16 @@ window_pane_get_palette(struct window_pane *wp, int c) return (new); } -static void -window_pane_mode_timer(__unused int fd, __unused short events, void *arg) -{ - struct window_pane *wp = arg; - struct timeval tv = { .tv_sec = 10 }; - int n = 0; - - evtimer_del(&wp->modetimer); - evtimer_add(&wp->modetimer, &tv); - - log_debug("%%%u in mode: last=%ld", wp->id, (long)wp->modelast); - - if (wp->modelast < time(NULL) - WINDOW_MODE_TIMEOUT) { - if (ioctl(wp->fd, FIONREAD, &n) == -1 || n > 0) - window_pane_reset_mode_all(wp); - } -} - int -window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode, - struct cmd_find_state *fs, struct args *args) +window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, + const struct window_mode *mode, struct cmd_find_state *fs, + struct args *args) { - struct timeval tv = { .tv_sec = 10 }; struct window_mode_entry *wme; if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode) return (1); - wp->modelast = time(NULL); - if (TAILQ_EMPTY(&wp->modes)) { - evtimer_set(&wp->modetimer, window_pane_mode_timer, wp); - evtimer_add(&wp->modetimer, &tv); - } - TAILQ_FOREACH(wme, &wp->modes, entry) { if (wme->mode == mode) break; @@ -1194,6 +1096,7 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode, } else { wme = xcalloc(1, sizeof *wme); wme->wp = wp; + wme->swp = swp; wme->mode = mode; wme->prefix = 1; TAILQ_INSERT_HEAD(&wp->modes, wme, entry); @@ -1225,7 +1128,6 @@ window_pane_reset_mode(struct window_pane *wp) next = TAILQ_FIRST(&wp->modes); if (next == NULL) { log_debug("%s: no next mode", __func__); - evtimer_del(&wp->modetimer); wp->screen = &wp->base; } else { log_debug("%s: next mode is %s", __func__, next->mode->name); @@ -1258,8 +1160,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { - wp->modelast = time(NULL); - if (wme->mode->key != NULL) + if (wme->mode->key != NULL && c != NULL) wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); return (0); } @@ -1267,7 +1168,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) return (0); - if (input_key(wp, key, m) != 0) + if (input_key_pane(wp, key, m) != 0) return (-1); if (KEYC_IS_MOUSE(key)) @@ -1279,7 +1180,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, wp2->fd != -1 && (~wp2->flags & PANE_INPUTOFF) && window_pane_visible(wp2)) - input_key(wp2, key, NULL); + input_key_pane(wp2, key, NULL); } } return (0); @@ -1324,7 +1225,7 @@ window_pane_search(struct window_pane *wp, const char *term, int regex, } log_debug("%s: %s", __func__, line); if (!regex) - found = (fnmatch(new, line, 0) == 0); + found = (fnmatch(new, line, flags) == 0); else found = (regexec(&r, line, 0, NULL, 0) == 0); free(line); @@ -1645,7 +1546,7 @@ int window_pane_start_input(struct window_pane *wp, struct cmdq_item *item, char **cause) { - struct client *c = item->client; + struct client *c = cmdq_get_client(item); struct window_pane_input_data *cdata; if (~wp->flags & PANE_EMPTY) { |