aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/lock.yml2
-rw-r--r--CHANGES127
-rw-r--r--Makefile.am6
-rw-r--r--arguments.c63
-rw-r--r--attributes.c4
-rw-r--r--cfg.c61
-rw-r--r--client.c39
-rw-r--r--cmd-attach-session.c17
-rw-r--r--cmd-bind-key.c2
-rw-r--r--cmd-break-pane.c52
-rw-r--r--cmd-capture-pane.c12
-rw-r--r--cmd-choose-tree.c11
-rw-r--r--cmd-command-prompt.c37
-rw-r--r--cmd-confirm-before.c37
-rw-r--r--cmd-copy-mode.c31
-rw-r--r--cmd-detach-client.c46
-rw-r--r--cmd-display-menu.c305
-rw-r--r--cmd-display-message.c32
-rw-r--r--cmd-display-panes.c40
-rw-r--r--cmd-find-window.c7
-rw-r--r--cmd-find.c31
-rw-r--r--cmd-if-shell.c79
-rw-r--r--cmd-join-pane.c82
-rw-r--r--cmd-kill-pane.c8
-rw-r--r--cmd-kill-server.c2
-rw-r--r--cmd-kill-session.c9
-rw-r--r--cmd-kill-window.c11
-rw-r--r--cmd-list-buffers.c28
-rw-r--r--cmd-list-clients.c7
-rw-r--r--cmd-list-keys.c18
-rw-r--r--cmd-list-panes.c35
-rw-r--r--cmd-list-sessions.c28
-rw-r--r--cmd-list-windows.c33
-rw-r--r--cmd-load-buffer.c10
-rw-r--r--cmd-lock-server.c19
-rw-r--r--cmd-move-window.c57
-rw-r--r--cmd-new-session.c75
-rw-r--r--cmd-new-window.c19
-rw-r--r--cmd-parse.y96
-rw-r--r--cmd-paste-buffer.c5
-rw-r--r--cmd-pipe-pane.c21
-rw-r--r--cmd-queue.c428
-rw-r--r--cmd-refresh-client.c73
-rw-r--r--cmd-rename-session.c20
-rw-r--r--cmd-rename-window.c11
-rw-r--r--cmd-resize-pane.c102
-rw-r--r--cmd-resize-window.c7
-rw-r--r--cmd-respawn-pane.c11
-rw-r--r--cmd-respawn-window.c12
-rw-r--r--cmd-rotate-window.c10
-rw-r--r--cmd-run-shell.c83
-rw-r--r--cmd-save-buffer.c14
-rw-r--r--cmd-select-layout.c11
-rw-r--r--cmd-select-pane.c50
-rw-r--r--cmd-select-window.c24
-rw-r--r--cmd-send-keys.c73
-rw-r--r--cmd-set-buffer.c4
-rw-r--r--cmd-set-environment.c32
-rw-r--r--cmd-set-option.c36
-rw-r--r--cmd-show-environment.c37
-rw-r--r--cmd-show-messages.c26
-rw-r--r--cmd-show-options.c43
-rw-r--r--cmd-source-file.c4
-rw-r--r--cmd-split-window.c17
-rw-r--r--cmd-swap-pane.c12
-rw-r--r--cmd-swap-window.c18
-rw-r--r--cmd-switch-client.c65
-rw-r--r--cmd-unbind-key.c2
-rw-r--r--cmd-wait-for.c6
-rw-r--r--cmd.c151
-rw-r--r--configure.ac8
-rw-r--r--control.c32
-rw-r--r--environ.c29
-rw-r--r--format-draw.c2
-rw-r--r--format.c287
-rw-r--r--grid.c48
-rw-r--r--input-keys.c115
-rw-r--r--input.c230
-rw-r--r--job.c100
-rw-r--r--key-bindings.c116
-rw-r--r--key-string.c7
-rw-r--r--layout-set.c43
-rw-r--r--menu.c85
-rw-r--r--mode-tree.c79
-rw-r--r--notify.c63
-rw-r--r--options-table.c85
-rw-r--r--osdep-darwin.c2
-rw-r--r--popup.c464
-rw-r--r--regress/capture-pane-sgr0.sh15
-rw-r--r--regsub.c3
-rw-r--r--screen-redraw.c24
-rw-r--r--screen-write.c436
-rw-r--r--screen.c186
-rw-r--r--server-client.c311
-rw-r--r--server-fn.c4
-rw-r--r--server.c17
-rw-r--r--session.c18
-rw-r--r--spawn.c39
-rw-r--r--status.c4
-rw-r--r--style.c4
-rw-r--r--tmux.1446
-rw-r--r--tmux.c165
-rw-r--r--tmux.h368
-rw-r--r--tty-acs.c2
-rw-r--r--tty-features.c348
-rw-r--r--tty-keys.c112
-rw-r--r--tty-term.c149
-rw-r--r--tty.c295
-rw-r--r--window-buffer.c5
-rw-r--r--window-client.c3
-rw-r--r--window-copy.c189
-rw-r--r--window-tree.c34
-rw-r--r--window.c135
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
diff --git a/CHANGES b/CHANGES
index 12d6aac1..b218e46b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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 },
diff --git a/cfg.c b/cfg.c
index 933eda4e..84be3967 100644
--- a/cfg.c
+++ b/cfg.c
@@ -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]);
diff --git a/client.c b/client.c
index f247d059..4e7f7dcc 100644
--- a/client.c
+++ b/client.c
@@ -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);
diff --git a/cmd-find.c b/cmd-find.c
index 154842ab..e70eb606 100644
--- a/cmd-find.c
+++ b/cmd-find.c
@@ -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(&current, item->client, flags) == 0) {
+ } else if (cmd_find_from_client(&current, cmdq_get_client(item),
+ flags) == 0) {
fs->current = &current;
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);
}
diff --git a/cmd.c b/cmd.c
index 380eedcf..9cd5ab49 100644
--- a/cmd.c
+++ b/cmd.c
@@ -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)
diff --git a/control.c b/control.c
index b8a16e73..bdc89de4 100644
--- a/control.c
+++ b/control.c
@@ -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);
}
diff --git a/environ.c b/environ.c
index 25968f7b..0ce717df 100644
--- a/environ.c
+++ b/environ.c
@@ -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)
diff --git a/format.c b/format.c
index 79e99b61..97fcb81f 100644
--- a/format.c
+++ b/format.c
@@ -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));
diff --git a/grid.c b/grid.c
index b2031045..0cef412b 100644
--- a/grid.c
+++ b/grid.c
@@ -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);
}
diff --git a/input.c b/input.c
index 82d2b398..1b1be485 100644
--- a/input.c
+++ b/input.c
@@ -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;
diff --git a/job.c b/job.c
index 10883e8e..607f7138 100644
--- a/job.c
+++ b/job.c
@@ -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;
diff --git a/menu.c b/menu.c
index 6024ba02..39cb50c7 100644
--- a/menu.c
+++ b/menu.c
@@ -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);
}
diff --git a/notify.c b/notify.c
index 200e23d6..e5ce761b 100644
--- a/notify.c
+++ b/notify.c
@@ -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
diff --git a/regsub.c b/regsub.c
index 22e236dc..4039b9be 100644
--- a/regsub.c
+++ b/regsub.c
@@ -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;
diff --git a/screen.c b/screen.c
index fafb3456..dbd418a1 100644
--- a/screen.c
+++ b/screen.c
@@ -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 = &tcy;
+ *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);
}
diff --git a/server.c b/server.c
index a5d5e37f..bf8d0310 100644
--- a/server.c
+++ b/server.c
@@ -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;
diff --git a/session.c b/session.c
index eddafa2c..87c5b83c 100644
--- a/session.c
+++ b/session.c
@@ -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);
}
}
diff --git a/spawn.c b/spawn.c
index ee19cb9d..f1ea87d2 100644
--- a/spawn.c
+++ b/spawn.c
@@ -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) {
diff --git a/status.c b/status.c
index 33f6c47a..6beadb81 100644
--- a/status.c
+++ b/status.c
@@ -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':
diff --git a/style.c b/style.c
index 6ba4c524..da3b4c78 100644
--- a/style.c
+++ b/style.c
@@ -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 = ",";
diff --git a/tmux.1 b/tmux.1
index 615274bf..a3a05878 100644
--- a/tmux.1
+++ b/tmux.1
@@ -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
diff --git a/tmux.c b/tmux.c
index 3c1feccc..82c3441c 100644
--- a/tmux.c
+++ b/tmux.c
@@ -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));
}
diff --git a/tmux.h b/tmux.h
index 3720beee..62eb7c98 100644
--- a/tmux.h
+++ b/tmux.h
@@ -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 *);
diff --git a/tty-acs.c b/tty-acs.c
index 14634120..3e811103 100644
--- a/tty-acs.c
+++ b/tty-acs.c
@@ -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, ",");
+ }
+}
diff --git a/tty-keys.c b/tty-keys.c
index 064f2172..f5a3418f 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -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);
}
diff --git a/tty-term.c b/tty-term.c
index e556df6f..e8ac6634 100644
--- a/tty-term.c
+++ b/tty-term.c
@@ -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);
}
diff --git a/tty.c b/tty.c
index 8efe57b5..016ce399 100644
--- a/tty.c
+++ b/tty.c
@@ -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) {
diff --git a/window.c b/window.c
index f911f186..3bf9ef95 100644
--- a/window.c
+++ b/window.c
@@ -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) {