diff options
author | Nicholas Marriott <nicholas.marriott@gmail.com> | 2019-07-29 10:51:30 +0100 |
---|---|---|
committer | Nicholas Marriott <nicholas.marriott@gmail.com> | 2019-07-29 10:51:30 +0100 |
commit | da552eb73b80bab3c0a28dfb9ae2c75fa6d4bdaf (patch) | |
tree | cf9c86c3218659faf46e606a1e38bc6ebb570dcd | |
parent | 5a501a8ae27c2d0128870caa48c5708e97528567 (diff) | |
parent | b90a9fcd13f4434aed0fe1785d619aa668bbc77d (diff) | |
download | rtmux-da552eb73b80bab3c0a28dfb9ae2c75fa6d4bdaf.tar.gz rtmux-da552eb73b80bab3c0a28dfb9ae2c75fa6d4bdaf.tar.bz2 rtmux-da552eb73b80bab3c0a28dfb9ae2c75fa6d4bdaf.zip |
Merge branch 'master' into 3.0-rc
73 files changed, 1843 insertions, 939 deletions
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 49e2b90b..3a589484 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,13 +2,20 @@ Before opening an issue, please ensure that: +- Your problem is a specific problem or question or suggestion, not a general + complaint. + - `$TERM` inside tmux is screen, screen-256color, tmux or tmux-256color. Check by running `echo $TERM` inside tmux. - You can reproduce the problem with the latest tmux release, or a build from Git master. -- Your question or issue is not covered in the manual (run man tmux). +- Your question or issue is not covered [in the + manual](https://man.openbsd.org/tmux.1) (run `man tmux`). + +- Your problem is not mentioned in the [CHANGES + file](https://raw.githubusercontent.com/tmux/tmux/master/CHANGES) file. - Nobody else has opened the same issue recently. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index ee457237..8bf1e66a 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,6 +3,9 @@ Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md before opening an issue. +If you have upgraded, make sure your issue is not covered in the CHANGES file +for your version: https://raw.githubusercontent.com/tmux/tmux/master/CHANGES + Describe the problem and the steps to reproduce. Add a minimal tmux config if necessary. Screenshots can be helpful, but no more than one or two. diff --git a/.github/README.md b/.github/README.md index 6e262ef5..2b299cc5 100644 --- a/.github/README.md +++ b/.github/README.md @@ -30,7 +30,8 @@ configure with `--enable-utempter` to enable this. ### From version control -To get and build the latest from version control: +To get and build the latest from version control - note that this requires +`autoconf`, `automake` and `pkg-config`: ~~~bash git clone https://github.com/tmux/tmux.git @@ -39,10 +40,6 @@ sh autogen.sh ./configure && make ~~~ -(Note that this requires at least a working C compiler, `make`, `autoconf`, -`automake`, `pkg-config` as well as `libevent` and `ncurses` libraries and -headers.) - ## Contributing Bug reports, feature suggestions and especially code contributions are most @@ -50,14 +47,12 @@ welcome. Please send by email to: tmux-users@googlegroups.com -Or open a GitHub issue or pull request. - -There is [a TODO list](https://github.com/tmux/tmux/wiki/Contributing) which -explains some ideas for tmux not yet developed. Please feel free to ask for -clarifications on the mailing list if you're thinking of working on these or -need further information. +Or open a GitHub issue or pull request. **Please read [this +document](CONTRIBUTING.md) before opening an issue.** -Please read the CONTRIBUTING file before opening an issue. +There is [a list of suggestions for contributions](https://github.com/tmux/tmux/wiki/Contributing). +Please feel free to ask on the mailing list if you're thinking of working on something or need +further information. ## Documentation @@ -1,3 +1,56 @@ +CHANGES FROM 3.0 to X.X + +* Expand arguments to C and s format modifiers to match the m modifier. + +* Add support for underscore colours (Setulc capability must be added with + terminal-overrides as described in tmux(1)). + +* Add a "fill" style attribute for the fill colour of the drawing area (where + appropriate). + +* New -H flag to send-keys to send literal keys. + +* Format variables for pane mouse modes (mouse_utf8_flag and mouse_sgr_flag) + and for origin mode (origin_flag). + +* Add -F to refresh-client for flags for control mode clients, only one flag + (no-output) supported at the moment. + +* Add a few vi(1) keys for menus. + +* Add pane options, set with set-option -p and displayed with show-options -p. + Pane options inherit from window options (so every pane option is also + a window option). The pane style is now configured by setting window-style + and window-active-style in the pane options; select-pane -P and -g now change + the option but are no longer documented. + +* Do not document set-window-option and show-window-options. set-option -w and + show-options -w should be used instead. + +* Add a -A flag to show-options to show parent options as well (they are marked + with a *). + +* Resize panes lazily - do not resize unless they are in an attached, active + window. + +* Add regular expression support for the format search, match and substitute + modifiers and make them able to ignore case. find-window now accepts -r to + use regular expressions. + +* Do not use $TMUX to find the session because for windows in multiple sessions + it is wrong as often as it is right, and for windows in one session it is + pointless. Instead use TMUX_PANE if it is present. + +* Do not always resize the window back to its original size after applying a + layout, keep it at the layout size until it must be resized (for example when + attached and window-size is not manual). + +* Add new-session -X and attach-session -x to send SIGHUP to parent when + detaching (like detach-client -P). + +* Support for octal escapes in strings (such as \007) and improve list-keys + output so it parses correctly if copied into a configuration file. + CHANGES FROM 2.9 to 3.0 * INCOMPATIBLE: Add a new {} syntax to the configuration file. This is a string diff --git a/Makefile.am b/Makefile.am index 2b88892f..94b1564b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -151,6 +151,7 @@ dist_tmux_SOURCES = \ options.c \ paste.c \ proc.c \ + regsub.c \ resize.c \ screen-redraw.c \ screen-write.c \ @@ -26,16 +26,14 @@ To build and install tmux from a release tarball, use: tmux can use the utempter library to update utmp(5), if it is installed - run configure with --enable-utempter to enable this. -To get and build the latest from version control: +To get and build the latest from version control - note that this requires +autoconf, automake and pkg-config: $ git clone https://github.com/tmux/tmux.git $ cd tmux $ sh autogen.sh $ ./configure && make -(Note that this requires at least a working C compiler, make, autoconf, -automake, pkg-config as well as libevent and ncurses libraries and headers.) - * Contributing Bug reports, feature suggestions and especially code contributions are most diff --git a/arguments.c b/arguments.c index 38e50829..026272af 100644 --- a/arguments.c +++ b/arguments.c @@ -37,6 +37,7 @@ TAILQ_HEAD(args_values, args_value); struct args_entry { u_char flag; struct args_values values; + u_int count; RB_ENTRY(args_entry) entry; }; @@ -173,6 +174,7 @@ args_print(struct args *args) size_t len; char *buf; int i; + u_int j; struct args_entry *entry; struct args_value *value; @@ -186,7 +188,8 @@ args_print(struct args *args) if (*buf == '\0') args_print_add(&buf, &len, "-"); - args_print_add(&buf, &len, "%c", entry->flag); + for (j = 0; j < entry->count; j++) + args_print_add(&buf, &len, "%c", entry->flag); } /* Then the flags with arguments. */ @@ -212,12 +215,14 @@ args_escape(const char *s) if (*s == '\0') return (xstrdup(s)); - if ((strchr(quoted, s[0]) != NULL || s[0] == '~') && s[1] == '\0') { + if (s[0] != ' ' && + (strchr(quoted, s[0]) != NULL || s[0] == '~') && + s[1] == '\0') { xasprintf(&escaped, "\\%c", s[0]); return (escaped); } - flags = VIS_OCTAL|VIS_TAB|VIS_NL; + flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; if (s[strcspn(s, quoted)] != '\0') flags |= VIS_DQ; utf8_stravis(&escaped, s, flags); @@ -241,7 +246,12 @@ args_escape(const char *s) int args_has(struct args *args, u_char ch) { - return (args_find(args, ch) != NULL); + struct args_entry *entry; + + entry = args_find(args, ch); + if (entry == NULL) + return (0); + return (entry->count); } /* Set argument value in the arguments tree. */ @@ -255,9 +265,11 @@ args_set(struct args *args, u_char ch, const char *s) if (entry == NULL) { entry = xcalloc(1, sizeof *entry); entry->flag = ch; + entry->count = 1; TAILQ_INIT(&entry->values); RB_INSERT(args_tree, &args->tree, entry); - } + } else + entry->count++; if (s != NULL) { value = xcalloc(1, sizeof *value); @@ -52,7 +52,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data) cfg_show_causes(RB_MIN(sessions, &sessions)); if (cfg_item != NULL) - cfg_item->flags &= ~CMDQ_WAITING; + cmdq_continue(cfg_item); status_prompt_load_history(); @@ -90,14 +90,14 @@ start_cfg(void) } if (cfg_file == NULL) - load_cfg(TMUX_CONF, NULL, NULL, CMD_PARSE_QUIET, NULL); + load_cfg(TMUX_CONF, c, NULL, CMD_PARSE_QUIET, NULL); if (cfg_file == NULL && (home = find_home()) != NULL) { xasprintf(&cfg_file, "%s/.tmux.conf", home); flags = CMD_PARSE_QUIET; } if (cfg_file != NULL) - load_cfg(cfg_file, NULL, NULL, flags, NULL); + load_cfg(cfg_file, c, NULL, flags, NULL); cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); } @@ -126,6 +126,8 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, pi.flags = flags; pi.file = path; pi.line = 1; + pi.item = item; + pi.c = c; pr = cmd_parse_from_file(f, &pi); fclose(f); @@ -145,7 +147,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, if (item != NULL) cmdq_insert_after(item, new_item0); else - cmdq_append(c, new_item0); + cmdq_append(NULL, new_item0); cmd_list_free(pr->cmdlist); if (new_item != NULL) @@ -202,7 +202,7 @@ client_exit_message(void) case CLIENT_EXIT_TERMINATED: return ("terminated"); case CLIENT_EXIT_LOST_SERVER: - return ("lost server"); + return ("server exited unexpectedly"); case CLIENT_EXIT_EXITED: return ("exited"); case CLIENT_EXIT_SERVER_EXITED: @@ -436,7 +436,7 @@ client_stdin_callback(__unused int fd, __unused short events, struct msg_stdin_data data; data.size = read(STDIN_FILENO, data.data, sizeof data.data); - if (data.size < 0 && (errno == EINTR || errno == EAGAIN)) + if (data.size == -1 && (errno == EINTR || errno == EAGAIN)) return; proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index bcd6d895..6de734e5 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -37,8 +37,8 @@ const struct cmd_entry cmd_attach_session_entry = { .name = "attach-session", .alias = "attach", - .args = { "c:dErt:", 0, 0 }, - .usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, + .args = { "c:dErt:x", 0, 0 }, + .usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE, /* -t is special */ @@ -48,7 +48,7 @@ const struct cmd_entry cmd_attach_session_entry = { enum cmd_retval cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, - int rflag, const char *cflag, int Eflag) + int xflag, int rflag, const char *cflag, int Eflag) { struct cmd_find_state *current = &item->shared->current; enum cmd_find_type type; @@ -58,6 +58,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, struct winlink *wl; struct window_pane *wp; char *cause; + enum msgtype msgtype; if (RB_EMPTY(&sessions)) { cmdq_error(item, "no sessions"); @@ -102,11 +103,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, c->last_session = c->session; if (c->session != NULL) { - if (dflag) { + if (dflag || xflag) { + if (xflag) + msgtype = MSG_DETACHKILL; + else + msgtype = MSG_DETACH; TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - server_client_detach(c_loop, MSG_DETACH); + server_client_detach(c_loop, msgtype); } } if (!Eflag) @@ -131,11 +136,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (rflag) c->flags |= CLIENT_READONLY; - if (dflag) { + if (dflag || xflag) { + if (xflag) + msgtype = MSG_DETACHKILL; + else + msgtype = MSG_DETACH; TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - server_client_detach(c_loop, MSG_DETACH); + server_client_detach(c_loop, msgtype); } } if (!Eflag) @@ -169,6 +178,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item) struct args *args = self->args; return (cmd_attach_session(item, args_get(args, 't'), - args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'), - args_has(args, 'E'))); + args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'), + args_get(args, 'c'), args_has(args, 'E'))); } diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 3b929dee..b4c5b7cd 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -77,6 +77,8 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) layout_close_pane(wp); w = wp->window = window_create(w->sx, w->sy); + options_set_parent(wp->options, w->options); + wp->flags |= PANE_STYLECHANGED; TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; diff --git a/cmd-display-message.c b/cmd-display-message.c index 8c1ad5f6..4d9bccb6 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -109,8 +109,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) format_defaults(ft, target_c, s, wl, wp); if (args_has(args, 'a')) { - if (item != NULL) - format_each(ft, cmd_display_message_each, item); + format_each(ft, cmd_display_message_each, item); return (CMD_RETURN_NORMAL); } diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 7f38cd9e..df97819c 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -188,7 +188,7 @@ cmd_display_panes_free(struct client *c) struct cmd_display_panes_data *cdata = c->overlay_data; if (cdata->item != NULL) - cdata->item->flags &= ~CMDQ_WAITING; + cmdq_continue(cdata->item); free(cdata->command); free(cdata); } diff --git a/cmd-find-window.c b/cmd-find-window.c index c2d230a5..c29878b5 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -32,8 +32,8 @@ const struct cmd_entry cmd_find_window_entry = { .name = "find-window", .alias = "findw", - .args = { "CNt:TZ", 1, 1 }, - .usage = "[-CNTZ] " CMD_TARGET_PANE_USAGE " match-string", + .args = { "CNrt:TZ", 1, 1 }, + .usage = "[-CNrTZ] " CMD_TARGET_PANE_USAGE " match-string", .target = { 't', CMD_FIND_PANE, 0 }, @@ -57,30 +57,59 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) if (!C && !N && !T) C = N = T = 1; - if (C && N && T) { - xasprintf(&filter, - "#{||:" - "#{C:%s},#{||:#{m:*%s*,#{window_name}}," - "#{m:*%s*,#{pane_title}}}}", - s, s, s); - } else if (C && N) { - xasprintf(&filter, - "#{||:#{C:%s},#{m:*%s*,#{window_name}}}", - s, s); - } else if (C && T) { - xasprintf(&filter, - "#{||:#{C:%s},#{m:*%s*,#{pane_title}}}", - s, s); - } else if (N && T) { - xasprintf(&filter, - "#{||:#{m:*%s*,#{window_name}},#{m:*%s*,#{pane_title}}}", - s, s); - } else if (C) - xasprintf(&filter, "#{C:%s}", s); - else if (N) - xasprintf(&filter, "#{m:*%s*,#{window_name}}", s); - else - xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s); + if (!args_has(args, 'r')) { + if (C && N && T) { + xasprintf(&filter, + "#{||:" + "#{C:%s},#{||:#{m:*%s*,#{window_name}}," + "#{m:*%s*,#{pane_title}}}}", + s, s, s); + } else if (C && N) { + xasprintf(&filter, + "#{||:#{C:%s},#{m:*%s*,#{window_name}}}", + s, s); + } else if (C && T) { + xasprintf(&filter, + "#{||:#{C:%s},#{m:*%s*,#{pane_title}}}", + s, s); + } else if (N && T) { + xasprintf(&filter, + "#{||:#{m:*%s*,#{window_name}}," + "#{m:*%s*,#{pane_title}}}", + s, s); + } else if (C) + xasprintf(&filter, "#{C:%s}", s); + else if (N) + xasprintf(&filter, "#{m:*%s*,#{window_name}}", s); + else + xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s); + } else { + if (C && N && T) { + xasprintf(&filter, + "#{||:" + "#{C/r:%s},#{||:#{m/r:%s,#{window_name}}," + "#{m/r:%s,#{pane_title}}}}", + s, s, s); + } else if (C && N) { + xasprintf(&filter, + "#{||:#{C/r:%s},#{m/r:%s,#{window_name}}}", + s, s); + } else if (C && T) { + xasprintf(&filter, + "#{||:#{C/r:%s},#{m/r:%s,#{pane_title}}}", + s, s); + } else if (N && T) { + xasprintf(&filter, + "#{||:#{m/r:%s,#{window_name}}," + "#{m/r:%s,#{pane_title}}}", + s, s); + } else if (C) + xasprintf(&filter, "#{C/r:%s}", s); + else if (N) + xasprintf(&filter, "#{m/r:%s,#{window_name}}", s); + else + xasprintf(&filter, "#{m/r:%s,#{pane_title}}", s); + } new_args = args_parse("", 1, &argv); if (args_has(args, 'Z')) @@ -75,38 +75,12 @@ static const char *cmd_find_pane_table[][2] = { { NULL, NULL } }; -/* Get session from TMUX if present. */ -static struct session * -cmd_find_try_TMUX(struct client *c) -{ - struct environ_entry *envent; - char tmp[256]; - long long pid; - u_int session; - struct session *s; - - envent = environ_find(c->environ, "TMUX"); - if (envent == NULL) - return (NULL); - - if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3) - return (NULL); - if (pid != getpid()) - return (NULL); - log_debug("%s: client %p TMUX %s (session $%u)", __func__, c, - envent->value, session); - - s = session_find_by_id(session); - if (s != NULL) - log_debug("%s: session $%u still exists", __func__, s->id); - return (s); -} - /* Find pane containing client if any. */ static struct window_pane * cmd_find_inside_pane(struct client *c) { struct window_pane *wp; + struct environ_entry *envent; if (c == NULL) return (NULL); @@ -115,6 +89,11 @@ cmd_find_inside_pane(struct client *c) if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0) break; } + if (wp == NULL) { + envent = environ_find(c->environ, "TMUX_PANE"); + if (envent != NULL) + wp = window_pane_find_by_id_str(envent->value); + } if (wp != NULL) log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty); return (wp); @@ -879,8 +858,6 @@ cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m, int flags) int cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) { - struct session *s; - struct winlink *wl; struct window_pane *wp; /* If no client, treat as from nothing. */ @@ -902,30 +879,6 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) if (wp == NULL) goto unknown_pane; - /* If we have a session in TMUX, see if it has this pane. */ - s = cmd_find_try_TMUX(c); - if (s != NULL) { - RB_FOREACH(wl, winlinks, &s->windows) { - if (window_has_pane(wl->window, wp)) - break; - } - if (wl != NULL) { - log_debug("%s: session $%u has pane %%%u", __func__, - s->id, wp->id); - - fs->s = s; - fs->wl = s->curw; /* use current session */ - fs->w = fs->wl->window; - fs->wp = fs->w->active; /* use active pane */ - - cmd_find_log_state(__func__, fs); - return (0); - } else { - log_debug("%s: session $%u does not have pane %%%u", - __func__, s->id, wp->id); - } - } - /* * Don't have a session, or it doesn't have this pane. Try all * sessions. @@ -947,17 +900,7 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) return (0); unknown_pane: - /* - * We're not running in a known pane, but maybe this client has TMUX - * in the environment. That'd give us a session. - */ - s = cmd_find_try_TMUX(c); - if (s != NULL) { - cmd_find_from_session(fs, s, flags); - return (0); - } - - /* Otherwise we need to guess. */ + /* We can't find the pane so need to guess. */ return (cmd_find_from_nothing(fs, flags)); } @@ -1005,6 +948,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, strlcat(tmp, "CANFAIL,", sizeof tmp); if (*tmp != '\0') tmp[strlen(tmp) - 1] = '\0'; + else + strlcat(tmp, "NONE", sizeof tmp); log_debug("%s: target %s, type %s, item %p, flags %s", __func__, target == NULL ? "none" : target, s, item, tmp); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 84f66657..2befbc0c 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -67,10 +67,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *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 = item->target.s; - struct winlink *wl = item->target.wl; - struct window_pane *wp = item->target.wp; + struct session *s = fs->s; + struct winlink *wl = fs->wl; + struct window_pane *wp = fs->wp; struct cmd_parse_input pi; struct cmd_parse_result *pr; @@ -92,7 +93,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) pi.line = self->line; pi.item = item; pi.c = c; - cmd_find_copy_state(&pi.fs, &item->target); + cmd_find_copy_state(&pi.fs, fs); pr = cmd_parse_from_string(cmd, &pi); switch (pr->status) { @@ -103,7 +104,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) free(pr->error); return (CMD_RETURN_ERROR); case CMD_PARSE_SUCCESS: - new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0); + new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); cmdq_insert_after(item, new_item); cmd_list_free(pr->cmdlist); break; @@ -120,7 +121,10 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->cmd_else = NULL; memcpy(&cdata->mouse, m, sizeof cdata->mouse); - cdata->client = item->client; + if (!args_has(args, 'b')) + cdata->client = item->client; + else + cdata->client = c; if (cdata->client != NULL) cdata->client->references++; @@ -137,7 +141,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->input.c = c; if (cdata->input.c != NULL) cdata->input.c->references++; - cmd_find_copy_state(&cdata->input.fs, &item->target); + cmd_find_copy_state(&cdata->input.fs, fs); if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) { @@ -195,7 +199,7 @@ cmd_if_shell_callback(struct job *job) out: if (cdata->item != NULL) - cdata->item->flags &= ~CMDQ_WAITING; + cmdq_continue(cdata->item); } static void diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 3d798e09..5d0e0d6a 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -139,6 +139,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) TAILQ_REMOVE(&src_w->panes, src_wp, entry); src_wp->window = dst_w; + options_set_parent(src_wp->options, dst_w->options); + src_wp->flags |= PANE_STYLECHANGED; TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); layout_assign_pane(lc, src_wp); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 3e669093..cdf44bf7 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -176,7 +176,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) free(cause); } out: - cdata->item->flags &= ~CMDQ_WAITING; + cmdq_continue(cdata->item); free(cdata->bufname); free(cdata); diff --git a/cmd-new-session.c b/cmd-new-session.c index 559c268c..e0540815 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -39,8 +39,8 @@ const struct cmd_entry cmd_new_session_entry = { .name = "new-session", .alias = "new", - .args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 }, - .usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " + .args = { "Ac:dDEF:n:Ps:t:x:Xy:", 0, -1 }, + .usage = "[-AdDEPX] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", @@ -105,7 +105,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'A')) { retval = cmd_attach_session(item, newname, args_has(args, 'D'), - 0, NULL, args_has(args, 'E')); + args_has(args, 'X'), 0, NULL, + args_has(args, 'E')); free(newname); return (retval); } diff --git a/cmd-parse.y b/cmd-parse.y index ce7b344b..6d2b970c 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -77,6 +77,8 @@ static char *cmd_parse_get_error(const char *, u_int, const char *); static void cmd_parse_free_command(struct cmd_parse_command *); static struct cmd_parse_commands *cmd_parse_new_commands(void); static void cmd_parse_free_commands(struct cmd_parse_commands *); +static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, + struct cmd_list *); %} @@ -508,6 +510,22 @@ cmd_parse_get_error(const char *file, u_int line, const char *error) } static void +cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line, + struct cmd_list *cmdlist) +{ + char *s; + + if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) { + s = cmd_list_print(cmdlist, 0); + if (pi->file != NULL) + cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s); + else + cmdq_print(pi->item, "%u: %s", line, s); + free(s); + } +} + +static void cmd_parse_free_command(struct cmd_parse_command *cmd) { free(cmd->name); @@ -663,6 +681,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, if (cmdlist == NULL || cmd->line != line) { if (cmdlist != NULL) { + cmd_parse_print_commands(pi, line, cmdlist); cmd_list_move(result, cmdlist); cmd_list_free(cmdlist); } @@ -682,6 +701,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, cmd_list_append(cmdlist, add); } if (cmdlist != NULL) { + cmd_parse_print_commands(pi, line, cmdlist); cmd_list_move(result, cmdlist); cmd_list_free(cmdlist); } @@ -1124,17 +1144,54 @@ error: static int yylex_token_escape(char **buf, size_t *len) { - int ch, type; + int ch, type, o2, o3; u_int size, i, tmp; char s[9]; struct utf8_data ud; - switch (ch = yylex_getc()) { + ch = yylex_getc(); + + if (ch >= '4' && ch <= '7') { + yyerror("invalid octal escape"); + return (0); + } + if (ch >= '0' && ch <= '3') { + o2 = yylex_getc(); + if (o2 >= '0' && o2 <= '7') { + o3 = yylex_getc(); + if (o3 >= '0' && o3 <= '7') { + ch = 64 * (ch - '0') + + 8 * (o2 - '0') + + (o3 - '0'); + yylex_append1(buf, len, ch); + return (1); + } + } + yyerror("invalid octal escape"); + return (0); + } + + switch (ch) { case EOF: return (0); + case 'a': + ch = '\a'; + break; + case 'b': + ch = '\b'; + break; case 'e': ch = '\033'; break; + case 'f': + ch = '\f'; + break; + case 's': + ch = ' '; + break; + case 'v': + ch = '\v'; + break; case 'r': ch = '\r'; break; diff --git a/cmd-queue.c b/cmd-queue.c index 1ee43508..ef68d5d5 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -156,6 +156,13 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, free(name); } +/* Continue processing command queue. */ +void +cmdq_continue(struct cmdq_item *item) +{ + item->flags &= ~CMDQ_WAITING; +} + /* Remove an item. */ static void cmdq_remove(struct cmdq_item *item) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index e5ae099f..49921a74 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -19,6 +19,7 @@ #include <sys/types.h> #include <stdlib.h> +#include <string.h> #include "tmux.h" @@ -33,8 +34,9 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "cC:DlLRSt:U", 0, 1 }, - .usage = "[-cDlLRSU] [-C size] " CMD_TARGET_CLIENT_USAGE " [adjustment]", + .args = { "cC:DF:lLRSt:U", 0, 1 }, + .usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE + " [adjustment]", .flags = CMD_AFTERHOOK, .exec = cmd_refresh_client_exec @@ -48,6 +50,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) struct tty *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) @@ -107,28 +110,43 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'l')) { if (c->session != NULL) tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?"); - } else if (args_has(args, 'C')) { - if ((size = args_get(args, 'C')) == NULL) { - cmdq_error(item, "missing size"); - return (CMD_RETURN_ERROR); - } - if (sscanf(size, "%u,%u", &x, &y) != 2 && - sscanf(size, "%ux%u", &x, &y)) { - cmdq_error(item, "bad size argument"); - return (CMD_RETURN_ERROR); - } - if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || - y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { - cmdq_error(item, "size too small or too big"); - return (CMD_RETURN_ERROR); + return (CMD_RETURN_NORMAL); + } + + if (args_has(args, 'C') || args_has(args, 'F')) { + if (args_has(args, 'C')) { + if (!(c->flags & CLIENT_CONTROL)) { + cmdq_error(item, "not a control client"); + return (CMD_RETURN_ERROR); + } + size = args_get(args, 'C'); + if (sscanf(size, "%u,%u", &x, &y) != 2 && + sscanf(size, "%ux%u", &x, &y) != 2) { + cmdq_error(item, "bad size argument"); + return (CMD_RETURN_ERROR); + } + if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || + y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { + cmdq_error(item, "size too small or too big"); + return (CMD_RETURN_ERROR); + } + tty_set_size(&c->tty, x, y); + c->flags |= CLIENT_SIZECHANGED; + recalculate_sizes(); } - if (!(c->flags & CLIENT_CONTROL)) { - cmdq_error(item, "not a control client"); - return (CMD_RETURN_ERROR); + if (args_has(args, 'F')) { + if (!(c->flags & CLIENT_CONTROL)) { + cmdq_error(item, "not a control client"); + return (CMD_RETURN_ERROR); + } + s = copy = xstrdup(args_get(args, 'F')); + while ((next = strsep(&s, ",")) != NULL) { + /* Unknown flags are ignored. */ + if (strcmp(next, "no-output") == 0) + c->flags |= CLIENT_CONTROL_NOOUTPUT; + } + free(copy); } - tty_set_size(&c->tty, x, y); - c->flags |= CLIENT_SIZECHANGED; - recalculate_sizes(); return (CMD_RETURN_NORMAL); } diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index c978edfb..8d35d96f 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -144,13 +144,13 @@ cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m) w = wl->window; y = m->y + m->oy; x = m->x + m->ox; - if (m->statusat == 0 && y > 0) - y--; + if (m->statusat == 0 && y >= m->statuslines) + y -= m->statuslines; else if (m->statusat > 0 && y >= (u_int)m->statusat) y = m->statusat - 1; ly = m->ly + m->oy; lx = m->lx + m->ox; - if (m->statusat == 0 && ly > 0) - ly--; + if (m->statusat == 0 && ly >= m->statuslines) + ly -= m->statuslines; else if (m->statusat > 0 && ly >= (u_int)m->statusat) ly = m->statusat - 1; diff --git a/cmd-run-shell.c b/cmd-run-shell.c index c9a478c7..2f45f492 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -155,7 +155,7 @@ cmd_run_shell_callback(struct job *job) free(msg); if (cdata->item != NULL) - cdata->item->flags &= ~CMDQ_WAITING; + cmdq_continue(cdata->item); } static void diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 92ecb734..52c58dbc 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_select_pane_entry = { .name = "select-pane", .alias = "selectp", - .args = { "DdegLlMmP:RT:t:U", 0, 0 }, - .usage = "[-DdegLlMmRU] [-P style] [-T title] " CMD_TARGET_PANE_USAGE, + .args = { "DdegLlMmP:RT:t:U", 0, 0 }, /* -P and -g deprecated */ + .usage = "[-DdeLlMmRU] [-T title] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -90,9 +90,10 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) struct window *w = wl->window; struct session *s = item->target.s; struct window_pane *wp = item->target.wp, *lastwp, *markedwp; - struct style *sy = &wp->style; char *pane_title; const char *style; + struct style *sy; + struct options_entry *o; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { lastwp = w->last; @@ -144,15 +145,18 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(self->args, 'P') || args_has(self->args, 'g')) { if ((style = args_get(args, 'P')) != NULL) { - style_set(sy, &grid_default_cell); - if (style_parse(sy, &grid_default_cell, style) == -1) { + o = options_set_style(wp->options, "window-style", 0, + style); + if (o == NULL) { cmdq_error(item, "bad style: %s", style); return (CMD_RETURN_ERROR); } - wp->flags |= PANE_REDRAW; + wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); } - if (args_has(self->args, 'g')) + if (args_has(self->args, 'g')) { + sy = options_get_style(wp->options, "window-style"); cmdq_print(item, "%s", style_tostring(sy)); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-send-keys.c b/cmd-send-keys.c index fd1c1ab9..fe202335 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_send_keys_entry = { .name = "send-keys", .alias = "send", - .args = { "lXRMN:t:", 0, -1 }, - .usage = "[-lXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...", + .args = { "HlXRMN:t:", 0, -1 }, + .usage = "[-HlXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...", .target = { 't', CMD_FIND_PANE, 0 }, @@ -56,7 +56,7 @@ const struct cmd_entry cmd_send_prefix_entry = { }; static struct cmdq_item * -cmd_send_keys_inject(struct client *c, struct cmd_find_state *fs, +cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, struct cmdq_item *item, key_code key) { struct window_mode_entry *wme; @@ -81,20 +81,56 @@ cmd_send_keys_inject(struct client *c, struct cmd_find_state *fs, return (item); } +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) +{ + const char *s = args->argv[i]; + struct utf8_data *ud, *uc; + wchar_t wc; + key_code key; + char *endptr; + long n; + int literal; + + if (args_has(args, 'H')) { + 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)); + } + + literal = args_has(args, 'l'); + if (!literal) { + key = key_string_lookup_string(s); + if (key != KEYC_NONE && key != KEYC_UNKNOWN) + return (cmd_send_keys_inject_key(c, fs, item, key)); + literal = 1; + } + if (literal) { + ud = utf8_fromcstr(s); + 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); + } + free(ud); + } + return (item); +} + 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 cmd_find_state *fs = &item->target; struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); - struct utf8_data *ud, *uc; - wchar_t wc; - int i, literal; + int i; key_code key; u_int np = 1; char *cause = NULL; @@ -141,7 +177,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) key = options_get_number(s->options, "prefix2"); else key = options_get_number(s->options, "prefix"); - cmd_send_keys_inject(c, fs, item, key); + cmd_send_keys_inject_key(c, fs, item, key); return (CMD_RETURN_NORMAL); } @@ -151,28 +187,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) } for (; np != 0; np--) { - for (i = 0; i < args->argc; i++) { - literal = args_has(args, 'l'); - if (!literal) { - key = key_string_lookup_string(args->argv[i]); - if (key != KEYC_NONE && key != KEYC_UNKNOWN) { - item = cmd_send_keys_inject(c, fs, item, - key); - } else - literal = 1; - } - if (literal) { - ud = utf8_fromcstr(args->argv[i]); - for (uc = ud; uc->size != 0; uc++) { - if (utf8_combine(uc, &wc) != UTF8_DONE) - continue; - item = cmd_send_keys_inject(c, fs, item, - wc); - } - free(ud); - } - } - + for (i = 0; i < args->argc; i++) + item = cmd_send_keys_inject_string(c, fs, item, args, i); } return (CMD_RETURN_NORMAL); diff --git a/cmd-set-option.c b/cmd-set-option.c index 7be561f2..23b45230 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -43,10 +43,10 @@ const struct cmd_entry cmd_set_option_entry = { .name = "set-option", .alias = "set", - .args = { "aFgoqst:uw", 1, 2 }, - .usage = "[-aFgosquw] [-t target-window] option [value]", + .args = { "aFgopqst:uw", 1, 2 }, + .usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]", - .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, + .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_set_option_exec @@ -88,20 +88,24 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) struct session *s = fs->s; struct winlink *wl = fs->wl; struct window *w; - enum options_table_scope scope; + struct window_pane *wp; struct options *oo; struct options_entry *parent, *o; char *name, *argument, *value = NULL, *cause; - const char *target; int window, idx, already, error, ambiguous; + int scope; struct style *sy; + window = (self->entry == &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); + /* If set-hook -R, fire the hook straight away. */ if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) { notify_hook(item, argument); + free(argument); return (CMD_RETURN_NORMAL); } @@ -123,25 +127,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) else value = xstrdup(args->argv[1]); - /* - * Figure out the scope: for user options it comes from the arguments, - * otherwise from the option name. - */ - if (*name == '@') { - window = (self->entry == &cmd_set_window_option_entry); - scope = options_scope_from_flags(args, window, fs, &oo, &cause); - } else { - if (options_get_only(global_options, name) != NULL) - scope = OPTIONS_TABLE_SERVER; - else if (options_get_only(global_s_options, name) != NULL) - scope = OPTIONS_TABLE_SESSION; - else if (options_get_only(global_w_options, name) != NULL) - scope = OPTIONS_TABLE_WINDOW; - else { - scope = OPTIONS_TABLE_NONE; - xasprintf(&cause, "unknown option: %s", argument); - } - } + /* Get the scope and table for the option .*/ + scope = options_scope_from_name(args, window, name, fs, &oo, &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) goto out; @@ -149,35 +136,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) free(cause); goto fail; } - - /* Which table should this option go into? */ - if (scope == OPTIONS_TABLE_SERVER) - oo = global_options; - else if (scope == OPTIONS_TABLE_SESSION) { - if (args_has(self->args, 'g')) - oo = global_s_options; - else if (s == NULL) { - target = args_get(args, 't'); - if (target != NULL) - cmdq_error(item, "no such session: %s", target); - else - cmdq_error(item, "no current session"); - goto fail; - } else - oo = s->options; - } else if (scope == OPTIONS_TABLE_WINDOW) { - if (args_has(self->args, 'g')) - oo = global_w_options; - else if (wl == NULL) { - target = args_get(args, 't'); - if (target != NULL) - cmdq_error(item, "no such window: %s", target); - else - cmdq_error(item, "no current window"); - goto fail; - } else - oo = wl->window->options; - } o = options_get_only(oo, name); parent = options_get(oo, name); @@ -292,8 +250,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) alerts_reset_all(); if (strcmp(name, "window-style") == 0 || strcmp(name, "window-active-style") == 0) { - RB_FOREACH(w, windows, &windows) - w->flags |= WINDOW_STYLECHANGED; + RB_FOREACH(wp, window_pane_tree, &all_window_panes) + wp->flags |= PANE_STYLECHANGED; } if (strcmp(name, "pane-border-status") == 0) { RB_FOREACH(w, windows, &windows) diff --git a/cmd-show-options.c b/cmd-show-options.c index 756f763c..da481139 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -30,18 +30,18 @@ static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *); static void cmd_show_options_print(struct cmd *, struct cmdq_item *, - struct options_entry *, int); + struct options_entry *, int, int); static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *, - struct options *); + int, struct options *); const struct cmd_entry cmd_show_options_entry = { .name = "show-options", .alias = "show", - .args = { "gHqst:vw", 0, 1 }, - .usage = "[-gHqsvw] [-t target-session|target-window] [option]", + .args = { "AgHpqst:vw", 0, 1 }, + .usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]", - .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, + .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_show_options_exec @@ -82,13 +82,12 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct options *oo; - enum options_table_scope scope; char *argument, *name = NULL, *cause; - const char *target; - int window, idx, ambiguous; + int window, idx, ambiguous, parent, scope; struct options_entry *o; window = (self->entry == &cmd_show_window_options_entry); + if (args->argc == 0) { scope = options_scope_from_flags(args, window, fs, &oo, &cause); if (scope == OPTIONS_TABLE_NONE) { @@ -98,7 +97,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } - return (cmd_show_options_all(self, item, oo)); + return (cmd_show_options_all(self, item, scope, oo)); } argument = format_single(item, args->argv[0], c, s, wl, NULL); @@ -112,49 +111,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "invalid option: %s", argument); goto fail; } - if (*name == '@') - scope = options_scope_from_flags(args, window, fs, &oo, &cause); - else { - if (options_get_only(global_options, name) != NULL) - scope = OPTIONS_TABLE_SERVER; - else if (options_get_only(global_s_options, name) != NULL) - scope = OPTIONS_TABLE_SESSION; - else if (options_get_only(global_w_options, name) != NULL) - scope = OPTIONS_TABLE_WINDOW; - else { - scope = OPTIONS_TABLE_NONE; - xasprintf(&cause, "unknown option: %s", argument); - } - if (scope == OPTIONS_TABLE_SERVER) - oo = global_options; - else if (scope == OPTIONS_TABLE_SESSION) { - if (args_has(self->args, 'g')) - oo = global_s_options; - else if (s == NULL) { - target = args_get(args, 't'); - if (target != NULL) { - cmdq_error(item, "no such session: %s", - target); - } else - cmdq_error(item, "no current session"); - goto fail; - } else - oo = s->options; - } else if (scope == OPTIONS_TABLE_WINDOW) { - if (args_has(self->args, 'g')) - oo = global_w_options; - else if (wl == NULL) { - target = args_get(args, 't'); - if (target != NULL) { - cmdq_error(item, "no such window: %s", - target); - } else - cmdq_error(item, "no current window"); - goto fail; - } else - oo = wl->window->options; - } - } + scope = options_scope_from_name(args, window, name, fs, &oo, &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) goto fail; @@ -163,8 +120,13 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) goto fail; } o = options_get_only(oo, name); + if (args_has(args, 'A') && o == NULL) { + o = options_get(oo, name); + parent = 1; + } else + parent = 0; if (o != NULL) - cmd_show_options_print(self, item, o, idx); + cmd_show_options_print(self, item, o, idx, parent); free(name); free(argument); @@ -178,7 +140,7 @@ fail: static void cmd_show_options_print(struct cmd *self, struct cmdq_item *item, - struct options_entry *o, int idx) + struct options_entry *o, int idx, int parent) { struct options_array_item *a; const char *name = options_name(o); @@ -197,7 +159,8 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, } while (a != NULL) { idx = options_array_item_index(a); - cmd_show_options_print(self, item, o, idx); + cmd_show_options_print(self, item, o, idx, + parent); a = options_array_next(a); } return; @@ -209,50 +172,81 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, cmdq_print(item, "%s", value); else if (options_isstring(o)) { escaped = args_escape(value); - cmdq_print(item, "%s %s", name, escaped); + if (parent) + cmdq_print(item, "%s* %s", name, escaped); + else + cmdq_print(item, "%s %s", name, escaped); free(escaped); - } else - cmdq_print(item, "%s %s", name, value); + } else { + if (parent) + cmdq_print(item, "%s* %s", name, value); + else + cmdq_print(item, "%s %s", name, value); + } free(value); free(tmp); } static enum cmd_retval -cmd_show_options_all(struct cmd *self, struct cmdq_item *item, +cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, struct options *oo) { + const struct options_table_entry *oe; struct options_entry *o; struct options_array_item *a; + const char *name; u_int idx; - const struct options_table_entry *oe; + int parent; o = options_first(oo); while (o != NULL) { - oe = options_table_entry(o); + if (options_table_entry(o) == NULL) + cmd_show_options_print(self, item, o, -1, 0); + o = options_next(o); + } + for (oe = options_table; oe->name != NULL; oe++) { + if (~oe->scope & scope) + continue; + if ((self->entry != &cmd_show_hooks_entry && !args_has(self->args, 'H') && oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK)) || (self->entry == &cmd_show_hooks_entry && (oe == NULL || - (~oe->flags & OPTIONS_TABLE_IS_HOOK)))) { - o = options_next(o); + (~oe->flags & OPTIONS_TABLE_IS_HOOK)))) continue; - } + + o = options_get_only(oo, oe->name); + if (o == NULL) { + if (!args_has(self->args, 'A')) + continue; + o = options_get(oo, oe->name); + if (o == NULL) + continue; + parent = 1; + } else + parent = 0; + if (!options_isarray(o)) - cmd_show_options_print(self, item, o, -1); + cmd_show_options_print(self, item, o, -1, parent); else if ((a = options_array_first(o)) == NULL) { - if (!args_has(self->args, 'v')) - cmdq_print(item, "%s", options_name(o)); + if (!args_has(self->args, 'v')) { + name = options_name(o); + if (parent) + cmdq_print(item, "%s*", name); + else + cmdq_print(item, "%s", name); + } } else { while (a != NULL) { idx = options_array_item_index(a); - cmd_show_options_print(self, item, o, idx); + cmd_show_options_print(self, item, o, idx, + parent); a = options_array_next(a); } } - o = options_next(o); } return (CMD_RETURN_NORMAL); } diff --git a/cmd-source-file.c b/cmd-source-file.c index 38aede0c..2e01ae67 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -37,8 +37,8 @@ const struct cmd_entry cmd_source_file_entry = { .name = "source-file", .alias = "source", - .args = { "nq", 1, -1 }, - .usage = "[-nq] path ...", + .args = { "nqv", 1, -1 }, + .usage = "[-nqv] path ...", .flags = 0, .exec = cmd_source_file_exec @@ -62,6 +62,8 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) flags |= CMD_PARSE_QUIET; if (args_has(args, 'n')) flags |= CMD_PARSE_PARSEONLY; + if (args_has(args, 'v')) + flags |= CMD_PARSE_VERBOSE; utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB); retval = CMD_RETURN_NORMAL; diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 2ad05561..994ad0e8 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -90,7 +90,11 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) src_wp->layout_cell = dst_lc; src_wp->window = dst_w; + options_set_parent(src_wp->options, dst_w->options); + src_wp->flags |= PANE_STYLECHANGED; dst_wp->window = src_w; + options_set_parent(dst_wp->options, src_w->options); + dst_wp->flags |= PANE_STYLECHANGED; sx = src_wp->sx; sy = src_wp->sy; xoff = src_wp->xoff; yoff = src_wp->yoff; diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 33600eda..4f438a7f 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -153,7 +153,7 @@ cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name, log_debug("signal wait channel %s, with waiters", wc->name); TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { - wi->item->flags &= ~CMDQ_WAITING; + cmdq_continue(wi->item); TAILQ_REMOVE(&wc->waiters, wi, entry); free(wi); @@ -229,7 +229,7 @@ cmd_wait_for_unlock(struct cmdq_item *item, const char *name, } if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) { - wi->item->flags &= ~CMDQ_WAITING; + cmdq_continue(wi->item); TAILQ_REMOVE(&wc->lockers, wi, entry); free(wi); } else { @@ -248,13 +248,13 @@ cmd_wait_for_flush(void) RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { - wi->item->flags &= ~CMDQ_WAITING; + cmdq_continue(wi->item); TAILQ_REMOVE(&wc->waiters, wi, entry); free(wi); } wc->woken = 1; TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) { - wi->item->flags &= ~CMDQ_WAITING; + cmdq_continue(wi->item); TAILQ_REMOVE(&wc->lockers, wi, entry); free(wi); } @@ -596,8 +596,8 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, } log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : ""); - if (m->statusat == 0 && y > 0) - y--; + if (m->statusat == 0 && y >= m->statuslines) + y -= m->statuslines; if (x < wp->xoff || x >= wp->xoff + wp->sx) return (-1); @@ -230,11 +230,85 @@ colour_fromstring(const char *s) return (-1); } -/* Convert 256 colour palette to 16. */ -u_char -colour_256to16(u_char c) +/* Convert 256 colour to RGB colour. */ +int +colour_256toRGB(int c) +{ + static const int table[256] = { + 0x000000, 0x800000, 0x008000, 0x808000, + 0x000080, 0x800080, 0x008080, 0xc0c0c0, + 0x808080, 0xff0000, 0x00ff00, 0xffff00, + 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, + 0x000000, 0x00005f, 0x000087, 0x0000af, + 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, + 0x005f87, 0x005faf, 0x005fd7, 0x005fff, + 0x008700, 0x00875f, 0x008787, 0x0087af, + 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, + 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, + 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, + 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, + 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, + 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, + 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, + 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, + 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, + 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, + 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, + 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, + 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, + 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, + 0x870000, 0x87005f, 0x870087, 0x8700af, + 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, + 0x875f87, 0x875faf, 0x875fd7, 0x875fff, + 0x878700, 0x87875f, 0x878787, 0x8787af, + 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, + 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, + 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, + 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, + 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, + 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, + 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, + 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, + 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, + 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, + 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, + 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, + 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, + 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, + 0xd70000, 0xd7005f, 0xd70087, 0xd700af, + 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, + 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, + 0xd78700, 0xd7875f, 0xd78787, 0xd787af, + 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, + 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, + 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, + 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, + 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, + 0xff0000, 0xff005f, 0xff0087, 0xff00af, + 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, + 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, + 0xff8700, 0xff875f, 0xff8787, 0xff87af, + 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, + 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, + 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, + 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, + 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, + 0x080808, 0x121212, 0x1c1c1c, 0x262626, + 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, + 0x585858, 0x626262, 0x6c6c6c, 0x767676, + 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, + 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, + 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee + }; + + return (table[c & 0xff] | COLOUR_FLAG_RGB); +} + +/* Convert 256 colour to 16 colour. */ +int +colour_256to16(int c) { - static const u_char table[256] = { + static const char table[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, @@ -253,5 +327,5 @@ colour_256to16(u_char c) 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15 }; - return (table[c]); + return (table[c & 0xff]); } diff --git a/configure.ac b/configure.ac index 8473a0d2..5fba1eaf 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.0-rc3) +AC_INIT([tmux], next-3.1) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) diff --git a/control-notify.c b/control-notify.c index 340dab73..a1e2b7bf 100644 --- a/control-notify.c +++ b/control-notify.c @@ -36,6 +36,9 @@ control_notify_input(struct client *c, struct window_pane *wp, if (c->session == NULL) return; + if (c->flags & CLIENT_CONTROL_NOOUTPUT) + return; + /* * Only write input if the window pane is linked to a window belonging * to the client's session. @@ -80,6 +80,7 @@ control_callback(struct client *c, int closed, __unused void *data) if (line == NULL) break; if (*line == '\0') { /* empty line exit */ + free(line); c->flags |= CLIENT_EXIT; break; } diff --git a/format-draw.c b/format-draw.c index b589ca5e..e0ca89f0 100644 --- a/format-draw.c +++ b/format-draw.c @@ -511,8 +511,9 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL]; u_int map[] = { LEFT, LEFT, CENTRE, RIGHT }; int focus_start = -1, focus_end = -1; - int list_state = -1; + int list_state = -1, fill = -1; enum style_align list_align = STYLE_ALIGN_DEFAULT; + struct grid_cell gc; struct style sy; struct utf8_data *ud = &sy.gc.data; const char *cp, *end; @@ -564,7 +565,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, cp++; } - /* Draw the cell to th current screen. */ + /* Draw the cell to the current screen. */ screen_write_cell(&ctx[current], &sy.gc); width[current] += ud->width; continue; @@ -590,6 +591,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, style_tostring(&sy)); free(tmp); + /* If this style has a fill colour, store it for later. */ + if (sy.fill != 8) + fill = sy.fill; + /* Check the list state. */ switch (sy.list) { case STYLE_LIST_ON: @@ -711,6 +716,14 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, fr->argument, names[fr->index], fr->start, fr->end); } + /* Clear the available area. */ + if (fill != -1) { + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.bg = fill; + for (i = 0; i < available; i++) + screen_write_putc(octx, &gc, ' '); + } + /* * Draw the screens. How they are arranged depends on where the list * appearsq. @@ -23,6 +23,7 @@ #include <errno.h> #include <fnmatch.h> #include <libgen.h> +#include <regex.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> @@ -1067,6 +1068,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers) if (~modifiers & FORMAT_TIMESTRING) { o = options_parse_get(global_options, key, &idx, 0); + if (o == NULL && ft->wp != NULL) + o = options_parse_get(ft->wp->options, key, &idx, 0); if (o == NULL && ft->w != NULL) o = options_parse_get(ft->w->options, key, &idx, 0); if (o == NULL) @@ -1263,7 +1266,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lmCbdtqETSWP<>", cp[0]) != NULL && + if (strchr("lbdtqETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -1284,7 +1287,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) } /* Now try single character with arguments. */ - if (strchr("s=", cp[0]) == NULL) + if (strchr("mCs=", cp[0]) == NULL) break; c = cp[0]; @@ -1345,39 +1348,67 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) return list; } -/* Perform substitution in string. */ +/* Match against an fnmatch(3) pattern or regular expression. */ static char * -format_substitute(const char *source, const char *from, const char *to) +format_match(struct format_modifier *fm, const char *pattern, const char *text) { - char *copy, *new; - const char *cp; - size_t fromlen, tolen, newlen, used; - - fromlen = strlen(from); - tolen = strlen(to); - - newlen = strlen(source) + 1; - copy = new = xmalloc(newlen); - - for (cp = source; *cp != '\0'; /* nothing */) { - if (strncmp(cp, from, fromlen) != 0) { - *new++ = *cp++; - continue; + const char *s = ""; + regex_t r; + int flags = 0; + + if (fm->argc >= 1) + s = fm->argv[0]; + if (strchr(s, 'r') == NULL) { + if (strchr(s, 'i') != NULL) + flags |= FNM_CASEFOLD; + if (fnmatch(pattern, text, flags) != 0) + return (xstrdup("0")); + } else { + flags = REG_EXTENDED|REG_NOSUB; + if (strchr(s, 'i') != NULL) + flags |= REG_ICASE; + if (regcomp(&r, pattern, flags) != 0) + return (xstrdup("0")); + if (regexec(&r, text, 0, NULL, 0) != 0) { + regfree(&r); + return (xstrdup("0")); } - used = new - copy; - - newlen += tolen; - copy = xrealloc(copy, newlen); + regfree(&r); + } + return (xstrdup("1")); +} - new = copy + used; - memcpy(new, to, tolen); +/* Perform substitution in string. */ +static char * +format_sub(struct format_modifier *fm, const char *text, const char *pattern, + const char *with) +{ + char *value; + int flags = REG_EXTENDED; + + if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL) + flags |= REG_ICASE; + value = regsub(pattern, with, text, flags); + if (value == NULL) + return (xstrdup(text)); + return (value); +} - new += tolen; - cp += fromlen; +/* Search inside pane. */ +static char * +format_search(struct format_modifier *fm, struct window_pane *wp, const char *s) +{ + int ignore = 0, regex = 0; + char *value; + + if (fm->argc >= 1) { + if (strchr(fm->argv[0], 'i') != NULL) + ignore = 1; + if (strchr(fm->argv[0], 'r') != NULL) + regex = 1; } - - *new = '\0'; - return (copy); + xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore)); + return (value); } /* Loop over sessions. */ @@ -1522,11 +1553,10 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, char *copy0, *condition, *found, *new; char *value, *left, *right; size_t valuelen; - int modifiers = 0, limit = 0; + int modifiers = 0, limit = 0, j; struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; struct format_modifier *sub = NULL; u_int i, count; - int j; /* Make a copy of the key. */ copy = copy0 = xstrndup(key, keylen); @@ -1553,18 +1583,18 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, search = fm; break; case 's': - if (fm->argc != 2) + if (fm->argc < 2) break; sub = fm; break; case '=': - if (fm->argc != 1 && fm->argc != 2) + if (fm->argc < 1) break; limit = strtonum(fm->argv[0], INT_MIN, INT_MAX, &errptr); if (errptr != NULL) limit = 0; - if (fm->argc == 2 && fm->argv[1] != NULL) + if (fm->argc >= 2 && fm->argv[1] != NULL) marker = fm->argv[1]; break; case 'l': @@ -1630,13 +1660,15 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; } else if (search != NULL) { /* Search in pane. */ + new = format_expand(ft, copy); if (wp == NULL) { - format_log(ft, "search '%s' but no pane", copy); + format_log(ft, "search '%s' but no pane", new); value = xstrdup("0"); } else { - format_log(ft, "search '%s' pane %%%u", copy, wp->id); - xasprintf(&value, "%u", window_pane_search(wp, copy)); + format_log(ft, "search '%s' pane %%%u", new, wp->id); + value = format_search(fm, wp, new); } + free(new); } else if (cmp != NULL) { /* Comparison of left and right. */ if (format_choose(ft, copy, &left, &right, 1) != 0) { @@ -1687,12 +1719,8 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, value = xstrdup("1"); else value = xstrdup("0"); - } else if (strcmp(cmp->modifier, "m") == 0) { - if (fnmatch(left, right, 0) == 0) - value = xstrdup("1"); - else - value = xstrdup("0"); - } + } else if (strcmp(cmp->modifier, "m") == 0) + value = format_match(cmp, left, right); free(right); free(left); @@ -1770,11 +1798,14 @@ done: /* Perform substitution if any. */ if (sub != NULL) { - new = format_substitute(value, sub->argv[0], sub->argv[1]); - format_log(ft, "substituted '%s' to '%s: %s", sub->argv[0], - sub->argv[1], new); + left = format_expand(ft, sub->argv[0]); + right = format_expand(ft, sub->argv[1]); + new = format_sub(sub, value, left, right); + format_log(ft, "substitute '%s' to '%s': %s", left, right, new); free(value); value = new; + free(right); + free(left); } /* Truncate the value if needed. */ @@ -1998,10 +2029,10 @@ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp) { - if (c != NULL) + if (c != NULL && c->name != NULL) log_debug("%s: c=%s", __func__, c->name); else - log_debug("%s: s=none", __func__); + log_debug("%s: c=none", __func__); if (s != NULL) log_debug("%s: s=$%u", __func__, s->id); else @@ -2286,6 +2317,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) !!(wp->base.mode & MODE_KKEYPAD)); format_add(ft, "wrap_flag", "%d", !!(wp->base.mode & MODE_WRAP)); + format_add(ft, "origin_flag", "%d", + !!(wp->base.mode & MODE_ORIGIN)); format_add(ft, "mouse_any_flag", "%d", !!(wp->base.mode & ALL_MOUSE_MODES)); @@ -2295,6 +2328,10 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) !!(wp->base.mode & MODE_MOUSE_BUTTON)); format_add(ft, "mouse_all_flag", "%d", !!(wp->base.mode & MODE_MOUSE_ALL)); + format_add(ft, "mouse_utf8_flag", "%d", + !!(wp->base.mode & MODE_MOUSE_UTF8)); + format_add(ft, "mouse_sgr_flag", "%d", + !!(wp->base.mode & MODE_MOUSE_SGR)); format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); } diff --git a/grid-view.c b/grid-view.c index a4bd5ba2..fa3bfbf6 100644 --- a/grid-view.c +++ b/grid-view.c @@ -214,7 +214,6 @@ grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx, u_int bg) sx = grid_view_x(gd, gd->sx); grid_move_cells(gd, px, px + nx, py, sx - px - nx, bg); - grid_clear(gd, sx - nx, py, px + nx - (sx - nx), 1, bg); } /* Convert cells into a string. */ @@ -37,12 +37,12 @@ /* Default grid cell data. */ const struct grid_cell grid_default_cell = { - 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } + { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 }; /* Cleared grid cell data. */ const struct grid_cell grid_cleared_cell = { - GRID_FLAG_CLEARED, 0, 8, 8, { { ' ' }, 0, 1, 1 } + { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0 }; static const struct grid_cell_entry grid_cleared_entry = { GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } } @@ -82,6 +82,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce, return (1); if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB)) return (1); + if (gc->us != 0) /* only supports 256 or RGB */ + return (1); return (0); } @@ -473,6 +475,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) gc->bg = gce->data.bg; if (gce->flags & GRID_FLAG_BG256) gc->bg |= COLOUR_FLAG_256; + gc->us = 0; utf8_set(&gc->data, gce->data.data); } @@ -544,7 +547,7 @@ void grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) { struct grid_line *gl; - u_int xx, yy; + u_int xx, yy, ox, sx; if (nx == 0 || ny == 0) return; @@ -561,16 +564,20 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) for (yy = py; yy < py + ny; yy++) { gl = &gd->linedata[yy]; - if (px + nx >= gd->sx && px < gl->cellused) - gl->cellused = px; - if (px > gl->cellsize && COLOUR_DEFAULT(bg)) - continue; - if (px + nx >= gl->cellsize && COLOUR_DEFAULT(bg)) { - gl->cellsize = px; - continue; + + sx = gd->sx; + if (sx > gl->cellsize) + sx = gl->cellsize; + ox = nx; + if (COLOUR_DEFAULT(bg)) { + if (px > sx) + continue; + if (px + nx > sx) + ox = sx - px; } - grid_expand_line(gd, yy, px + nx, 8); /* default bg first */ - for (xx = px; xx < px + nx; xx++) + + grid_expand_line(gd, yy, px + ox, 8); /* default bg first */ + for (xx = px; xx < px + ox; xx++) grid_clear_cell(gd, xx, yy, bg); } } @@ -1213,6 +1220,10 @@ grid_reflow(struct grid *gd, u_int sx) struct grid_cell gc; u_int yy, width, i, at, first; + /* Do not reflow to the same size. */ + if (sx == gd->sx) + return; + /* * Create a destination grid. This is just used as a container for the * line data and may not be fully valid. diff --git a/input-keys.c b/input-keys.c index f0a38c09..9e47a553 100644 --- a/input-keys.c +++ b/input-keys.c @@ -172,6 +172,13 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) return; } + /* 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); + return; + } + /* * If this is a normal 7-bit key, just send it, with a leading escape * if necessary. If it is a UTF-8 key, split it and send it. @@ -1829,6 +1829,8 @@ input_csi_dispatch_sgr_256_do(struct input_ctx *ictx, int fgbg, int c) gc->fg = c | COLOUR_FLAG_256; else if (fgbg == 48) gc->bg = c | COLOUR_FLAG_256; + else if (fgbg == 58) + gc->us = c | COLOUR_FLAG_256; } return (1); } @@ -1862,6 +1864,8 @@ input_csi_dispatch_sgr_rgb_do(struct input_ctx *ictx, int fgbg, int r, int g, gc->fg = colour_join_rgb(r, g, b); else if (fgbg == 48) gc->bg = colour_join_rgb(r, g, b); + else if (fgbg == 58) + gc->us = colour_join_rgb(r, g, b); return (1); } @@ -1938,7 +1942,7 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i) } return; } - if (n < 2 || (p[0] != 38 && p[0] != 48)) + if (n < 2 || (p[0] != 38 && p[0] != 48 && p[0] != 58)) return; switch (p[1]) { case 2: @@ -1983,7 +1987,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) if (n == -1) continue; - if (n == 38 || n == 48) { + if (n == 38 || n == 48 || n == 58) { i++; switch (input_get(ictx, i, 0, -1)) { case 2: @@ -2078,6 +2082,9 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 55: gc->attr &= ~GRID_ATTR_OVERLINE; break; + case 59: + gc->us = 0; + break; case 90: case 91: case 92: @@ -2259,7 +2266,7 @@ input_exit_rename(struct input_ctx *ictx) { if (ictx->flags & INPUT_DISCARD) return; - if (!options_get_number(ictx->wp->window->options, "allow-rename")) + if (!options_get_number(ictx->wp->options, "allow-rename")) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); @@ -2347,12 +2354,14 @@ input_osc_10(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; u_int r, g, b; + char tmp[16]; if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) goto bad; - - wp->style.gc.fg = colour_join_rgb(r, g, b); - wp->flags |= PANE_REDRAW; + xsnprintf(tmp, sizeof tmp, "fg=#%02x%02x%02x", r, g, b); + options_set_style(wp->options, "window-style", 1, tmp); + options_set_style(wp->options, "window-active-style", 1, tmp); + wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; @@ -2366,12 +2375,14 @@ input_osc_11(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; u_int r, g, b; + char tmp[16]; if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) goto bad; - - wp->style.gc.bg = colour_join_rgb(r, g, b); - wp->flags |= PANE_REDRAW; + xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x", r, g, b); + options_set_style(wp->options, "window-style", 1, tmp); + options_set_style(wp->options, "window-active-style", 1, tmp); + wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; @@ -117,7 +117,7 @@ job_run(const char *cmd, struct session *s, const char *cwd, close(out[0]); nullfd = open(_PATH_DEVNULL, O_RDWR, 0); - if (nullfd < 0) + if (nullfd == -1) fatal("open failed"); if (dup2(nullfd, STDERR_FILENO) == -1) fatal("dup2 failed"); diff --git a/key-string.c b/key-string.c index 8442727d..a1ef4f51 100644 --- a/key-string.c +++ b/key-string.c @@ -284,6 +284,12 @@ key_string_lookup_key(key_code key) return (out); } + /* Literal keys are themselves. */ + if (key & KEYC_LITERAL) { + snprintf(out, sizeof out, "%c", (int)(key & 0xff)); + return (out); + } + /* * Special case: display C-@ as C-Space. Could do this below in * the (key >= 0 && key <= 32), but this way we let it be found diff --git a/layout-custom.c b/layout-custom.c index 9886afe1..d759c206 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -122,7 +122,7 @@ layout_parse(struct window *w, const char *layout) { struct layout_cell *lc, *lcchild; struct window_pane *wp; - u_int npanes, ncells, sx, sy; + u_int npanes, ncells; u_short csum; /* Check validity. */ @@ -153,8 +153,7 @@ layout_parse(struct window *w, const char *layout) layout_destroy_cell(w, lcchild, &lc); } - /* Save the old window size and resize to the layout size. */ - sx = w->sx; sy = w->sy; + /* Resize to the layout size. */ window_resize(w, lc->sx, lc->sy); /* Destroy the old layout and swap to the new. */ @@ -166,12 +165,9 @@ layout_parse(struct window *w, const char *layout) layout_assign(&wp, lc); /* Update pane offsets and sizes. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); - - /* Then resize the layout back to the original window size. */ - layout_resize(w, sx, sy); - window_resize(w, sx, sy); + recalculate_sizes(); layout_print_cell(lc, __func__, 0); diff --git a/layout-set.c b/layout-set.c index 12b4780f..82247149 100644 --- a/layout-set.c +++ b/layout-set.c @@ -158,7 +158,7 @@ layout_set_even(struct window *w, enum layout_type type) layout_spread_cell(w, lc); /* Fix cell offsets. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -257,7 +257,7 @@ layout_set_main_h(struct window *w) } /* Fix cell offsets. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -344,7 +344,7 @@ layout_set_main_v(struct window *w) } /* Fix cell offsets. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -453,7 +453,7 @@ layout_set_tiled(struct window *w) } /* Fix cell offsets. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -39,7 +39,6 @@ static int layout_resize_pane_grow(struct window *, struct layout_cell *, enum layout_type, int, int); static int layout_resize_pane_shrink(struct window *, struct layout_cell *, enum layout_type, int); -static int layout_need_status(struct layout_cell *, int); static u_int layout_new_pane_size(struct window *, u_int, struct layout_cell *, enum layout_type, u_int, u_int, u_int); @@ -199,9 +198,9 @@ layout_make_node(struct layout_cell *lc, enum layout_type type) lc->wp = NULL; } -/* Fix cell offsets based on their sizes. */ -void -layout_fix_offsets(struct layout_cell *lc) +/* Fix cell offsets for a child cell. */ +static void +layout_fix_offsets1(struct layout_cell *lc) { struct layout_cell *lcchild; u_int xoff, yoff; @@ -212,7 +211,7 @@ layout_fix_offsets(struct layout_cell *lc) lcchild->xoff = xoff; lcchild->yoff = lc->yoff; if (lcchild->type != LAYOUT_WINDOWPANE) - layout_fix_offsets(lcchild); + layout_fix_offsets1(lcchild); xoff += lcchild->sx + 1; } } else { @@ -221,61 +220,92 @@ layout_fix_offsets(struct layout_cell *lc) lcchild->xoff = lc->xoff; lcchild->yoff = yoff; if (lcchild->type != LAYOUT_WINDOWPANE) - layout_fix_offsets(lcchild); + layout_fix_offsets1(lcchild); yoff += lcchild->sy + 1; } } } -/* - * Returns 1 if we need to reserve space for the pane status line. This is the - * case for the most upper panes only. - */ +/* Update cell offsets based on their sizes. */ +void +layout_fix_offsets(struct window *w) +{ + struct layout_cell *lc = w->layout_root; + + lc->xoff = 0; + lc->yoff = 0; + + layout_fix_offsets1(lc); +} + +/* Is this a top cell? */ static int -layout_need_status(struct layout_cell *lc, int at_top) +layout_cell_is_top(struct window *w, struct layout_cell *lc) { - struct layout_cell *first_lc; + struct layout_cell *next; - if (lc->parent != NULL) { - if (lc->parent->type == LAYOUT_LEFTRIGHT) - return (layout_need_status(lc->parent, at_top)); + while (lc != w->layout_root) { + next = lc->parent; + if (next->type == LAYOUT_TOPBOTTOM && + lc != TAILQ_FIRST(&next->cells)) + return (0); + lc = next; + } + return (1); +} - if (at_top) - first_lc = TAILQ_FIRST(&lc->parent->cells); - else - first_lc = TAILQ_LAST(&lc->parent->cells,layout_cells); - if (lc == first_lc) - return (layout_need_status(lc->parent, at_top)); - return (0); +/* Is this a bottom cell? */ +static int +layout_cell_is_bottom(struct window *w, struct layout_cell *lc) +{ + struct layout_cell *next; + + while (lc != w->layout_root) { + next = lc->parent; + if (next->type == LAYOUT_TOPBOTTOM && + lc != TAILQ_LAST(&next->cells, layout_cells)) + return (0); + lc = next; } return (1); } +/* + * Returns 1 if we need to add an extra line for the pane status line. This is + * the case for the most upper or lower panes only. + */ +static int +layout_add_border(struct window *w, struct layout_cell *lc, int status) +{ + if (status == PANE_STATUS_TOP) + return (layout_cell_is_top(w, lc)); + if (status == PANE_STATUS_BOTTOM) + return (layout_cell_is_bottom(w, lc)); + return (0); +} + /* Update pane offsets and sizes based on their cells. */ void layout_fix_panes(struct window *w) { struct window_pane *wp; struct layout_cell *lc; - int shift, status; + int status; status = options_get_number(w->options, "pane-border-status"); TAILQ_FOREACH(wp, &w->panes, entry) { if ((lc = wp->layout_cell) == NULL) continue; - if (status != 0) - shift = layout_need_status(lc, status == 1); - else - shift = 0; - wp->xoff = lc->xoff; wp->yoff = lc->yoff; - if (shift && status == 1) - wp->yoff += 1; - - window_pane_resize(wp, lc->sx, lc->sy - shift); + if (layout_add_border(w, lc, status)) { + if (status == PANE_STATUS_TOP) + wp->yoff++; + window_pane_resize(wp, lc->sx, lc->sy - 1); + } else + window_pane_resize(wp, lc->sx, lc->sy); } } @@ -312,13 +342,15 @@ layout_resize_check(struct window *w, struct layout_cell *lc, status = options_get_number(w->options, "pane-border-status"); if (lc->type == LAYOUT_WINDOWPANE) { /* Space available in this cell only. */ - minimum = PANE_MINIMUM; - if (type == LAYOUT_LEFTRIGHT) + if (type == LAYOUT_LEFTRIGHT) { available = lc->sx; - else { + minimum = PANE_MINIMUM; + } else { available = lc->sy; - if (status != 0) - minimum += layout_need_status(lc, status == 1); + if (layout_add_border(w, lc, status)) + minimum = PANE_MINIMUM + 1; + else + minimum = PANE_MINIMUM; } if (available > minimum) available -= minimum; @@ -507,7 +539,7 @@ layout_resize(struct window *w, u_int sx, u_int sy) layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange); /* Fix cell offsets. */ - layout_fix_offsets(lc); + layout_fix_offsets(w); layout_fix_panes(w); } @@ -567,7 +599,7 @@ layout_resize_layout(struct window *w, struct layout_cell *lc, } /* Fix cell offsets. */ - layout_fix_offsets(w->layout_root); + layout_fix_offsets(w); layout_fix_panes(w); notify_window("window-layout-changed", w); } @@ -861,9 +893,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, return (NULL); break; case LAYOUT_TOPBOTTOM: - minimum = PANE_MINIMUM * 2 + 1; - if (status != 0) - minimum += layout_need_status(lc, status == 1); + if (layout_add_border(wp->window, lc, status)) + minimum = PANE_MINIMUM * 2 + 2; + else + minimum = PANE_MINIMUM * 2 + 1; if (sy < minimum) return (NULL); break; @@ -988,7 +1021,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, if (full_size) { if (!resize_first) layout_resize_child_cells(wp->window, lc); - layout_fix_offsets(wp->window->layout_root); + layout_fix_offsets(wp->window); } else layout_make_leaf(lc, wp); @@ -1006,7 +1039,7 @@ layout_close_pane(struct window_pane *wp) /* Fix pane offsets and sizes. */ if (w->layout_root != NULL) { - layout_fix_offsets(w->layout_root); + layout_fix_offsets(w); layout_fix_panes(w); } notify_window("window-layout-changed", w); @@ -1021,7 +1054,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) number = 0; TAILQ_FOREACH (lc, &parent->cells, entry) - number++; + number++; if (number <= 1) return (0); status = options_get_number(w->options, "pane-border-status"); @@ -1029,9 +1062,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) if (parent->type == LAYOUT_LEFTRIGHT) size = parent->sx; else if (parent->type == LAYOUT_TOPBOTTOM) { - size = parent->sy; - if (status != 0) - size -= layout_need_status(parent, status == 1); + if (layout_add_border(w, parent, status)) + size = parent->sy - 1; + else + size = parent->sy; } else return (0); if (size < number - 1) @@ -1049,9 +1083,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) change = each - (int)lc->sx; layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change); } else if (parent->type == LAYOUT_TOPBOTTOM) { - this = each; - if (status != 0) - this += layout_need_status(lc, status == 1); + if (layout_add_border(w, lc, status)) + this = each + 1; + else + this = each; change = this - (int)lc->sy; layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change); } @@ -1073,7 +1108,7 @@ layout_spread_out(struct window_pane *wp) do { if (layout_spread_cell(w, parent)) { - layout_fix_offsets(parent); + layout_fix_offsets(w); layout_fix_panes(w); break; } @@ -161,7 +161,7 @@ menu_free_cb(struct client *c) struct menu_data *md = c->overlay_data; if (md->item != NULL) - md->item->flags &= ~CMDQ_WAITING; + cmdq_continue(md->item); if (md->cb != NULL) md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); @@ -206,8 +206,18 @@ menu_key_cb(struct client *c, struct key_event *event) c->flags |= CLIENT_REDRAWOVERLAY; return (0); } + for (i = 0; i < (u_int)count; i++) { + name = menu->items[i].name; + if (name == NULL || *name == '-') + continue; + if (event->key == menu->items[i].key) { + md->choice = i; + goto chosen; + } + } switch (event->key) { case KEYC_UP: + case 'k': if (old == -1) old = 0; do { @@ -220,6 +230,7 @@ menu_key_cb(struct client *c, struct key_event *event) c->flags |= CLIENT_REDRAWOVERLAY; return (0); case KEYC_DOWN: + case 'j': if (old == -1) old = 0; do { @@ -239,15 +250,6 @@ menu_key_cb(struct client *c, struct key_event *event) case 'q': return (1); } - for (i = 0; i < (u_int)count; i++) { - name = menu->items[i].name; - if (name == NULL || *name == '-') - continue; - if (event->key == menu->items[i].key) { - md->choice = i; - goto chosen; - } - } return (0); chosen: diff --git a/mode-tree.c b/mode-tree.c index 75034675..03a91ef8 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -480,7 +480,7 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, saved = mode_tree_find_item(&mtd->saved, tag); if (saved != NULL) { - if (parent == NULL || (parent != NULL && parent->expanded)) + if (parent == NULL || parent->expanded) mti->tagged = saved->tagged; mti->expanded = saved->expanded; } else if (expanded == -1) @@ -933,6 +933,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, case '\016': /* C-n */ mode_tree_down(mtd, 1); break; + case 'g': case KEYC_PPAGE: case '\002': /* C-b */ for (i = 0; i < mtd->height; i++) { @@ -941,6 +942,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_up(mtd, 1); } break; + case 'G': case KEYC_NPAGE: case '\006': /* C-f */ for (i = 0; i < mtd->height; i++) { @@ -1019,6 +1021,8 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_build(mtd); } break; + case '?': + case '/': case '\023': /* C-s */ mtd->references++; status_prompt_set(c, "(search) ", "", diff --git a/options-table.c b/options-table.c index cb258014..ba7db3e1 100644 --- a/options-table.c +++ b/options-table.c @@ -562,13 +562,13 @@ const struct options_table_entry options_table[] = { { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_num = 0 }, { .name = "alternate-screen", .type = OPTIONS_TABLE_FLAG, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_num = 1 }, @@ -688,7 +688,7 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_pane_status_list, - .default_num = 0 + .default_num = PANE_STATUS_OFF }, { .name = "pane-border-style", @@ -699,7 +699,7 @@ const struct options_table_entry options_table[] = { { .name = "remain-on-exit", .type = OPTIONS_TABLE_FLAG, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_num = 0 }, @@ -711,7 +711,7 @@ const struct options_table_entry options_table[] = { { .name = "window-active-style", .type = OPTIONS_TABLE_STYLE, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "default" }, @@ -724,7 +724,7 @@ const struct options_table_entry options_table[] = { { .name = "window-style", .type = OPTIONS_TABLE_STYLE, - .scope = OPTIONS_TABLE_WINDOW, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "default" }, @@ -100,7 +100,7 @@ options_parent_table_entry(struct options *oo, const char *s) if (oo->parent == NULL) fatalx("no parent options for %s", s); - o = options_get_only(oo->parent, s); + o = options_get(oo->parent, s); if (o == NULL) fatalx("%s not in parent options", s); return (o->tableentry); @@ -178,6 +178,12 @@ options_free(struct options *oo) free(oo); } +void +options_set_parent(struct options *oo, struct options *parent) +{ + oo->parent = parent; +} + struct options_entry * options_first(struct options *oo) { @@ -365,7 +371,8 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, pr = cmd_parse_from_string(value, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: - *cause = xstrdup("empty command"); + if (cause != NULL) + *cause = xstrdup("empty command"); return (-1); case CMD_PARSE_ERROR: if (cause != NULL) @@ -544,7 +551,7 @@ options_parse_get(struct options *oo, const char *s, int *idx, int only) } char * -options_match(const char *s, int *idx, int* ambiguous) +options_match(const char *s, int *idx, int *ambiguous) { const struct options_table_entry *oe, *found; char *name; @@ -724,20 +731,102 @@ options_set_style(struct options *oo, const char *name, int append, return (o); } -enum options_table_scope +int +options_scope_from_name(struct args *args, int window, + const char *name, struct cmd_find_state *fs, struct options **oo, + char **cause) +{ + struct session *s = fs->s; + struct winlink *wl = fs->wl; + struct window_pane *wp = fs->wp; + const char *target = args_get(args, 't'); + const struct options_table_entry *oe; + int scope = OPTIONS_TABLE_NONE; + + if (*name == '@') + return (options_scope_from_flags(args, window, fs, oo, cause)); + + for (oe = options_table; oe->name != NULL; oe++) { + if (strcmp(oe->name, name) == 0) + break; + } + if (oe->name == NULL) { + xasprintf(cause, "unknown option: %s", name); + return (OPTIONS_TABLE_NONE); + } + switch (oe->scope) { + case OPTIONS_TABLE_SERVER: + *oo = global_options; + scope = OPTIONS_TABLE_SERVER; + break; + case OPTIONS_TABLE_SESSION: + if (args_has(args, 'g')) { + *oo = global_s_options; + scope = OPTIONS_TABLE_SESSION; + } else if (s == NULL && target != NULL) + xasprintf(cause, "no such session: %s", target); + else if (s == NULL) + xasprintf(cause, "no current session"); + else { + *oo = s->options; + scope = OPTIONS_TABLE_SESSION; + } + break; + case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE: + if (args_has(args, 'p')) { + if (wp == NULL && target != NULL) + xasprintf(cause, "no such pane: %s", target); + else if (wp == NULL) + xasprintf(cause, "no current pane"); + else { + *oo = wp->options; + scope = OPTIONS_TABLE_PANE; + } + break; + } + /* FALLTHROUGH */ + case OPTIONS_TABLE_WINDOW: + if (args_has(args, 'g')) { + *oo = global_w_options; + scope = OPTIONS_TABLE_WINDOW; + } else if (wl == NULL && target != NULL) + xasprintf(cause, "no such window: %s", target); + else if (wl == NULL) + xasprintf(cause, "no current window"); + else { + *oo = wl->window->options; + scope = OPTIONS_TABLE_WINDOW; + } + break; + } + return (scope); +} + +int options_scope_from_flags(struct args *args, int window, struct cmd_find_state *fs, struct options **oo, char **cause) { - struct session *s = fs->s; - struct winlink *wl = fs->wl; - const char *target= args_get(args, 't'); + struct session *s = fs->s; + struct winlink *wl = fs->wl; + struct window_pane *wp = fs->wp; + const char *target = args_get(args, 't'); if (args_has(args, 's')) { *oo = global_options; return (OPTIONS_TABLE_SERVER); } - if (window || args_has(args, 'w')) { + if (args_has(args, 'p')) { + if (wp == NULL) { + if (target != NULL) + xasprintf(cause, "no such pane: %s", target); + else + xasprintf(cause, "no current pane"); + return (OPTIONS_TABLE_NONE); + } + *oo = wp->options; + return (OPTIONS_TABLE_PANE); + } else if (window || args_has(args, 'w')) { if (args_has(args, 'g')) { *oo = global_w_options; return (OPTIONS_TABLE_WINDOW); diff --git a/osdep-netbsd.c b/osdep-netbsd.c index daca1abb..67894175 100644 --- a/osdep-netbsd.c +++ b/osdep-netbsd.c @@ -133,13 +133,27 @@ char * osdep_get_cwd(int fd) { static char target[PATH_MAX + 1]; - char *path; pid_t pgrp; +#ifdef KERN_PROC_CWD + int mib[4]; + size_t len; +#else + char *path; ssize_t n; +#endif if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); +#ifdef KERN_PROC_CWD + mib[0] = CTL_KERN; + mib[1] = KERN_PROC_ARGS; + mib[2] = pgrp; + mib[3] = KERN_PROC_CWD; + len = sizeof(target); + if (sysctl(mib, __arraycount(mib), target, &len, NULL, 0) == 0) + return (target); +#else xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); n = readlink(path, target, sizeof(target) - 1); free(path); @@ -147,6 +161,7 @@ osdep_get_cwd(int fd) target[n] = '\0'; return (target); } +#endif return (NULL); } diff --git a/regress/command-order.sh b/regress/command-order.sh index fb4e0324..04046f0d 100644 --- a/regress/command-order.sh +++ b/regress/command-order.sh @@ -1,7 +1,5 @@ #!/bin/sh -# new-session without clients should be the right size - PATH=/bin:/usr/bin TERM=screen @@ -19,6 +17,7 @@ EOF $TMUX -f$TMP start </dev/null || exit 1 sleep 1 $TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1 +$TMUX kill-server 2>/dev/null cat <<EOF|cmp -s $TMP - || exit 1 bar,bar0 bar,bar1 @@ -27,7 +26,6 @@ foo,foo0 foo,foo1 foo,foo2 EOF -$TMUX kill-server 2>/dev/null cat <<EOF >$TMP new -sfoo -nfoo0 @@ -40,6 +38,7 @@ EOF $TMUX -f$TMP start </dev/null || exit 1 sleep 1 $TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1 +$TMUX kill-server 2>/dev/null cat <<EOF|cmp -s $TMP - || exit 1 bar,bar0 bar,bar1 @@ -48,6 +47,5 @@ foo,foo0 foo,foo1 foo,foo2 EOF -$TMUX kill-server 2>/dev/null exit 0 diff --git a/regress/conf/21867280ff7e99631046f9cc669b80d2.conf b/regress/conf/21867280ff7e99631046f9cc669b80d2.conf new file mode 100644 index 00000000..43b142b4 --- /dev/null +++ b/regress/conf/21867280ff7e99631046f9cc669b80d2.conf @@ -0,0 +1,8 @@ +%if #{l:1} +set -g status-style fg=cyan,bg='#001040' +%elif #{l:1} +set -g status-style fg=white,bg='#400040' +%else +set -g status-style fg=white,bg='#800000' +%endif +bind ^X last-window diff --git a/regress/conf/99749670b62bcb99a9b2e3d59708e357.conf b/regress/conf/99749670b62bcb99a9b2e3d59708e357.conf new file mode 100644 index 00000000..dd1700b0 --- /dev/null +++ b/regress/conf/99749670b62bcb99a9b2e3d59708e357.conf @@ -0,0 +1,93 @@ +# ----------------------------------------------------------------------------- +# This config is targeted for tmux 2.1+ and should be placed in $HOME. +# +# Read the "Plugin Manager" section (bottom) before trying to use this config! +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# Global options +# ----------------------------------------------------------------------------- + +# Set a new prefix / leader key. +set -g prefix ` +bind ` send-prefix + +# Allow opening multiple terminals to view the same session at different sizes. +setw -g aggressive-resize on + +# Remove delay when switching between Vim modes. +set -s escape-time 0 + +# Allow Vim's FocusGained to work when your terminal gains focus. +# Requires Vim plugin: https://github.com/tmux-plugins/vim-tmux-focus-events +set -g focus-events on + +# Add a bit more scroll history in the buffer. +set -g history-limit 50000 + +# Enable color support inside of tmux. +set -g default-terminal "screen-256color" + +# Ensure window titles get renamed automatically. +setw -g automatic-rename + +# Start windows and panes index at 1, not 0. +set -g base-index 1 +setw -g pane-base-index 1 + +# Enable full mouse support. +set -g mouse on + +# Status bar optimized for Gruvbox. +set -g status-fg colour244 +set -g status-bg default +set -g status-left '' +set -g status-right-length 0 +#set -g status-right-length 20 +#set -g status-right '%a %Y-%m-%d %H:%M' + +set -g pane-border-fg default +set -g pane-border-bg default +set -g pane-active-border-fg colour250 +set -g pane-active-border-bg default + +set-window-option -g window-status-current-attr bold +set-window-option -g window-status-current-fg colour223 + +# ----------------------------------------------------------------------------- +# Key bindings +# ----------------------------------------------------------------------------- + +# Unbind default keys +unbind C-b +unbind '"' +unbind % + +# Reload the tmux config. +bind-key r source-file ~/.tmux.conf + +# Split panes. +bind-key h split-window -v +bind-key v split-window -h + +# Move around panes with ALT + arrow keys. +bind-key -n M-Up select-pane -U +bind-key -n M-Left select-pane -L +bind-key -n M-Down select-pane -D +bind-key -n M-Right select-pane -R + +# ----------------------------------------------------------------------------- +# Plugin Manager - https://github.com/tmux-plugins/tpm +# In order to use the plugins below you need to install TPM and the plugins. +# Step 1) git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm +# Step 2) Reload tmux if it's already started with `r +# Step 3) Launch tmux and hit `I (capital i) to fetch any plugins +# ----------------------------------------------------------------------------- + +# List of plugins. +set -g @plugin 'tmux-plugins/tpm' +set -g @plugin 'tmux-plugins/tmux-resurrect' +set -g @plugin 'tmux-plugins/tmux-yank' + +# Initialize TPM (keep this line at the very bottom of your tmux.conf). +run -b '~/.tmux/plugins/tpm/tpm' diff --git a/regress/xenl-terminal.sh b/regress/xenl-terminal.sh new file mode 100644 index 00000000..07469ceb --- /dev/null +++ b/regress/xenl-terminal.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest" +$TMUX kill-server 2>/dev/null +TMUX2="$TEST_TMUX -Ltest2" +$TMUX2 kill-server 2>/dev/null + +TMP=$(mktemp) +trap "rm -f $TMP" 0 1 15 + +$TMUX2 -f/dev/null new -d || exit 1 +$TMUX2 set -as terminal-overrides ',*:xenl@' || exit 1 +$TMUX2 set -g status-right 'RRR' || exit 1 +$TMUX2 set -g status-left 'LLL' || exit 1 +$TMUX2 set -g window-status-current-format 'WWW' || exit 1 +$TMUX -f/dev/null new -x20 -y2 -d "$TMUX2 attach" || exit 1 +sleep 1 +$TMUX capturep -p|tail -1 >$TMP || exit 1 +$TMUX kill-server 2>/dev/null +$TMUX2 kill-server 2>/dev/null +cat <<EOF|cmp -s $TMP - || exit 1 +LLLWWW RR +EOF + +exit 0 diff --git a/regsub.c b/regsub.c new file mode 100644 index 00000000..89355bef --- /dev/null +++ b/regsub.c @@ -0,0 +1,115 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2019 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 <regex.h> +#include <string.h> + +#include "tmux.h" + +static void +regsub_copy(char **buf, size_t *len, const char *text, size_t start, + size_t end) +{ + size_t add = end - start; + + *buf = xrealloc(*buf, (*len) + add + 1); + memcpy((*buf) + *len, text + start, add); + (*len) += add; +} + +static void +regsub_expand(char **buf, size_t *len, const char *with, const char *text, + regmatch_t *m, u_int n) +{ + const char *cp; + u_int i; + + for (cp = with; *cp != '\0'; cp++) { + if (*cp == '\\') { + cp++; + if (*cp >= '0' && *cp <= '9') { + i = *cp - '0'; + if (i < n && m[i].rm_so != m[i].rm_eo) { + regsub_copy(buf, len, text, m[i].rm_so, + m[i].rm_eo); + continue; + } + } + } + *buf = xrealloc(*buf, (*len) + 2); + (*buf)[(*len)++] = *cp; + } +} + +char * +regsub(const char *pattern, const char *with, const char *text, int flags) +{ + regex_t r; + regmatch_t m[10]; + ssize_t start, end, last, len = 0; + int empty = 0; + char *buf = NULL; + + if (*text == '\0') + return (xstrdup("")); + if (regcomp(&r, pattern, flags) != 0) + return (NULL); + + start = 0; + last = 0; + end = strlen(text); + + while (start <= end) { + m[0].rm_so = start; + m[0].rm_eo = end; + + if (regexec(&r, text, nitems(m), m, REG_STARTEND) != 0) { + regsub_copy(&buf, &len, text, start, end); + break; + } + + /* + * Append any text not part of this match (from the end of the + * last match). + */ + regsub_copy(&buf, &len, text, last, m[0].rm_so); + + /* + * If the last match was empty and this one isn't (it is either + * later or has matched text), expand this match. If it is + * empty, move on one character and try again from there. + */ + if (empty || m[0].rm_so != last || m[0].rm_so != m[0].rm_eo) { + regsub_expand(&buf, &len, with, text, m, nitems(m)); + + last = m[0].rm_eo; + start = m[0].rm_eo; + empty = 0; + } else { + last = m[0].rm_eo; + start = m[0].rm_eo + 1; + empty = 1; + } + } + buf[len] = '\0'; + + regfree(&r); + return (buf); +} diff --git a/screen-redraw.c b/screen-redraw.c index 65e890b6..e7f4f077 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -45,10 +45,6 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *, #define CELL_BORDERS " xqlkmjwvtun~" -#define CELL_STATUS_OFF 0 -#define CELL_STATUS_TOP 1 -#define CELL_STATUS_BOTTOM 2 - /* Check if cell is on the border of a particular pane. */ static int screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py) @@ -112,12 +108,12 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, if (px > w->sx || py > w->sy) return (CELL_OUTSIDE); - if (pane_status != CELL_STATUS_OFF) { + if (pane_status != PANE_STATUS_OFF) { TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; - if (pane_status == CELL_STATUS_TOP) + if (pane_status == PANE_STATUS_TOP) line = wp->yoff - 1; else line = wp->yoff + wp->sy; @@ -153,7 +149,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, borders |= 8; if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py)) borders |= 4; - if (pane_status == CELL_STATUS_TOP) { + if (pane_status == PANE_STATUS_TOP) { if (py != 0 && screen_redraw_cell_border(c, px, py - 1)) borders |= 2; } else { @@ -208,9 +204,9 @@ screen_redraw_check_is(u_int px, u_int py, int type, int pane_status, border = screen_redraw_cell_border1(wantwp, px, py); if (border == 0 || border == -1) return (0); - if (pane_status == CELL_STATUS_TOP && border == 4) + if (pane_status == PANE_STATUS_TOP && border == 4) return (0); - if (pane_status == CELL_STATUS_BOTTOM && border == 3) + if (pane_status == PANE_STATUS_BOTTOM && border == 3) return (0); /* If there are more than two panes, that's enough. */ @@ -222,7 +218,7 @@ screen_redraw_check_is(u_int px, u_int py, int type, int pane_status, return (1); /* With status lines mark the entire line. */ - if (pane_status != CELL_STATUS_OFF) + if (pane_status != PANE_STATUS_OFF) return (1); /* Check if the pane covers the whole width. */ @@ -270,7 +266,7 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, fmt = options_get_string(w->options, "pane-border-format"); - ft = format_create(c, NULL, FORMAT_PANE|wp->id, 0); + ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); format_defaults(ft, c, NULL, NULL, wp); expanded = format_expand_time(ft, fmt); @@ -324,7 +320,7 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) s = &wp->status_screen; size = wp->status_size; - if (ctx->pane_status == CELL_STATUS_TOP) + if (ctx->pane_status == PANE_STATUS_TOP) yoff = wp->yoff - 1; else yoff = wp->yoff + wp->sy; @@ -386,7 +382,7 @@ screen_redraw_update(struct client *c, int flags) if (c->overlay_draw != NULL) flags |= CLIENT_REDRAWOVERLAY; - if (options_get_number(wo, "pane-border-status") != CELL_STATUS_OFF) { + if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) { redraw = 0; TAILQ_FOREACH(wp, &w->panes, entry) { if (screen_redraw_make_pane_status(c, w, wp)) @@ -441,7 +437,7 @@ screen_redraw_screen(struct client *c) screen_redraw_set_context(c, &ctx); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { - if (ctx.pane_status != CELL_STATUS_OFF) + if (ctx.pane_status != PANE_STATUS_OFF) screen_redraw_draw_pane_status(&ctx); screen_redraw_draw_borders(&ctx); } diff --git a/screen-write.c b/screen-write.c index 174c7a82..98cdf158 100644 --- a/screen-write.c +++ b/screen-write.c @@ -36,7 +36,7 @@ static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, const struct utf8_data *, u_int *); static const struct grid_cell screen_write_pad_cell = { - GRID_FLAG_PADDING, 0, 8, 8, { { 0 }, 0, 0, 0 } + { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 0, 8, 8 }; struct screen_write_collect_item { @@ -1169,11 +1169,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) void screen_write_clearhistory(struct screen_write_ctx *ctx) { - struct screen *s = ctx->s; - struct grid *gd = s->grid; - - grid_move_lines(gd, 0, gd->hsize, gd->sy, 8); - gd->hscrolled = gd->hsize = 0; + grid_clear_history(ctx->s->grid); } /* Clear a collected line. */ diff --git a/server-client.c b/server-client.c index 0d0d561c..f44631c9 100644 --- a/server-client.c +++ b/server-client.c @@ -522,9 +522,10 @@ have_event: /* Is this on the status line? */ m->statusat = status_at_line(c); + m->statuslines = status_line_size(c); if (m->statusat != -1 && y >= (u_int)m->statusat && - y < m->statusat + status_line_size(c)) { + y < m->statusat + m->statuslines) { sr = status_get_range(c, x, y - m->statusat); if (sr == NULL) { where = STATUS_DEFAULT; @@ -553,8 +554,8 @@ have_event: /* Not on status line. Adjust position and check for border or pane. */ if (where == NOWHERE) { px = x; - if (m->statusat == 0 && y > 0) - py = y - 1; + if (m->statusat == 0 && y >= m->statuslines) + py = y - m->statuslines; else if (m->statusat > 0 && y >= (u_int)m->statusat) py = m->statusat - 1; else @@ -1024,16 +1025,6 @@ server_client_key_callback(struct cmdq_item *item, void *data) fatal("gettimeofday failed"); session_update_activity(s, &c->activity_time); - /* Handle status line. */ - if (~c->flags & CLIENT_READONLY) - status_message_clear(c); - if (c->prompt_string != NULL) { - if (c->flags & CLIENT_READONLY) - goto out; - if (status_prompt_key(c, key) == 0) - goto out; - } - /* Check for mouse keys. */ m->valid = 0; if (key == KEYC_MOUSE) { @@ -1214,17 +1205,26 @@ server_client_handle_key(struct client *c, struct key_event *event) return (0); /* - * Key presses in overlay mode are a special case. The queue might be - * blocked so they need to be processed immediately rather than queued. + * Key presses in overlay mode and the command prompt are a special + * case. The queue might be blocked so they need to be processed + * immediately rather than queued. */ - if ((~c->flags & CLIENT_READONLY) && c->overlay_key != NULL) { - switch (c->overlay_key(c, event)) { - case 0: - return (0); - case 1: - server_client_clear_overlay(c); - return (0); + if (~c->flags & CLIENT_READONLY) { + status_message_clear(c); + if (c->prompt_string != NULL) { + if (status_prompt_key(c, event->key) == 0) + return (0); + } + if (c->overlay_key != NULL) { + switch (c->overlay_key(c, event)) { + case 0: + return (0); + case 1: + server_client_clear_overlay(c); + return (0); + } } + server_client_clear_overlay(c); } /* @@ -1243,6 +1243,8 @@ server_client_loop(void) struct client *c; struct window *w; struct window_pane *wp; + struct winlink *wl; + struct session *s; int focus; TAILQ_FOREACH(c, &clients, entry) { @@ -1259,11 +1261,17 @@ server_client_loop(void) */ focus = options_get_number(global_options, "focus-events"); RB_FOREACH(w, windows, &windows) { + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + s = wl->session; + if (s->attached != 0 && s->curw == wl) + break; + } TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { if (focus) server_client_check_focus(wp); - server_client_check_resize(wp); + if (wl != NULL) + server_client_check_resize(wp); } wp->flags &= ~PANE_REDRAW; } @@ -1525,7 +1533,9 @@ server_client_click_timer(__unused int fd, __unused short events, void *data) static void server_client_check_exit(struct client *c) { - if (!(c->flags & CLIENT_EXIT)) + if (~c->flags & CLIENT_EXIT) + return; + if (c->flags & CLIENT_EXITED) return; if (EVBUFFER_LENGTH(c->stdin_data) != 0) @@ -1538,7 +1548,7 @@ server_client_check_exit(struct client *c) if (c->flags & CLIENT_ATTACHED) notify_client("client-detached", c); proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); - c->flags &= ~CLIENT_EXIT; + c->flags |= CLIENT_EXITED; } /* Redraw timer callback. */ @@ -1943,26 +1953,29 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) close(c->fd); c->fd = -1; - - return; - } - - if (c->fd == -1) - return; - if (tty_init(&c->tty, c, c->fd, c->term) != 0) { - close(c->fd); - c->fd = -1; - return; + } else if (c->fd != -1) { + if (tty_init(&c->tty, c, c->fd, c->term) != 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; + } } - 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); - if (!(c->flags & CLIENT_CONTROL)) - c->flags |= CLIENT_TERMINAL; + /* + * If this is the first client that has finished identifying, load + * configuration files. + */ + if ((~c->flags & CLIENT_EXIT) && + !cfg_finished && + c == TAILQ_FIRST(&clients) && + TAILQ_NEXT(c, entry) == NULL) + start_cfg(); } /* Handle shell message. */ diff --git a/server-fn.c b/server-fn.c index 213d72ea..cfafef21 100644 --- a/server-fn.c +++ b/server-fn.c @@ -308,7 +308,7 @@ server_destroy_pane(struct window_pane *wp, int notify) wp->fd = -1; } - if (options_get_number(w->options, "remain-on-exit")) { + if (options_get_number(wp->options, "remain-on-exit")) { if (~wp->flags & PANE_STATUSREADY) return; @@ -43,7 +43,7 @@ struct clients clients; struct tmuxproc *server_proc; -static int server_fd; +static int server_fd = -1; static int server_exit; static struct event server_ev_accept; @@ -209,9 +209,7 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd, c->flags |= CLIENT_EXIT; } - start_cfg(); server_add_accept(0); - proc_loop(server_proc, server_loop); job_kill_all(); @@ -363,6 +361,9 @@ server_add_accept(int timeout) { struct timeval tv = { timeout, 0 }; + if (server_fd == -1) + return; + if (event_initialized(&server_ev_accept)) event_del(&server_ev_accept); @@ -170,10 +170,8 @@ spawn_window(struct spawn_context *sc, char **cause) /* Spawn the pane. */ wp = spawn_pane(sc, cause); if (wp == NULL) { - if (~sc->flags & SPAWN_RESPAWN) { - window_destroy(w); + if (~sc->flags & SPAWN_RESPAWN) winlink_remove(&s->windows, sc->wl); - } return (NULL); } @@ -30,8 +30,9 @@ /* Default style. */ static struct style style_default = { - { 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } }, + { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 }, + 8, STYLE_ALIGN_DEFAULT, STYLE_LIST_OFF, @@ -127,6 +128,10 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->align = STYLE_ALIGN_RIGHT; else goto error; + } else if (end > 5 && strncasecmp(tmp, "fill=", 5) == 0) { + if ((value = colour_fromstring(tmp + 5)) == -1) + goto error; + sy->fill = value; } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) { if ((value = colour_fromstring(tmp + 3)) == -1) goto error; @@ -213,6 +218,11 @@ style_tostring(struct style *sy) tmp); comma = ","; } + if (sy->fill != 8) { + off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma, + colour_tostring(sy->fill)); + comma = ","; + } if (gc->fg != 8) { off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma, colour_tostring(gc->fg)); @@ -290,6 +300,8 @@ style_equal(struct style *sy1, struct style *sy2) return (0); if ((gc1->attr & STYLE_ATTR_MASK) != (gc2->attr & STYLE_ATTR_MASK)) return (0); + if (sy1->fill != sy2->fill) + return (0); if (sy1->align != sy2->align) return (0); return (1); @@ -461,7 +461,7 @@ Will execute .Ic if-shell , the shell command .Xr true 1 , -.Ic new-window +.Ic split-window and .Ic kill-session in that order. @@ -475,7 +475,7 @@ commands and their arguments. This section describes the syntax of commands parsed by .Nm , for example in a configuration file or at the command prompt. -Note the when commands are entered into the shell, they are parsed by the shell +Note that when commands are entered into the shell, they are parsed by the shell - see for example .Xr ksh 1 or @@ -520,7 +520,11 @@ the given four or eight digit hexadecimal number. When preceded (escaped) by a \e, the following characters are replaced: \ee by the escape character; \er by a carriage return; \en by a newline; and \et by a tab. -.Pp +.It +\eooo is replaced by a character of the octal value ooo. +Three octal digits are required, for example \e001. +The largest valid character is \e377. +.It Any other characters preceded by \e are replaced by themselves (that is, the \e is removed) and are not treated as having any special meaning - so for example \e; will not mark a command sequence and \e$ will not expand an environment @@ -610,7 +614,7 @@ Most commands accept the optional .Fl s ) argument with one of .Ar target-client , -.Ar target-session +.Ar target-session , .Ar target-window , or .Ar target-pane . @@ -777,7 +781,7 @@ may consist entirely of the token .Ql {mouse} (alternative form .Ql = ) -to specify the most recent mouse event +to specify the session, window or pane where the most recent mouse event occurred (see the .Sx MOUSE SUPPORT section) @@ -877,7 +881,7 @@ refresh-client -t/dev/ttyp2 rename-session -tfirst newname -set-window-option -t:0 monitor-activity on +set-option -wt:0 monitor-activity on new-window ; split-window -d @@ -919,7 +923,7 @@ section. The following commands are available to manage clients and sessions: .Bl -tag -width Ds .It Xo Ic attach-session -.Op Fl dEr +.Op Fl dErx .Op Fl c Ar working-directory .Op Fl t Ar target-session .Xc @@ -932,6 +936,10 @@ If used from inside, switch the current client. If .Fl d is specified, any other clients attached to the session are detached. +If +.Fl x +is given, send SIGHUP to the parent process of the client as well as +detaching the client, typically causing it to exit. .Fl r signifies the client is read-only (only keys bound to the .Ic detach-client @@ -1049,7 +1057,7 @@ command. Lock all clients attached to .Ar target-session . .It Xo Ic new-session -.Op Fl AdDEP +.Op Fl AdDEPX .Op Fl c Ar start-directory .Op Fl F Ar format .Op Fl n Ar window-name @@ -1106,6 +1114,12 @@ already exists; in this case, behaves like .Fl d to +.Ic attach-session , +and +.Fl X +behaves like +.Fl x +to .Ic attach-session . .Pp If @@ -1153,7 +1167,8 @@ is used, the option will not be applied. .It Xo Ic refresh-client .Op Fl cDlLRSU -.Op Fl C Ar width,height +.Op Fl C Ar XxY +.Op Fl F Ar flags .Op Fl t Ar target-client .Op Ar adjustment .Xc @@ -1196,7 +1211,13 @@ window, changing the current window in the attached session will reset it. .Pp .Fl C -sets the width and height of a control client. +sets the width and height of a control client and +.Fl F +sets a comma-separated list of flags. +Currently the only flag available is +.Ql no-output +to disable receiving pane output. +.Pp .Fl l requests the clipboard from the client using the .Xr xterm 1 @@ -1242,7 +1263,7 @@ and .Fl T show debugging information about jobs and terminals. .It Xo Ic source-file -.Op Fl nq +.Op Fl nqv .Ar path .Ar ... .Xc @@ -1260,6 +1281,8 @@ does not exist. With .Fl n , the file is parsed but no commands are executed. +.Fl v +shows the parsed commands and line numbers if possible. .It Ic start-server .D1 (alias: Ic start ) Start the @@ -1343,6 +1366,9 @@ It is also entered when a command that produces output, such as .Ic list-keys , is executed from a key binding. .Pp +In copy mode an indicator is displayed in the top-right corner of the pane with +the current position and the number of lines in the history. +.Pp Commands are sent to copy mode using the .Fl X flag to the @@ -1396,6 +1422,7 @@ The following commands are supported in copy mode: .It Li "jump-to-backward <to>" Ta "T" Ta "" .It Li "jump-to-forward <to>" Ta "t" Ta "" .It Li "middle-line" Ta "M" Ta "M-r" +.It Li "next-matching-bracket" Ta "%" Ta "M-C-f" .It Li "next-paragraph" Ta "}" Ta "M-}" .It Li "next-space" Ta "W" Ta "" .It Li "next-space-end" Ta "E" Ta "" @@ -1405,6 +1432,7 @@ The following commands are supported in copy mode: .It Li "page-down" Ta "C-f" Ta "PageDown" .It Li "page-down-and-cancel" Ta "" Ta "" .It Li "page-up" Ta "C-b" Ta "PageUp" +.It Li "previous-matching-bracket" Ta "" Ta "M-C-b" .It Li "previous-paragraph" Ta "{" Ta "M-{" .It Li "previous-space" Ta "B" Ta "" .It Li "previous-word" Ta "b" Ta "M-b" @@ -1817,14 +1845,16 @@ With .Fl b , other commands are not blocked from running until the indicator is closed. .It Xo Ic find-window -.Op Fl CNTZ +.Op Fl rCNTZ .Op Fl t Ar target-pane .Ar match-string .Xc .D1 (alias: Ic findw ) -Search for the +Search for a .Xr fnmatch 3 -pattern +pattern or, with +.Fl r , +regular expression .Ar match-string in window names, titles, and visible content (but not history). The flags control matching behavior: @@ -2312,8 +2342,7 @@ applies the last set layout if possible (undoes the most recent layout change). .Fl E spreads the current pane and any panes next to it out evenly. .It Xo Ic select-pane -.Op Fl DdegLlMmRU -.Op Fl P Ar style +.Op Fl DdeLlMmRU .Op Fl T Ar title .Op Fl t Ar target-pane .Xc @@ -2321,9 +2350,7 @@ spreads the current pane and any panes next to it out evenly. Make pane .Ar target-pane the active pane in window -.Ar target-window , -or set its style (with -.Fl P ) . +.Ar target-window . If one of .Fl D , .Fl L , @@ -2340,6 +2367,8 @@ command. enables or .Fl d disables input to the pane. +.Fl T +sets the pane title. .Pp .Fl m and @@ -2354,25 +2383,6 @@ to .Ic swap-pane and .Ic swap-window . -.Pp -Each pane has a style: by default the -.Ic window-style -and -.Ic window-active-style -options are used, -.Ic select-pane -.Fl P -sets the style for a single pane. -For example, to set the pane 1 background to red: -.Bd -literal -offset indent -select-pane -t:.1 -P 'bg=red' -.Ed -.Pp -.Fl g -shows the current pane style. -.Pp -.Fl T -sets the pane title. .It Xo Ic select-window .Op Fl lnpT .Op Fl t Ar target-window @@ -2629,7 +2639,7 @@ With only .Ar key-table . .It Xo Ic send-keys -.Op Fl lMRX +.Op Fl HlMRX .Op Fl N Ar repeat-count .Op Fl t Ar target-pane .Ar key Ar ... @@ -2644,10 +2654,16 @@ or .Ql NPage ) to send; if the string is not recognised as a key, it is sent as a series of characters. +All arguments are sent sequentially from first to last. +.Pp The .Fl l -flag disables key name lookup and sends the keys literally. -All arguments are sent sequentially from first to last. +flag disables key name lookup and processes the keys as literal UTF-8 +characters. +The +.Fl H +flag expects each key to be a hexadecimal number for an ASCII character. +.Pp The .Fl R flag causes the terminal state to be reset. @@ -2691,16 +2707,17 @@ is present, all key bindings are removed. The appearance and behaviour of .Nm may be modified by changing the value of various options. -There are three types of option: +There are four types of option: .Em server options , .Em session options +.Em window options and -.Em window options . +.Em pane options . .Pp The .Nm server has a set of global options which do not apply to any particular -window or session. +window or session or pane. These are altered with the .Ic set-option .Fl s @@ -2722,16 +2739,29 @@ The available server and session options are listed under the .Ic set-option command. .Pp -Similarly, a set of window options is attached to each window, and there is -a set of global window options from which any unset options are inherited. -Window options are altered with the -.Ic set-window-option -command and can be listed with the -.Ic show-window-options -command. -All window options are documented with the -.Ic set-window-option -command. +Similarly, a set of window options is attached to each window and a set of pane +options to each pane. +Pane options inherit from window options. +This means any pane option may be set as a window option to apply the option to +all panes in the window without the option set, for example these commands will +set the background colour to red for all panes except pane 0: +.Bd -literal -offset indent +set -w window-style bg=red +set -pt:.0 window-style bg=blue +.Ed +.Pp +There is also a set of global window options from which any unset window or +pane options are inherited. +Window and pane options are altered with +.Ic set-option +.Fl w +and +.Fl p +commands and displayed with +.Ic show-option +.Fl w +and +.Fl p . .Pp .Nm also supports user options which are prefixed with a @@ -2749,26 +2779,27 @@ abc123 Commands which set options are as follows: .Bl -tag -width Ds .It Xo Ic set-option -.Op Fl aFgoqsuw -.Op Fl t Ar target-session | Ar target-window +.Op Fl aFgopqsuw +.Op Fl t Ar target-pane .Ar option Ar value .Xc .D1 (alias: Ic set ) -Set a window option with -.Fl w -(equivalent to the -.Ic set-window-option -command), +Set a pane option with +.Fl p , +a window option with +.Fl w , a server option with .Fl s , otherwise a session option. If the option is not a user option, .Fl w -and +or .Fl s -are unnecessary - +may be unnecessary - .Nm -will infer the type from the option name. +will infer the type from the option name, assuming +.Fl w +for pane options. If .Fl g is given, the global session or window option is set. @@ -2813,13 +2844,49 @@ blue foreground. Without .Fl a , the result would be the default background and a blue foreground. -.Pp -Available window options are listed under -.Ic set-window-option . -.Pp +.It Xo Ic show-options +.Op Fl AgHpqsvw +.Op Fl t Ar target-pane +.Op Ar option +.Xc +.D1 (alias: Ic show ) +Show the pane options (or a single option if +.Ar option +is provided) with +.Fl p , +the window options with +.Fl w , +the server options with +.Fl s , +otherwise the session options. +If the option is not a user option, +.Fl w +or +.Fl s +may be unnecessary - +.Nm +will infer the type from the option name, assuming +.Fl w +for pane options. +Global session or window options are listed if +.Fl g +is used. +.Fl v +shows only the option value, not the name. +If +.Fl q +is set, no error will be returned if +.Ar option +is unset. +.Fl H +includes hooks (omitted by default). +.Fl A +includes options inherited from a parent set of options, such options are +marked with an asterisk. .Ar value depends on the option and may be a number, a string, or a flag (on, off, or omitted to toggle). +.El .Pp Available server options are: .Bl -tag -width Ds @@ -2960,6 +3027,18 @@ for all terminal types matching The terminal entry value is passed through .Xr strunvis 3 before interpretation. +.It Ic user-keys[] Ar key +Set list of user-defined key escape sequences. +Each item is associated with a key named +.Ql User0 , +.Ql User1 , +and so on. +.Pp +For example: +.Bd -literal -offset indent +set -s user-keys[0] "\ee[5;30012~" +bind User0 resize-pane -L 3 +.Ed .El .Pp Available session options are: @@ -3166,7 +3245,7 @@ the terminal appears to be .Xr xterm 1 . This option is off by default. .It Ic set-titles-string Ar string -String used to set the window title if +String used to set the client terminal title if .Ic set-titles is on. Formats are expanded, see the @@ -3300,18 +3379,6 @@ removed from the session environment (as if was given to the .Ic set-environment command). -.It Ic user-keys[] Ar key -Set list of user-defined key escape sequences. -Each item is associated with a key named -.Ql User0 , -.Ql User1 , -and so on. -.Pp -For example: -.Bd -literal -offset indent -set -s user-keys[0] "\ee[5;30012~" -bind User0 resize-pane -L 3 -.Ed .It Xo Ic visual-activity .Op Ic on | off | both .Xc @@ -3346,26 +3413,8 @@ copy mode. The default is .Ql \ -_@ . .El -.It Xo Ic set-window-option -.Op Fl aFgoqu -.Op Fl t Ar target-window -.Ar option Ar value -.Xc -.D1 (alias: Ic setw ) -Set a window option. -The -.Fl a , -.Fl F , -.Fl g , -.Fl o , -.Fl q -and -.Fl u -flags work similarly to the -.Ic set-option -command. .Pp -Supported window options are: +Available window options are: .Pp .Bl -tag -width Ds -compact .It Xo Ic aggressive-resize @@ -3384,29 +3433,6 @@ session; this option is good for full-screen programs which support .Dv SIGWINCH and poor for interactive programs such as shells. .Pp -.It Xo Ic allow-rename -.Op Ic on | off -.Xc -Allow programs to change the window name using a terminal escape -sequence (\eek...\ee\e\e). -The default is off. -.Pp -.It Xo Ic alternate-screen -.Op Ic on | off -.Xc -This option configures whether programs running inside -.Nm -may use the terminal alternate screen feature, which allows the -.Em smcup -and -.Em rmcup -.Xr terminfo 5 -capabilities. -The alternate screen feature preserves the contents of the window when an -interactive application starts and restores it on exit, so that any output -visible before the application starts reappears unchanged after it exits. -The default is on. -.Pp .It Xo Ic automatic-rename .Op Ic on | off .Xc @@ -3425,7 +3451,7 @@ or later with or with a terminal escape sequence. It may be switched off globally with: .Bd -literal -offset indent -set-window-option -g automatic-rename off +set-option -wg automatic-rename off .Ed .Pp .It Ic automatic-rename-format Ar format @@ -3542,29 +3568,12 @@ see the section. Attributes are ignored. .Pp -.It Xo Ic remain-on-exit -.Op Ic on | off -.Xc -A window with this flag set is not destroyed when the program running in it -exits. -The window may be reactivated with the -.Ic respawn-window -command. -.Pp .It Xo Ic synchronize-panes .Op Ic on | off .Xc Duplicate input to any pane to all other panes in the same window (only for panes that are not in any special mode). .Pp -.It Ic window-active-style Ar style -Set the style for the window's active pane. -For how to specify -.Ar style , -see the -.Sx STYLES -section. -.Pp .It Ic window-status-activity-style Ar style Set status line style for windows with an activity alert. For how to specify @@ -3644,14 +3653,6 @@ command and the .Ic aggressive-resize option. .Pp -.It Ic window-style Ar style -Set the default window style. -For how to specify -.Ar style , -see the -.Sx STYLES -section. -.Pp .It Xo Ic wrap-search .Op Ic on | off .Xc @@ -3668,54 +3669,54 @@ will generate function key sequences; these have a number included to indicate modifiers such as Shift, Alt or Ctrl. .El -.It Xo Ic show-options -.Op Fl gHqsvw -.Op Fl t Ar target-session | Ar target-window -.Op Ar option +.Pp +Available pane options are: +.Pp +.Bl -tag -width Ds -compact +.It Xo Ic allow-rename +.Op Ic on | off .Xc -.D1 (alias: Ic show ) -Show the window options (or a single window option if given) with -.Fl w -(equivalent to -.Ic show-window-options ) , -the server options with -.Fl s , -otherwise the session options for -.Ar target session . -If -.Ar option -is given and is not a user option, -.Fl w +Allow programs in the pane to change the window name using a terminal escape +sequence (\eek...\ee\e\e). +.Pp +.It Xo Ic alternate-screen +.Op Ic on | off +.Xc +This option configures whether programs running inside the pane may use the +terminal alternate screen feature, which allows the +.Em smcup and -.Fl s -are unnecessary - -.Nm -will infer the type from the option name. -Global session or window options are listed if -.Fl g -is used. -.Fl v -shows only the option value, not the name. -If -.Fl q -is set, no error will be returned if -.Ar option -is unset. -.Fl H -includes hooks (omitted by default). -.It Xo Ic show-window-options -.Op Fl gv -.Op Fl t Ar target-window -.Op Ar option +.Em rmcup +.Xr terminfo 5 +capabilities. +The alternate screen feature preserves the contents of the window when an +interactive application starts and restores it on exit, so that any output +visible before the application starts reappears unchanged after it exits. +.Pp +.It Xo Ic remain-on-exit +.Op Ic on | off .Xc -.D1 (alias: Ic showw ) -List the window options or a single option for -.Ar target-window , -or the global window options if -.Fl g -is used. -.Fl v -shows only the option value, not the name. +A pane with this flag set is not destroyed when the program running in it +exits. +The pane may be reactivated with the +.Ic respawn-pane +command. +.Pp +.It Ic window-active-style Ar style +Set the pane style when it is the active pane. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-style Ar style +Set the pane style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. .El .Sh HOOKS .Nm @@ -3924,7 +3925,7 @@ flag with a .Ar format argument. This is a string which controls the output format of the command. -Replacement variables are enclosed in +Format variables are enclosed in .Ql #{ and .Ql } , @@ -3983,7 +3984,7 @@ For example: #{?pane_in_mode,#[fg=white#,bg=red],#[fg=red#,bg=white]}#W . .Ed .Pp -Comparisons may be expressed by prefixing two comma-separated +String comparisons may be expressed by prefixing two comma-separated alternatives by .Ql == , .Ql != , @@ -4001,25 +4002,45 @@ if running on .Ql myhost , otherwise by .Ql 0 . -An -.Ql m -specifies an -.Xr fnmatch 3 -comparison where the first argument is the pattern and the second the string to -compare, for example -.Ql #{m:*foo*,#{host}} . .Ql || and .Ql && evaluate to true if either or both of two comma-separated alternatives are true, for example .Ql #{||:#{pane_in_mode},#{alternate_on}} . +.Pp +An +.Ql m +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: +.Ql r +means the pattern is a regular expression instead of the default +.Xr fnmatch 3 +pattern, and +.Ql i +means to ignore case. +For example: +.Ql #{m:*foo*,#{host}} +or +.Ql #{m/ri:^A,MYVAR} . A .Ql C performs a search for an .Xr fnmatch 3 -pattern in the pane content and evaluates to zero if not found, or a line -number if found. +pattern or regular expression in the pane content and evaluates to zero if not +found, or a line number if found. +Like +.Ql m , +an +.Ql r +flag means search for a regular expression and +.Ql i +ignores case. +For example: +.Ql #{C/r:^Start} .Pp A limit may be placed on the length of the resultant string by prefixing it by an @@ -4065,7 +4086,7 @@ will expand the format twice, for example .Ql #{E:status-left} is the result of expanding the content of the .Ic status-left -option rather than the content itself. +option rather than the option itself. .Ql T: is like .Ql E: @@ -4092,8 +4113,16 @@ will substitute with .Ql bar throughout. -.Pp -In addition, the first line of a shell command's output may be inserted using +The first argument may be an extended regular expression and a final argument may be +.Ql i +to ignore case, for example +.Ql s/a(.)/\e1x/i: +would change +.Ql abABab +into +.Ql bxBxbx . +.Pp +In addition, the last line of a shell command's output may be inserted using .Ql #() . For example, .Ql #(uptime) @@ -4112,10 +4141,18 @@ global environment set (see the .Sx GLOBAL AND SESSION ENVIRONMENT section). .Pp +An +.Ql l +specifies that a string should be interpreted literally and not expanded. +For example +.Ql #{l:#{?pane_in_mode,yes,no}} +will be replaced by +.Ql #{?pane_in_mode,yes,no} . +.Pp The following variables are available, where appropriate: .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" .It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" -.It Li "alternate_on" Ta "" Ta "If pane is in alternate screen" +.It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen" .It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" .It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" .It Li "buffer_created" Ta "" Ta "Time buffer created" @@ -4166,11 +4203,14 @@ The following variables are available, where appropriate: .It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag" .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" .It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" +.It Li "mouse_line" Ta "" Ta "Line under mouse, if any" +.It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag" .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" +.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" +.It Li "mouse_word" Ta "" Ta "Word under mouse, if any" .It Li "mouse_x" Ta "" Ta "Mouse X position, if any" .It Li "mouse_y" Ta "" Ta "Mouse Y position, if any" -.It Li "mouse_word" Ta "" Ta "Word under mouse, if any" -.It Li "mouse_line" Ta "" Ta "Line under mouse, if any" +.It Li "origin_flag" Ta "" Ta "Pane origin flag" .It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window" .It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window" @@ -4184,19 +4224,19 @@ The following variables are available, where appropriate: .It Li "pane_format" Ta "" Ta "1 if format is for a pane (not assuming the current)" .It Li "pane_height" Ta "" Ta "Height of pane" .It Li "pane_id" Ta "#D" Ta "Unique pane ID" -.It Li "pane_in_mode" Ta "" Ta "If pane is in a mode" +.It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode" .It Li "pane_index" Ta "#P" Ta "Index of pane" -.It Li "pane_input_off" Ta "" Ta "If input to pane is disabled" +.It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled" .It Li "pane_left" Ta "" Ta "Left of pane" .It Li "pane_marked" Ta "" Ta "1 if this is the marked pane" .It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set" -.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any." +.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" .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_start_command" Ta "" Ta "Command pane started with" -.It Li "pane_synchronized" Ta "" Ta "If pane is synchronized" +.It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" .It Li "pane_title" Ta "#T" Ta "Title of pane" .It Li "pane_top" Ta "" Ta "Top of pane" @@ -4325,10 +4365,12 @@ to unset. .Ic align=right .Xc Align text to the left, centre or right of the available space if appropriate. +.It Ic fill=colour +Fill the available space with a background colour if appropriate. .It Xo Ic list=on , .Ic list=focus , .Ic list=left-marker , -.Ic list=right=marker , +.Ic list=right-marker , .Ic nolist .Xc Mark the position of the various window list components in the @@ -4560,7 +4602,7 @@ session option. Commands related to the status line are as follows: .Bl -tag -width Ds .It Xo Ic command-prompt -.Op Fl 1i +.Op Fl 1Ni .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client @@ -4610,6 +4652,8 @@ but any quotation marks are escaped. .Fl 1 makes the prompt only accept one key press, in this case the resulting input is a single character. +.Fl N +makes the prompt only accept numeric key presses. .Fl i executes the command every time the prompt input changes instead of when the user exits the command prompt. @@ -4621,7 +4665,7 @@ 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 "Delete current word" Ta "" Ta "C-w" +.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" .It Li "Execute command" Ta "Enter" Ta "Enter" @@ -4739,8 +4783,7 @@ section; information is taken from .Ar target-pane if .Fl t -is given, otherwise the active pane for the session attached to -.Ar target-client . +is given, otherwise the active pane. .Pp .Fl v prints verbose logging as the format is parsed and @@ -5032,11 +5075,33 @@ $ printf '\e033]12;red\e033\e\e' .Ed .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 underline. -The single parameter is one of: 0 for no underline, 1 for normal -underline, 2 for double underline, 3 for curly underline, 4 for dotted -underline and 5 for dashed underline. +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 @@ -321,11 +321,11 @@ main(int argc, char **argv) global_s_options = options_create(NULL); global_w_options = options_create(NULL); for (oe = options_table; oe->name != NULL; oe++) { - if (oe->scope == OPTIONS_TABLE_SERVER) + if (oe->scope & OPTIONS_TABLE_SERVER) options_default(global_options, oe); - if (oe->scope == OPTIONS_TABLE_SESSION) + if (oe->scope & OPTIONS_TABLE_SESSION) options_default(global_s_options, oe); - if (oe->scope == OPTIONS_TABLE_WINDOW) + if (oe->scope & OPTIONS_TABLE_WINDOW) options_default(global_w_options, oe); } @@ -113,9 +113,10 @@ struct winlink; #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) +#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM|KEYC_LITERAL) #define KEYC_MASK_KEY (~KEYC_MASK_MOD) /* Is this a mouse key? */ @@ -429,6 +430,7 @@ enum tty_code_code { TTYC_SETAF, TTYC_SETRGBB, TTYC_SETRGBF, + TTYC_SETULC, TTYC_SGR0, TTYC_SITM, TTYC_SMACS, @@ -597,12 +599,13 @@ enum utf8_state { /* Grid cell data. */ struct grid_cell { - u_char flags; + struct utf8_data data; /* 21 bytes */ u_short attr; + u_char flags; int fg; int bg; - struct utf8_data data; -}; + int us; +} __packed; struct grid_cell_entry { u_char flags; union { @@ -682,6 +685,7 @@ TAILQ_HEAD(style_ranges, style_range); struct style { struct grid_cell gc; + int fill; enum style_align align; enum style_list list; @@ -812,6 +816,7 @@ struct window_pane { u_int active_point; struct window *window; + struct options *options; struct layout_cell *layout_cell; struct layout_cell *saved_layout_cell; @@ -838,6 +843,7 @@ struct window_pane { #define PANE_STATUSREADY 0x200 #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 +#define PANE_STYLECHANGED 0x1000 int argc; char **argv; @@ -856,7 +862,8 @@ struct window_pane { struct input_ctx *ictx; - struct style style; + struct style cached_style; + struct style cached_active_style; int *palette; int pipe_fd; @@ -916,7 +923,6 @@ struct window { #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 #define WINDOW_ZOOMED 0x8 -#define WINDOW_STYLECHANGED 0x10 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) int alerts_queued; @@ -924,9 +930,6 @@ struct window { struct options *options; - struct style style; - struct style active_style; - u_int references; TAILQ_HEAD(, winlink) winlinks; @@ -958,6 +961,11 @@ TAILQ_HEAD(winlink_stack, winlink); #define WINDOW_SIZE_SMALLEST 1 #define WINDOW_SIZE_MANUAL 2 +/* Pane border status option. */ +#define PANE_STATUS_OFF 0 +#define PANE_STATUS_TOP 1 +#define PANE_STATUS_BOTTOM 2 + /* Layout direction. */ enum layout_type { LAYOUT_LEFTRIGHT, @@ -1326,6 +1334,7 @@ struct cmd_parse_input { #define CMD_PARSE_QUIET 0x1 #define CMD_PARSE_PARSEONLY 0x2 #define CMD_PARSE_NOALIAS 0x4 +#define CMD_PARSE_VERBOSE 0x8 const char *file; u_int line; @@ -1491,7 +1500,7 @@ struct client { #define CLIENT_REPEAT 0x20 #define CLIENT_SUSPENDED 0x40 #define CLIENT_ATTACHED 0x80 -/* 0x100 unused */ +#define CLIENT_EXITED 0x100 #define CLIENT_DEAD 0x200 #define CLIENT_REDRAWBORDERS 0x400 #define CLIENT_READONLY 0x800 @@ -1509,6 +1518,7 @@ struct client { #define CLIENT_STATUSOFF 0x800000 #define CLIENT_REDRAWSTATUSALWAYS 0x1000000 #define CLIENT_REDRAWOVERLAY 0x2000000 +#define CLIENT_CONTROL_NOOUTPUT 0x4000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -1606,12 +1616,11 @@ enum options_table_type { OPTIONS_TABLE_COMMAND }; -enum options_table_scope { - OPTIONS_TABLE_NONE, - OPTIONS_TABLE_SERVER, - OPTIONS_TABLE_SESSION, - OPTIONS_TABLE_WINDOW -}; +#define OPTIONS_TABLE_NONE 0 +#define OPTIONS_TABLE_SERVER 0x1 +#define OPTIONS_TABLE_SESSION 0x2 +#define OPTIONS_TABLE_WINDOW 0x4 +#define OPTIONS_TABLE_PANE 0x8 #define OPTIONS_TABLE_IS_ARRAY 0x1 #define OPTIONS_TABLE_IS_HOOK 0x2 @@ -1619,7 +1628,7 @@ enum options_table_scope { struct options_table_entry { const char *name; enum options_table_type type; - enum options_table_scope scope; + int scope; int flags; u_int minimum; @@ -1781,6 +1790,7 @@ void notify_pane(const char *, struct window_pane *); /* options.c */ struct options *options_create(struct options *); void options_free(struct options *); +void options_set_parent(struct options *, struct options *); struct options_entry *options_first(struct options *); struct options_entry *options_next(struct options_entry *); struct options_entry *options_empty(struct options *, @@ -1820,7 +1830,10 @@ struct options_entry *options_set_number(struct options *, const char *, long long); struct options_entry *options_set_style(struct options *, const char *, int, const char *); -enum options_table_scope options_scope_from_flags(struct args *, int, +int options_scope_from_name(struct args *, int, + const char *, struct cmd_find_state *, struct options **, + char **); +int options_scope_from_flags(struct args *, int, struct cmd_find_state *, struct options **, char **); /* options-table.c */ @@ -2011,7 +2024,7 @@ extern const struct cmd_entry *cmd_table[]; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int, - const char *, int); + int, const char *, int); /* cmd-parse.c */ void cmd_parse_empty(struct cmd_parse_input *); @@ -2038,6 +2051,7 @@ void cmdq_insert_after(struct cmdq_item *, struct cmdq_item *); void 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 *); @@ -2187,7 +2201,8 @@ int colour_join_rgb(u_char, u_char, u_char); void colour_split_rgb(int, u_char *, u_char *, u_char *); const char *colour_tostring(int); int colour_fromstring(const char *s); -u_char colour_256to16(u_char); +int colour_256toRGB(int); +int colour_256to16(int); /* attributes.c */ const char *attributes_tostring(int); @@ -2352,7 +2367,6 @@ struct window *window_find_by_id_str(const char *); struct window *window_find_by_id(u_int); void window_update_activity(struct window *); struct window *window_create(u_int, u_int); -void window_destroy(struct window *); void window_pane_set_event(struct window_pane *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); @@ -2397,7 +2411,8 @@ void window_pane_key(struct window_pane *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); -u_int window_pane_search(struct window_pane *, const char *); +u_int window_pane_search(struct window_pane *, const char *, int, + int); const char *window_printable_flags(struct winlink *); struct window_pane *window_pane_find_up(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *); @@ -2425,7 +2440,7 @@ void layout_set_size(struct layout_cell *, u_int, u_int, u_int, u_int); void layout_make_leaf(struct layout_cell *, struct window_pane *); void layout_make_node(struct layout_cell *, enum layout_type); -void layout_fix_offsets(struct layout_cell *); +void layout_fix_offsets(struct window *); void layout_fix_panes(struct window *); void layout_resize_adjust(struct window *, struct layout_cell *, enum layout_type, int); @@ -2634,4 +2649,7 @@ int style_is_default(struct style *); struct winlink *spawn_window(struct spawn_context *, char **); struct window_pane *spawn_pane(struct spawn_context *, char **); +/* regsub.c */ +char *regsub(const char *, const char *, const char *, int); + #endif /* TMUX_H */ @@ -249,6 +249,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SETAF] = { TTYCODE_STRING, "setaf" }, [TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" }, [TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" }, + [TTYC_SETULC] = { TTYCODE_STRING, "Setulc" }, [TTYC_SE] = { TTYCODE_STRING, "Se" }, [TTYC_SGR0] = { TTYCODE_STRING, "sgr0" }, [TTYC_SITM] = { TTYCODE_STRING, "sitm" }, @@ -691,7 +692,7 @@ tty_term_describe(struct tty_term *term, enum tty_code_code code) break; case TTYCODE_STRING: strnvis(out, term->codes[code].value.string, sizeof out, - VIS_OCTAL|VIS_TAB|VIS_NL); + VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); xsnprintf(s, sizeof s, "%4u: %s: (string) %s", code, tty_term_codes[code].name, out); @@ -49,8 +49,11 @@ static void tty_check_fg(struct tty *, struct window_pane *, struct grid_cell *); static void tty_check_bg(struct tty *, struct window_pane *, struct grid_cell *); +static void tty_check_us(struct tty *, struct window_pane *, + struct grid_cell *); static void tty_colours_fg(struct tty *, const struct grid_cell *); static void tty_colours_bg(struct tty *, const struct grid_cell *); +static void tty_colours_us(struct tty *, const struct grid_cell *); static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int); @@ -527,6 +530,12 @@ tty_putc(struct tty *tty, u_char ch) { const char *acs; + if ((tty->term->flags & TERM_EARLYWRAP) && + ch >= 0x20 && ch != 0x7f && + tty->cy == tty->sy - 1 && + tty->cx + 1 >= tty->sx) + return; + if (tty->cell.attr & GRID_ATTR_CHARSET) { acs = tty_acs_get(tty, ch); if (acs != NULL) @@ -557,6 +566,11 @@ tty_putc(struct tty *tty, u_char ch) void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { + if ((tty->term->flags & TERM_EARLYWRAP) && + tty->cy == tty->sy - 1 && + tty->cx + len >= tty->sx) + len = tty->sx - tty->cx - 1; + tty_add(tty, buf, len); if (tty->cx + width > tty->sx) { tty->cx = (tty->cx + width) - tty->sx; @@ -1049,17 +1063,17 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, *y = ctx->yoff + py - ctx->oy; *ry = ny; } else if (yoff < ctx->oy && yoff + ny > ctx->oy + ctx->sy) { - /* Both left and right not visible. */ + /* Both top and bottom not visible. */ *j = ctx->oy; *y = 0; *ry = ctx->sy; } else if (yoff < ctx->oy) { - /* Left not visible. */ + /* Top not visible. */ *j = ctx->oy - (ctx->yoff + py); *y = 0; *ry = ny - *j; } else { - /* Right not visible. */ + /* Bottom not visible. */ *j = 0; *y = (ctx->yoff + py) - ctx->oy; *ry = ctx->sy - *y; @@ -1203,7 +1217,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, const struct grid_cell *gcp; struct grid_line *gl; u_int i, j, ux, sx, width; - int flags, cleared = 0; + int flags, cleared = 0, wrapped = 0; char buf[512]; size_t len; u_int cellsize; @@ -1260,8 +1274,10 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, tty_putcode(tty, TTYC_EL1); cleared = 1; } - } else + } else { log_debug("%s: wrapped line %u", __func__, aty); + wrapped = 1; + } memcpy(&last, &grid_default_cell, sizeof last); len = 0; @@ -1276,6 +1292,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, gcp->attr != last.attr || gcp->fg != last.fg || gcp->bg != last.bg || + gcp->us != last.us || ux + width + gcp->data.width > nx || (sizeof buf) - len < gcp->data.size)) { tty_attributes(tty, &last, wp); @@ -1284,13 +1301,15 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, tty_clear_line(tty, wp, aty, atx + ux, width, last.bg); } else { - tty_cursor(tty, atx + ux, aty); + if (!wrapped || atx != 0 || ux != 0) + tty_cursor(tty, atx + ux, aty); tty_putn(tty, buf, len, width); } ux += width; len = 0; width = 0; + wrapped = 0; } if (gcp->flags & GRID_FLAG_SELECTED) @@ -1324,7 +1343,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, log_debug("%s: %zu cleared (end)", __func__, len); tty_clear_line(tty, wp, aty, atx + ux, width, last.bg); } else { - tty_cursor(tty, atx + ux, aty); + if (!wrapped || atx != 0 || ux != 0) + tty_cursor(tty, atx + ux, aty); tty_putn(tty, buf, len, width); } ux += width; @@ -2121,10 +2141,11 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, /* Ignore cell if it is the same as the last one. */ if (wp != NULL && (int)wp->id == tty->last_wp && - ~(wp->window->flags & WINDOW_STYLECHANGED) && + ~(wp->flags & PANE_STYLECHANGED) && gc->attr == tty->last_cell.attr && gc->fg == tty->last_cell.fg && - gc->bg == tty->last_cell.bg) + gc->bg == tty->last_cell.bg && + gc->us == tty->last_cell.us) return; tty->last_wp = (wp != NULL ? (int)wp->id : -1); memcpy(&tty->last_cell, gc, sizeof tty->last_cell); @@ -2152,14 +2173,18 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, /* Fix up the colours if necessary. */ tty_check_fg(tty, wp, &gc2); tty_check_bg(tty, wp, &gc2); + tty_check_us(tty, wp, &gc2); - /* If any bits are being cleared, reset everything. */ - if (tc->attr & ~gc2.attr) + /* + * If any bits are being cleared or the underline colour is now default, + * reset everything. + */ + if ((tc->attr & ~gc2.attr) || (tc->us != gc2.us && gc2.us == 0)) tty_reset(tty); /* * Set the colours. This may call tty_reset() (so it comes next) and - * may add to (NOT remove) the desired attributes by changing new_attr. + * may add to (NOT remove) the desired attributes. */ tty_colours(tty, &gc2); @@ -2212,7 +2237,7 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) int have_ax; /* No changes? Nothing is necessary. */ - if (gc->fg == tc->fg && gc->bg == tc->bg) + if (gc->fg == tc->fg && gc->bg == tc->bg && gc->us == tc->us) return; /* @@ -2260,6 +2285,10 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) */ if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg) tty_colours_bg(tty, gc); + + /* Set the underscore color. */ + if (gc->us != tc->us) + tty_colours_us(tty, gc); } static void @@ -2375,6 +2404,22 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) } static void +tty_check_us(__unused struct tty *tty, struct window_pane *wp, struct grid_cell *gc) +{ + int c; + + /* Perform substitution if this pane has a palette. */ + if (~gc->flags & GRID_FLAG_NOPALETTE) { + if ((c = window_pane_get_palette(wp, gc->us)) != -1) + gc->us = c; + } + + /* Underscore colour is set as RGB so convert a 256 colour to RGB. */ + if (gc->us & COLOUR_FLAG_256) + gc->us = colour_256toRGB (gc->us); +} + +static void tty_colours_fg(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; @@ -2438,6 +2483,31 @@ save_bg: tc->bg = gc->bg; } +static void +tty_colours_us(struct tty *tty, const struct grid_cell *gc) +{ + struct grid_cell *tc = &tty->cell; + u_int c; + u_char r, g, b; + + /* Must be an RGB colour - this should never happen. */ + if (~gc->us & COLOUR_FLAG_RGB) + return; + + /* + * Setulc follows the ncurses(3) one argument "direct colour" + * capability format. Calculate the colour value. + */ + colour_split_rgb(gc->us, &r, &g, &b); + c = (65536 * r) + (256 * g) + b; + + /* Write the colour. */ + tty_putcode1(tty, TTYC_SETULC, c); + + /* Save the new values in the terminal current cell. */ + tc->us = gc->us; +} + static int tty_try_colour(struct tty *tty, int colour, const char *type) { @@ -2503,30 +2573,28 @@ fallback_256: static void tty_default_colours(struct grid_cell *gc, struct window_pane *wp) { - struct window *w = wp->window; - struct options *oo = w->options; - struct style *active, *pane, *window; - int c; - - if (w->flags & WINDOW_STYLECHANGED) { - w->flags &= ~WINDOW_STYLECHANGED; - active = options_get_style(oo, "window-active-style"); - style_copy(&w->active_style, active); - window = options_get_style(oo, "window-style"); - style_copy(&w->style, window); + struct options *oo = wp->options; + struct style *style, *active_style; + int c; + + if (wp->flags & PANE_STYLECHANGED) { + wp->flags &= ~PANE_STYLECHANGED; + + active_style = options_get_style(oo, "window-active-style"); + style = options_get_style(oo, "window-style"); + + style_copy(&wp->cached_active_style, active_style); + style_copy(&wp->cached_style, style); } else { - active = &w->active_style; - window = &w->style; + active_style = &wp->cached_active_style; + style = &wp->cached_style; } - pane = &wp->style; if (gc->fg == 8) { - if (pane->gc.fg != 8) - gc->fg = pane->gc.fg; - else if (wp == w->active && active->gc.fg != 8) - gc->fg = active->gc.fg; + if (wp == wp->window->active && active_style->gc.fg != 8) + gc->fg = active_style->gc.fg; else - gc->fg = window->gc.fg; + gc->fg = style->gc.fg; if (gc->fg != 8) { c = window_pane_get_palette(wp, gc->fg); @@ -2536,12 +2604,10 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) } if (gc->bg == 8) { - if (pane->gc.bg != 8) - gc->bg = pane->gc.bg; - else if (wp == w->active && active->gc.bg != 8) - gc->bg = active->gc.bg; + if (wp == wp->window->active && active_style->gc.bg != 8) + gc->bg = active_style->gc.bg; else - gc->bg = window->gc.bg; + gc->bg = style->gc.bg; if (gc->bg != 8) { c = window_pane_get_palette(wp, gc->bg); diff --git a/window-buffer.c b/window-buffer.c index d65916b5..224dfedb 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -245,7 +245,7 @@ window_buffer_draw(__unused void *modedata, void *itemdata, at = 0; while (end != pdata + psize && *end != '\n') { if ((sizeof line) - at > 5) { - cp = vis(line + at, *end, VIS_TAB|VIS_OCTAL, 0); + cp = vis(line + at, *end, VIS_OCTAL|VIS_TAB, 0); at = cp - line; } end++; diff --git a/window-copy.c b/window-copy.c index d81073bf..d868631c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -577,6 +577,7 @@ 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; struct screen_write_ctx ctx; + int search; screen_resize(s, sx, sy, 1); if (data->backing != &wp->base) @@ -589,13 +590,15 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) if (data->oy > screen_hsize(data->backing)) data->oy = screen_hsize(data->backing); + 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); screen_write_stop(&ctx); - if (data->searchmark != NULL) + if (search) window_copy_search_marks(wme, NULL); data->searchx = data->cx; data->searchy = data->cy; @@ -2700,7 +2703,7 @@ window_copy_append_selection(struct window_mode_entry *wme) struct window_pane *wp = wme->wp; char *buf; struct paste_buffer *pb; - const char *bufdata, *bufname; + const char *bufdata, *bufname = NULL; size_t len, bufsize; struct screen_write_ctx ctx; @@ -3027,8 +3030,8 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) window_copy_other_end(wme); - data->cx = data->lastcx; if (scroll_only || data->cy == 0) { + data->cx = data->lastcx; window_copy_scroll_down(wme, 1); if (scroll_only) { if (data->cy == screen_size_y(s) - 1) @@ -3037,7 +3040,7 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) window_copy_redraw_lines(wme, data->cy, 2); } } else { - window_copy_update_cursor(wme, data->cx, data->cy - 1); + window_copy_update_cursor(wme, data->lastcx, data->cy - 1); if (window_copy_update_selection(wme, 1)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wme, data->cy, 1); @@ -3077,13 +3080,13 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) window_copy_other_end(wme); - data->cx = data->lastcx; if (scroll_only || data->cy == screen_size_y(s) - 1) { + data->cx = data->lastcx; window_copy_scroll_up(wme, 1); if (scroll_only && data->cy > 0) window_copy_redraw_lines(wme, data->cy - 1, 2); } else { - window_copy_update_cursor(wme, data->cx, data->cy + 1); + window_copy_update_cursor(wme, data->lastcx, data->cy + 1); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy - 1, 2); } @@ -19,9 +19,11 @@ #include <sys/types.h> #include <sys/ioctl.h> +#include <ctype.h> #include <errno.h> #include <fcntl.h> #include <fnmatch.h> +#include <regex.h> #include <signal.h> #include <stdint.h> #include <stdlib.h> @@ -310,7 +312,7 @@ window_create(u_int sx, u_int sy) w = xcalloc(1, sizeof *w); w->name = NULL; - w->flags = WINDOW_STYLECHANGED; + w->flags = 0; TAILQ_INIT(&w->panes); w->active = NULL; @@ -334,7 +336,7 @@ window_create(u_int sx, u_int sy) return (w); } -void +static void window_destroy(struct window *w) { log_debug("window @%u destroyed (%d references)", w->id, w->references); @@ -408,6 +410,7 @@ window_set_name(struct window *w, const char *new_name) void window_resize(struct window *w, u_int sx, u_int sy) { + log_debug("%s: @%u resize %ux%u", __func__, w->id, sx, sy); w->sx = sx; w->sy = sy; } @@ -447,31 +450,37 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) void window_redraw_active_switch(struct window *w, struct window_pane *wp) { - struct style *sy; + struct style *sy1, *sy2; + int c1, c2; if (wp == w->active) return; - /* - * If window-style and window-active-style are the same, we don't need - * to redraw panes when switching active panes. - */ - sy = options_get_style(w->options, "window-active-style"); - if (style_equal(sy, options_get_style(w->options, "window-style"))) - return; - - /* - * If the now active or inactive pane do not have a custom style or if - * the palette is different, they need to be redrawn. - */ - if (window_pane_get_palette(w->active, w->active->style.gc.fg) != -1 || - window_pane_get_palette(w->active, w->active->style.gc.bg) != -1 || - style_is_default(&w->active->style)) - w->active->flags |= PANE_REDRAW; - if (window_pane_get_palette(wp, wp->style.gc.fg) != -1 || - window_pane_get_palette(wp, wp->style.gc.bg) != -1 || - style_is_default(&wp->style)) - wp->flags |= PANE_REDRAW; + for (;;) { + /* + * If the active and inactive styles or palettes are different, + * need to redraw the panes. + */ + sy1 = &wp->cached_style; + sy2 = &wp->cached_active_style; + if (!style_equal(sy1, sy2)) + wp->flags |= PANE_REDRAW; + else { + c1 = window_pane_get_palette(wp, sy1->gc.fg); + c2 = window_pane_get_palette(wp, sy2->gc.fg); + if (c1 != c2) + wp->flags |= PANE_REDRAW; + else { + c1 = window_pane_get_palette(wp, sy1->gc.bg); + c2 = window_pane_get_palette(wp, sy2->gc.bg); + if (c1 != c2) + wp->flags |= PANE_REDRAW; + } + } + if (wp == w->active) + break; + wp = w->active; + } } struct window_pane * @@ -775,6 +784,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp = xcalloc(1, sizeof *wp); wp->window = w; + wp->options = options_create(w->options); + wp->flags = PANE_STYLECHANGED; wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); @@ -802,8 +813,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->pipe_event = NULL; wp->saved_grid = NULL; - - style_set(&wp->style, &grid_default_cell); + wp->saved_cx = UINT_MAX; + wp->saved_cy = UINT_MAX; screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; @@ -850,6 +861,7 @@ window_pane_destroy(struct window_pane *wp) RB_REMOVE(window_pane_tree, &all_window_panes, wp); + options_free(wp->options); free((void *)wp->cwd); free(wp->shell); cmd_free_argv(wp->argc, wp->argv); @@ -913,6 +925,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wp->sx = sx; 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); wme = TAILQ_FIRST(&wp->modes); @@ -935,7 +948,7 @@ window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, if (wp->saved_grid != NULL) return; - if (!options_get_number(wp->window->options, "alternate-screen")) + if (!options_get_number(wp->options, "alternate-screen")) return; sx = screen_size_x(s); sy = screen_size_y(s); @@ -963,9 +976,24 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, struct screen *s = &wp->base; u_int sx, sy; - if (wp->saved_grid == NULL) + if (!options_get_number(wp->options, "alternate-screen")) return; - if (!options_get_number(wp->window->options, "alternate-screen")) + + /* + * 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); @@ -977,17 +1005,8 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, if (sy > wp->saved_grid->sy) screen_resize(s, sx, wp->saved_grid->sy, 1); - /* Restore the grid, cursor position and cell. */ + /* Restore the saved grid. */ grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); - if (cursor) - s->cx = wp->saved_cx; - if (s->cx > screen_size_x(s) - 1) - s->cx = screen_size_x(s) - 1; - if (cursor) - 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); /* * Turn history back on (so resize can use it) and then resize back to @@ -1137,7 +1156,7 @@ window_pane_reset_mode(struct window_pane *wp) } else { log_debug("%s: next mode is %s", __func__, next->mode->name); wp->screen = next->screen; - if (next != NULL && next->mode->resize != NULL) + if (next->mode->resize != NULL) next->mode->resize(next, wp->sx, wp->sy); } wp->flags |= (PANE_REDRAW|PANE_CHANGED); @@ -1199,24 +1218,48 @@ window_pane_visible(struct window_pane *wp) } u_int -window_pane_search(struct window_pane *wp, const char *searchstr) +window_pane_search(struct window_pane *wp, const char *term, int regex, + int ignore) { struct screen *s = &wp->base; - char *newsearchstr, *line; + regex_t r; + char *new = NULL, *line; u_int i; + int flags = 0, found; + size_t n; - xasprintf(&newsearchstr, "*%s*", searchstr); + if (!regex) { + if (ignore) + flags |= FNM_CASEFOLD; + xasprintf(&new, "*%s*", term); + } else { + if (ignore) + flags |= REG_ICASE; + if (regcomp(&r, term, flags|REG_EXTENDED) != 0) + return (0); + } for (i = 0; i < screen_size_y(s); i++) { line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); - if (fnmatch(newsearchstr, line, 0) == 0) { - free(line); - break; + for (n = strlen(line); n > 0; n--) { + if (!isspace((u_char)line[n - 1])) + break; + line[n - 1] = '\0'; } + log_debug("%s: %s", __func__, line); + if (!regex) + found = (fnmatch(new, line, 0) == 0); + else + found = (regexec(&r, line, 0, NULL, 0) == 0); free(line); + if (found) + break; } + if (!regex) + free(new); + else + regfree(&r); - free(newsearchstr); if (i == screen_size_y(s)) return (0); return (i + 1); @@ -1248,25 +1291,35 @@ window_pane_choose_best(struct window_pane **list, u_int size) struct window_pane * window_pane_find_up(struct window_pane *wp) { + struct window *w; struct window_pane *next, *best, **list; u_int edge, left, right, end, size; int status, found; if (wp == NULL) return (NULL); - status = options_get_number(wp->window->options, "pane-border-status"); + w = wp->window; + status = options_get_number(w->options, "pane-border-status"); list = NULL; size = 0; edge = wp->yoff; - if (edge == (status == 1 ? 1 : 0)) - edge = wp->window->sy + 1 - (status == 2 ? 1 : 0); + if (status == PANE_STATUS_TOP) { + if (edge == 1) + edge = w->sy + 1; + } else if (status == PANE_STATUS_BOTTOM) { + if (edge == 0) + edge = w->sy; + } else { + if (edge == 0) + edge = w->sy + 1; + } left = wp->xoff; right = wp->xoff + wp->sx; - TAILQ_FOREACH(next, &wp->window->panes, entry) { + TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->yoff + next->sy + 1 != edge) @@ -1295,25 +1348,35 @@ window_pane_find_up(struct window_pane *wp) struct window_pane * window_pane_find_down(struct window_pane *wp) { + struct window *w; struct window_pane *next, *best, **list; u_int edge, left, right, end, size; int status, found; if (wp == NULL) return (NULL); - status = options_get_number(wp->window->options, "pane-border-status"); + w = wp->window; + status = options_get_number(w->options, "pane-border-status"); list = NULL; size = 0; edge = wp->yoff + wp->sy + 1; - if (edge >= wp->window->sy - (status == 2 ? 1 : 0)) - edge = (status == 1 ? 1 : 0); + if (status == PANE_STATUS_TOP) { + if (edge >= w->sy) + edge = 1; + } else if (status == PANE_STATUS_BOTTOM) { + if (edge >= w->sy - 1) + edge = 0; + } else { + if (edge >= w->sy) + edge = 0; + } left = wp->xoff; right = wp->xoff + wp->sx; - TAILQ_FOREACH(next, &wp->window->panes, entry) { + TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->yoff != edge) @@ -1342,24 +1405,26 @@ window_pane_find_down(struct window_pane *wp) struct window_pane * window_pane_find_left(struct window_pane *wp) { + struct window *w; struct window_pane *next, *best, **list; u_int edge, top, bottom, end, size; int found; if (wp == NULL) return (NULL); + w = wp->window; list = NULL; size = 0; edge = wp->xoff; if (edge == 0) - edge = wp->window->sx + 1; + edge = w->sx + 1; top = wp->yoff; bottom = wp->yoff + wp->sy; - TAILQ_FOREACH(next, &wp->window->panes, entry) { + TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->xoff + next->sx + 1 != edge) @@ -1388,24 +1453,26 @@ window_pane_find_left(struct window_pane *wp) struct window_pane * window_pane_find_right(struct window_pane *wp) { + struct window *w; struct window_pane *next, *best, **list; u_int edge, top, bottom, end, size; int found; if (wp == NULL) return (NULL); + w = wp->window; list = NULL; size = 0; edge = wp->xoff + wp->sx + 1; - if (edge >= wp->window->sx) + if (edge >= w->sx) edge = 0; top = wp->yoff; bottom = wp->yoff + wp->sy; - TAILQ_FOREACH(next, &wp->window->panes, entry) { + TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->xoff != edge) @@ -1487,7 +1554,7 @@ window_pane_input_callback(struct client *c, int closed, void *data) c->stdin_callback = NULL; server_client_unref(c); - cdata->item->flags &= ~CMDQ_WAITING; + cmdq_continue(cdata->item); free(cdata); return; @@ -111,7 +111,7 @@ xvasprintf(char **ret, const char *fmt, va_list ap) i = vasprintf(ret, fmt, ap); - if (i < 0 || *ret == NULL) + if (i == -1) fatalx("xasprintf: %s", strerror(errno)); return i; |