diff options
-rw-r--r-- | .github/FUNDING.yml | 1 | ||||
-rw-r--r-- | CHANGES | 61 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | cmd-attach-session.c | 2 | ||||
-rw-r--r-- | cmd-break-pane.c | 3 | ||||
-rw-r--r-- | cmd-capture-pane.c | 9 | ||||
-rw-r--r-- | cmd-choose-tree.c | 12 | ||||
-rw-r--r-- | cmd-join-pane.c | 50 | ||||
-rw-r--r-- | cmd-kill-pane.c | 2 | ||||
-rw-r--r-- | cmd-new-session.c | 34 | ||||
-rw-r--r-- | cmd-new-window.c | 1 | ||||
-rw-r--r-- | cmd-parse.y | 17 | ||||
-rw-r--r-- | cmd-queue.c | 1 | ||||
-rw-r--r-- | cmd-refresh-client.c | 2 | ||||
-rw-r--r-- | cmd-resize-pane.c | 69 | ||||
-rw-r--r-- | cmd-resize-window.c | 14 | ||||
-rw-r--r-- | cmd-respawn-window.c | 1 | ||||
-rw-r--r-- | cmd-rotate-window.c | 17 | ||||
-rw-r--r-- | cmd-select-pane.c | 30 | ||||
-rw-r--r-- | cmd-send-keys.c | 5 | ||||
-rw-r--r-- | cmd-split-window.c | 36 | ||||
-rw-r--r-- | cmd-swap-pane.c | 26 | ||||
-rw-r--r-- | cmd-switch-client.c | 19 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | format-draw.c | 21 | ||||
-rw-r--r-- | format.c | 145 | ||||
-rw-r--r-- | grid.c | 22 | ||||
-rw-r--r-- | input-keys.c | 13 | ||||
-rw-r--r-- | input.c | 110 | ||||
-rw-r--r-- | key-bindings.c | 17 | ||||
-rw-r--r-- | key-string.c | 12 | ||||
-rw-r--r-- | layout-custom.c | 2 | ||||
-rw-r--r-- | layout-set.c | 8 | ||||
-rw-r--r-- | log.c | 3 | ||||
-rw-r--r-- | mode-tree.c | 25 | ||||
-rw-r--r-- | options-table.c | 24 | ||||
-rw-r--r-- | options.c | 54 | ||||
-rw-r--r-- | regsub.c | 6 | ||||
-rw-r--r-- | resize.c | 313 | ||||
-rw-r--r-- | screen-write.c | 26 | ||||
-rw-r--r-- | screen.c | 8 | ||||
-rw-r--r-- | server-client.c | 142 | ||||
-rw-r--r-- | spawn.c | 19 | ||||
-rw-r--r-- | style.c | 39 | ||||
-rw-r--r-- | tmux.1 | 285 | ||||
-rw-r--r-- | tmux.c | 1 | ||||
-rw-r--r-- | tmux.h | 68 | ||||
-rw-r--r-- | tty-keys.c | 27 | ||||
-rw-r--r-- | tty-term.c | 3 | ||||
-rw-r--r-- | tty.c | 39 | ||||
-rw-r--r-- | utf8.c | 23 | ||||
-rw-r--r-- | window-buffer.c | 70 | ||||
-rw-r--r-- | window-client.c | 106 | ||||
-rw-r--r-- | window-copy.c | 320 | ||||
-rw-r--r-- | window-tree.c | 168 | ||||
-rw-r--r-- | window.c | 77 | ||||
-rw-r--r-- | xmalloc.c | 14 | ||||
-rw-r--r-- | xmalloc.h | 1 |
58 files changed, 1747 insertions, 881 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..c4f1ae0d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +liberapay: tmux @@ -1,3 +1,64 @@ +CHANGES FROM 3.0 to X.X + +* Do not use bright when emulating 256 colours on an 8 colour terminal because + it is also bold on some terminals. + +* Add a "latest" window-size option which tries to size windows based on the + most recently used client. This is now the default. + +* Make select-pane -P set window-active-style also to match previous behaviour. + +* Do not truncate list-keys output. + +* Turn automatic-rename back on if the \033k rename escape sequence is used + with an empty name. + +* Add support for percentage sizes for resize-pane ("-x 10%"). Also change + split-window and join-pane -l to accept similar percentages and deprecate the + -p flag. + +* Add -F flag to send-keys to expand formats in search-backward and forward + copy mode commands, this makes it easier to use the cursor_word and + cursor_line formats. + +* Add formats for word and line at cursor position in copy mode. + +* Add formats for cursor and selection position in copy mode + +* Support all the forms of RGB colour strings in OSC sequences rather than + requiring two digits. + +* Limit lazy resize to panes in attached sessions only + +* Add an option to set the key sent by backspace for those whose system uses ^H + rather than ^?. + +* Change new-session -A without a session name (that is, no -s option also) to + attach to the best existing session like attach-session rather than a new + one. + +* Change window-size default from smallest to latest. + +* Add simple support for OSC 7 (result is available in the pane_path format). + +* Add push-default and pop-default for styles which change the colours and + attributes used for #[default]. These are used in status-format to restore + the behaviour of window-status-style being the default for + window-status-format. + +* Add window_marked_flag. + +* Add cursor-down-and-cancel in copy mode. + +* Default to previous search string for search-forward and search-backward. + +* Add -Z flag to rotate-window, select-pane, swap-pane, switch-client to + preserve zoomed state. + +* Add -N to capture-pane to preserve trailing spaces. + +* Add reverse sorting in tree, client and buffer modes. + CHANGES FROM 3.0 to 3.0a * Do not require REG_STARTEND. diff --git a/Makefile.am b/Makefile.am index 94b1564b..ab302637 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,8 +6,9 @@ CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c # Distribution tarball options. EXTRA_DIST = \ - CHANGES README README.ja COPYING example_tmux.conf compat/*.[ch] \ + CHANGES README README.ja COPYING example_tmux.conf \ osdep-*.c mdoc2man.awk tmux.1 +dist_EXTRA_tmux_SOURCES = compat/*.[ch] # Preprocessor flags. AM_CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\"" diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 6de734e5..477d3517 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -127,6 +127,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; + s->curw->window->latest = c; } else { if (server_client_open(c, &cause) != 0) { cmdq_error(item, "open terminal failed: %s", cause); @@ -159,6 +160,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; + s->curw->window->latest = c; if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index b4c5b7cd..6c638103 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -76,11 +76,12 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) window_lost_pane(w, wp); layout_close_pane(wp); - w = wp->window = window_create(w->sx, w->sy); + w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel); options_set_parent(wp->options, w->options); wp->flags |= PANE_STYLECHANGED; TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; + w->latest = c; if (!args_has(args, 'n')) { name = default_window_name(w); diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index a3ec066c..fc6c26e4 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = { .name = "capture-pane", .alias = "capturep", - .args = { "ab:CeE:JpPqS:t:", 0, 0 }, - .usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] " + .args = { "ab:CeE:JNpPqS:t:", 0, 0 }, + .usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] " "[-S start-line] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -110,7 +110,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, struct grid *gd; const struct grid_line *gl; struct grid_cell *gc = NULL; - int n, with_codes, escape_c0, join_lines; + int n, with_codes, escape_c0, join_lines, no_trim; u_int i, sx, top, bottom, tmp; char *cause, *buf, *line; const char *Sflag, *Eflag; @@ -170,11 +170,12 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, with_codes = args_has(args, 'e'); escape_c0 = args_has(args, 'C'); join_lines = args_has(args, 'J'); + no_trim = args_has(args, 'N'); buf = NULL; for (i = top; i <= bottom; i++) { line = grid_string_cells(gd, 0, i, sx, &gc, with_codes, - escape_c0, !join_lines); + escape_c0, !join_lines && !no_trim); linelen = strlen(line); buf = cmd_capture_pane_append(buf, len, line, linelen); diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 5eb58ff5..8178ec9f 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -30,8 +30,8 @@ const struct cmd_entry cmd_choose_tree_entry = { .name = "choose-tree", .alias = NULL, - .args = { "F:Gf:NO:st:wZ", 0, 1 }, - .usage = "[-GNsw] [-F format] [-f filter] [-O sort-order] " + .args = { "F:Gf:NO:rst:wZ", 0, 1 }, + .usage = "[-GNrswZ] [-F format] [-f filter] [-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -44,8 +44,8 @@ const struct cmd_entry cmd_choose_client_entry = { .name = "choose-client", .alias = NULL, - .args = { "F:f:NO:t:Z", 0, 1 }, - .usage = "[-N] [-F format] [-f filter] [-O sort-order] " + .args = { "F:f:NO:rt:Z", 0, 1 }, + .usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -58,8 +58,8 @@ const struct cmd_entry cmd_choose_buffer_entry = { .name = "choose-buffer", .alias = NULL, - .args = { "F:f:NO:t:Z", 0, 1 }, - .usage = "[-N] [-F format] [-f filter] [-O sort-order] " + .args = { "F:f:NO:rt:Z", 0, 1 }, + .usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 5d0e0d6a..67207291 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -20,6 +20,7 @@ #include <sys/types.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include "tmux.h" @@ -34,8 +35,8 @@ const struct cmd_entry cmd_join_pane_entry = { .name = "join-pane", .alias = "joinp", - .args = { "bdhvp:l:s:t:", 0, 0 }, - .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, + .args = { "bdfhvp:l:s:t:", 0, 0 }, + .usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .target = { 't', CMD_FIND_PANE, 0 }, @@ -67,11 +68,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *src_wp, *dst_wp; - char *cause; - int size, percentage, dst_idx; + char *cause, *copy; + const char *errstr, *p; + size_t plen; + int size, percentage, dst_idx, not_same_window; + int flags; enum layout_type type; struct layout_cell *lc; - int not_same_window, flags; if (self->entry == &cmd_join_pane_entry) not_same_window = 1; @@ -104,12 +107,28 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) type = LAYOUT_LEFTRIGHT; size = -1; - if (args_has(args, 'l')) { - size = args_strtonum(args, 'l', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(item, "size %s", cause); - free(cause); - return (CMD_RETURN_ERROR); + if ((p = args_get(args, 'l')) != NULL) { + plen = strlen(p); + if (p[plen - 1] == '%') { + copy = xstrdup(p); + copy[plen - 1] = '\0'; + percentage = strtonum(copy, 0, INT_MAX, &errstr); + free(copy); + if (errstr != NULL) { + cmdq_error(item, "percentage %s", errstr); + return (CMD_RETURN_ERROR); + } + if (type == LAYOUT_TOPBOTTOM) + size = (dst_wp->sy * percentage) / 100; + else + size = (dst_wp->sx * percentage) / 100; + } else { + size = args_strtonum(args, 'l', 0, INT_MAX, &cause); + if (cause != NULL) { + cmdq_error(item, "size %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } } } else if (args_has(args, 'p')) { percentage = args_strtonum(args, 'p', 0, 100, &cause); @@ -123,10 +142,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) else size = (dst_wp->sx * percentage) / 100; } + + flags = 0; if (args_has(args, 'b')) - flags = SPAWN_BEFORE; - else - flags = 0; + flags |= SPAWN_BEFORE; + if (args_has(args, 'f')) + flags |= SPAWN_FULLSIZE; + lc = layout_split_pane(dst_wp, type, size, flags); if (lc == NULL) { cmdq_error(item, "create pane failed: pane too small"); diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 01b1f518..f0aacb2a 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_kill_pane_entry = { .target = { 't', CMD_FIND_PANE, 0 }, - .flags = 0, + .flags = CMD_AFTERHOOK, .exec = cmd_kill_pane_exec }; diff --git a/cmd-new-session.c b/cmd-new-session.c index e0540815..c76b564e 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -94,26 +94,31 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } - if (args_has(args, 's')) { - newname = format_single(item, args_get(args, 's'), c, NULL, - NULL, NULL); + tmp = args_get(args, 's'); + if (tmp != NULL) { + newname = format_single(item, tmp, c, NULL, NULL, NULL); if (!session_check_name(newname)) { cmdq_error(item, "bad session name: %s", newname); goto fail; } - if ((as = session_find(newname)) != NULL) { - if (args_has(args, 'A')) { - retval = cmd_attach_session(item, - newname, args_has(args, 'D'), - args_has(args, 'X'), 0, NULL, - args_has(args, 'E')); - free(newname); - return (retval); - } - cmdq_error(item, "duplicate session: %s", newname); - goto fail; + } + if (args_has(args, 'A')) { + if (newname != NULL) + as = session_find(newname); + else + as = item->target.s; + if (as != NULL) { + retval = cmd_attach_session(item, as->name, + args_has(args, 'D'), args_has(args, 'X'), 0, NULL, + args_has(args, 'E')); + free(newname); + return (retval); } } + if (newname != NULL && session_find(newname) != NULL) { + cmdq_error(item, "duplicate session: %s", newname); + goto fail; + } /* Is this going to be part of a session group? */ group = args_get(args, 't'); @@ -259,6 +264,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; + sc.c = c; sc.name = args_get(args, 'n'); sc.argc = args->argc; diff --git a/cmd-new-window.c b/cmd-new-window.c index 6cb33dd9..9a1025e9 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -72,6 +72,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; + sc.c = c; sc.name = args_get(args, 'n'); sc.argc = args->argc; diff --git a/cmd-parse.y b/cmd-parse.y index a51e4f6e..2c924010 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -176,18 +176,18 @@ expanded : format struct cmd_parse_input *pi = ps->input; struct format_tree *ft; struct client *c = pi->c; - struct cmd_find_state *fs; + struct cmd_find_state *fsp; + struct cmd_find_state fs; int flags = FORMAT_NOJOBS; if (cmd_find_valid_state(&pi->fs)) - fs = &pi->fs; - else - fs = NULL; + fsp = &pi->fs; + else { + cmd_find_from_client(&fs, c, 0); + fsp = &fs; + } ft = format_create(NULL, pi->item, FORMAT_NONE, flags); - if (fs != NULL) - format_defaults(ft, c, fs->s, fs->wl, fs->wp); - else - format_defaults(ft, c, NULL, NULL, NULL); + format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp); $$ = format_expand(ft, $1); format_free(ft); @@ -696,6 +696,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, pr.status = CMD_PARSE_ERROR; pr.error = cmd_parse_get_error(pi->file, line, cause); free(cause); + cmd_list_free(cmdlist); goto out; } cmd_list_append(cmdlist, add); diff --git a/cmd-queue.c b/cmd-queue.c index ef68d5d5..fa6999e8 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -102,7 +102,6 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) } while (item != NULL); } - /* Insert a hook. */ void cmdq_insert_hook(struct session *s, struct cmdq_item *item, diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 49921a74..b4c5e844 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -130,7 +130,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "size too small or too big"); return (CMD_RETURN_ERROR); } - tty_set_size(&c->tty, x, y); + tty_set_size(&c->tty, x, y, 0, 0); c->flags |= CLIENT_SIZECHANGED; recalculate_sizes(); } diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 8d35d96f..3962546d 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -19,6 +19,7 @@ #include <sys/types.h> #include <stdlib.h> +#include <string.h> #include "tmux.h" @@ -55,10 +56,11 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) struct window *w = wl->window; struct client *c = item->client; struct session *s = item->target.s; - const char *errstr; - char *cause; + const char *errstr, *p; + char *cause, *copy; u_int adjust; - int x, y; + int x, y, percentage; + size_t plen; if (args_has(args, 'M')) { if (cmd_mouse_window(&shared->mouse, &s) == NULL) @@ -91,21 +93,58 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) } } - if (args_has(args, 'x')) { - x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(item, "width %s", cause); - free(cause); - return (CMD_RETURN_ERROR); + if ((p = args_get(args, 'x')) != NULL) { + plen = strlen(p); + if (p[plen - 1] == '%') { + copy = xstrdup(p); + copy[plen - 1] = '\0'; + percentage = strtonum(copy, 0, INT_MAX, &errstr); + free(copy); + if (errstr != NULL) { + cmdq_error(item, "width %s", errstr); + return (CMD_RETURN_ERROR); + } + x = (w->sx * percentage) / 100; + if (x < PANE_MINIMUM) + x = PANE_MINIMUM; + if (x > INT_MAX) + x = INT_MAX; + } else { + x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX, + &cause); + if (cause != NULL) { + cmdq_error(item, "width %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } } layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x); } - if (args_has(args, 'y')) { - y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(item, "height %s", cause); - free(cause); - return (CMD_RETURN_ERROR); + if ((p = args_get(args, 'y')) != NULL) { + plen = strlen(p); + if (p[plen - 1] == '%') { + copy = xstrdup(p); + copy[plen - 1] = '\0'; + percentage = strtonum(copy, 0, INT_MAX, &errstr); + free(copy); + if (errstr != NULL) { + cmdq_error(item, "height %s", errstr); + return (CMD_RETURN_ERROR); + } + y = (w->sy * percentage) / 100; + if (y < PANE_MINIMUM) + y = PANE_MINIMUM; + if (y > INT_MAX) + y = INT_MAX; + } + else { + y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX, + &cause); + if (cause != NULL) { + cmdq_error(item, "height %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } } layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y); } diff --git a/cmd-resize-window.c b/cmd-resize-window.c index d780b6ee..9cc74e82 100644 --- a/cmd-resize-window.c +++ b/cmd-resize-window.c @@ -53,6 +53,7 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) const char *errstr; char *cause; u_int adjust, sx, sy; + int xpixel = -1, ypixel = -1; if (args->argc == 0) adjust = 1; @@ -97,13 +98,16 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) } else if (args_has(args, 'D')) sy += adjust; - if (args_has(args, 'A')) - default_window_size(s, w, &sx, &sy, WINDOW_SIZE_LARGEST); - else if (args_has(args, 'a')) - default_window_size(s, w, &sx, &sy, WINDOW_SIZE_SMALLEST); + if (args_has(args, 'A')) { + default_window_size(NULL, s, w, &sx, &sy, &xpixel, &ypixel, + WINDOW_SIZE_LARGEST); + } else if (args_has(args, 'a')) { + default_window_size(NULL, s, w, &sx, &sy, &xpixel, &ypixel, + WINDOW_SIZE_SMALLEST); + } options_set_number(w->options, "window-size", WINDOW_SIZE_MANUAL); - resize_window(w, sx, sy); + resize_window(w, sx, sy, xpixel, ypixel); return (CMD_RETURN_NORMAL); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index aec22912..497e401e 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -59,6 +59,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) sc.item = item; sc.s = s; sc.wl = wl; + sc.c = cmd_find_client(item, NULL, 1); sc.name = NULL; sc.argc = args->argc; diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 6dc0f2a8..cc661163 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -31,8 +31,8 @@ const struct cmd_entry cmd_rotate_window_entry = { .name = "rotate-window", .alias = "rotatew", - .args = { "Dt:U", 0, 0 }, - .usage = "[-DU] " CMD_TARGET_WINDOW_USAGE, + .args = { "Dt:UZ", 0, 0 }, + .usage = "[-DUZ] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -50,7 +50,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) struct layout_cell *lc; u_int sx, sy, xoff, yoff; - server_unzoom_window(w); + window_push_zoom(w, args_has(self->args, 'Z')); if (args_has(self->args, 'D')) { wp = TAILQ_LAST(&w->panes, window_panes); @@ -77,9 +77,6 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL) wp = TAILQ_LAST(&w->panes, window_panes); - window_set_active_pane(w, wp, 1); - cmd_find_from_winlink_pane(current, wl, wp, 0); - server_redraw_window(w); } else { wp = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, wp, entry); @@ -105,10 +102,12 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) if ((wp = TAILQ_NEXT(w->active, entry)) == NULL) wp = TAILQ_FIRST(&w->panes); - window_set_active_pane(w, wp, 1); - cmd_find_from_winlink_pane(current, wl, wp, 0); - server_redraw_window(w); } + window_set_active_pane(w, wp, 1); + cmd_find_from_winlink_pane(current, wl, wp, 0); + window_pop_zoom(w); + server_redraw_window(w); + return (CMD_RETURN_NORMAL); } diff --git a/cmd-select-pane.c b/cmd-select-pane.c index b46f7cd9..6542c919 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 }, /* -P and -g deprecated */ - .usage = "[-DdeLlMmRU] [-T title] " CMD_TARGET_PANE_USAGE, + .args = { "DdegLlMmP:RT:t:UZ", 0, 0 }, /* -P and -g deprecated */ + .usage = "[-DdeLlMmRUZ] [-T title] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -46,8 +46,8 @@ const struct cmd_entry cmd_last_pane_entry = { .name = "last-pane", .alias = "lastp", - .args = { "det:", 0, 0 }, - .usage = "[-de] " CMD_TARGET_WINDOW_USAGE, + .args = { "det:Z", 0, 0 }, + .usage = "[-deZ] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -111,12 +111,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) else if (args_has(self->args, 'd')) lastwp->flags |= PANE_INPUTOFF; else { - server_unzoom_window(w); + if (window_push_zoom(w, args_has(self->args, 'Z'))) + server_redraw_window(w); window_redraw_active_switch(w, lastwp); if (window_set_active_pane(w, lastwp, 1)) { cmd_find_from_winlink(current, wl, 0); cmd_select_pane_redraw(w); } + if (window_pop_zoom(w)) + server_redraw_window(w); } return (CMD_RETURN_NORMAL); } @@ -163,17 +166,21 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) } if (args_has(self->args, 'L')) { - server_unzoom_window(wp->window); + window_push_zoom(w, 1); wp = window_pane_find_left(wp); + window_pop_zoom(w); } else if (args_has(self->args, 'R')) { - server_unzoom_window(wp->window); + window_push_zoom(w, 1); wp = window_pane_find_right(wp); + window_pop_zoom(w); } else if (args_has(self->args, 'U')) { - server_unzoom_window(wp->window); + window_push_zoom(w, 1); wp = window_pane_find_up(wp); + window_pop_zoom(w); } else if (args_has(self->args, 'D')) { - server_unzoom_window(wp->window); + window_push_zoom(w, 1); wp = window_pane_find_down(wp); + window_pop_zoom(w); } if (wp == NULL) return (CMD_RETURN_NORMAL); @@ -198,13 +205,16 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (wp == w->active) return (CMD_RETURN_NORMAL); - server_unzoom_window(wp->window); + if (window_push_zoom(w, args_has(self->args, 'Z'))) + server_redraw_window(w); window_redraw_active_switch(w, wp); if (window_set_active_pane(w, wp, 1)) { cmd_find_from_winlink_pane(current, wl, wp, 0); cmdq_insert_hook(s, item, current, "after-select-pane"); cmd_select_pane_redraw(w); } + if (window_pop_zoom(w)) + server_redraw_window(w); return (CMD_RETURN_NORMAL); } diff --git a/cmd-send-keys.c b/cmd-send-keys.c index fe202335..ddbab6f7 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -33,8 +33,9 @@ const struct cmd_entry cmd_send_keys_entry = { .name = "send-keys", .alias = "send", - .args = { "HlXRMN:t:", 0, -1 }, - .usage = "[-HlXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...", + .args = { "FHlMN:Rt:X", 0, -1 }, + .usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE + " key ...", .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-split-window.c b/cmd-split-window.c index 8d63473f..eaf1f74c 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -41,8 +41,7 @@ const struct cmd_entry cmd_split_window_entry = { .args = { "bc:de:fF:hIl:p:Pt:v", 0, -1 }, .usage = "[-bdefhIPv] [-c start-directory] [-e environment] " - "[-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE - " [command]", + "[-F format] [-l size] " CMD_TARGET_PANE_USAGE " [command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -64,20 +63,37 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct layout_cell *lc; struct cmd_find_state fs; int size, percentage, flags, input; - const char *template, *add; - char *cause, *cp; + const char *template, *add, *errstr, *p; + char *cause, *cp, *copy; + size_t plen; struct args_value *value; if (args_has(args, 'h')) type = LAYOUT_LEFTRIGHT; else type = LAYOUT_TOPBOTTOM; - if (args_has(args, 'l')) { - size = args_strtonum(args, 'l', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(item, "create pane failed: -l %s", cause); - free(cause); - return (CMD_RETURN_ERROR); + if ((p = args_get(args, 'l')) != NULL) { + plen = strlen(p); + if (p[plen - 1] == '%') { + copy = xstrdup(p); + copy[plen - 1] = '\0'; + percentage = strtonum(copy, 0, INT_MAX, &errstr); + free(copy); + if (errstr != NULL) { + cmdq_error(item, "percentage %s", errstr); + return (CMD_RETURN_ERROR); + } + if (type == LAYOUT_TOPBOTTOM) + size = (wp->sy * percentage) / 100; + else + size = (wp->sx * percentage) / 100; + } else { + size = args_strtonum(args, 'l', 0, INT_MAX, &cause); + if (cause != NULL) { + cmdq_error(item, "lines %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } } } else if (args_has(args, 'p')) { percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 994ad0e8..3e0e6e60 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -32,8 +32,8 @@ const struct cmd_entry cmd_swap_pane_entry = { .name = "swap-pane", .alias = "swapp", - .args = { "dDs:t:U", 0, 0 }, - .usage = "[-dDU] " CMD_SRCDST_PANE_USAGE, + .args = { "dDs:t:UZ", 0, 0 }, + .usage = "[-dDUZ] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .target = { 't', CMD_FIND_PANE, 0 }, @@ -45,6 +45,7 @@ const struct cmd_entry cmd_swap_pane_entry = { static enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) { + struct args *args = self->args; struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; @@ -54,23 +55,27 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) dst_wp = item->target.wp; src_w = item->source.wl->window; src_wp = item->source.wp; - server_unzoom_window(dst_w); - if (args_has(self->args, 'D')) { + if (window_push_zoom(dst_w, args_has(args, 'Z'))) + server_redraw_window(dst_w); + + if (args_has(args, 'D')) { src_w = dst_w; src_wp = TAILQ_NEXT(dst_wp, entry); if (src_wp == NULL) src_wp = TAILQ_FIRST(&dst_w->panes); - } else if (args_has(self->args, 'U')) { + } else if (args_has(args, 'U')) { src_w = dst_w; src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); } - server_unzoom_window(src_w); + + if (src_w != dst_w && window_push_zoom(src_w, args_has(args, 'Z'))) + server_redraw_window(src_w); if (src_wp == dst_wp) - return (CMD_RETURN_NORMAL); + goto out; tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry); TAILQ_REMOVE(&dst_w->panes, dst_wp, entry); @@ -103,7 +108,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) dst_wp->xoff = xoff; dst_wp->yoff = yoff; window_pane_resize(dst_wp, sx, sy); - if (!args_has(self->args, 'd')) { + if (!args_has(args, 'd')) { if (src_w != dst_w) { window_set_active_pane(src_w, dst_wp, 1); window_set_active_pane(dst_w, src_wp, 1); @@ -126,5 +131,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) server_redraw_window(src_w); server_redraw_window(dst_w); +out: + if (window_pop_zoom(src_w)) + server_redraw_window(src_w); + if (src_w != dst_w && window_pop_zoom(dst_w)) + server_redraw_window(dst_w); return (CMD_RETURN_NORMAL); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 8f51d0fe..309a7e7c 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_switch_client_entry = { .name = "switch-client", .alias = "switchc", - .args = { "lc:Enpt:rT:", 0, 0 }, - .usage = "[-Elnpr] [-c target-client] [-t target-session] " + .args = { "lc:Enpt:rT:Z", 0, 0 }, + .usage = "[-ElnprZ] [-c target-client] [-t target-session] " "[-T key-table]", /* -t is special */ @@ -54,6 +54,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) struct client *c; struct session *s; struct winlink *wl; + struct window *w; struct window_pane *wp; const char *tablename; struct key_table *table; @@ -72,6 +73,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); s = item->target.s; wl = item->target.wl; + w = wl->window; wp = item->target.wp; if (args_has(args, 'r')) @@ -112,12 +114,15 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) } else { if (item->client == NULL) return (CMD_RETURN_NORMAL); + if (wl != NULL && wp != NULL) { + if (window_push_zoom(w, args_has(self->args, 'Z'))) + server_redraw_window(w); + window_redraw_active_switch(w, wp); + window_set_active_pane(w, wp, 1); + if (window_pop_zoom(w)) + server_redraw_window(w); + } if (wl != NULL) { - server_unzoom_window(wl->window); - if (wp != NULL) { - window_redraw_active_switch(wp->window, wp); - window_set_active_pane(wp->window, wp, 1); - } session_set_current(s, wl); cmd_find_from_session(&item->shared->current, s, 0); } diff --git a/configure.ac b/configure.ac index 57c3fd73..5fba1eaf 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.0a) +AC_INIT([tmux], next-3.1) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) diff --git a/format-draw.c b/format-draw.c index e0ca89f0..6cced9fd 100644 --- a/format-draw.c +++ b/format-draw.c @@ -513,8 +513,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, int focus_start = -1, focus_end = -1; int list_state = -1, fill = -1; enum style_align list_align = STYLE_ALIGN_DEFAULT; - struct grid_cell gc; - struct style sy; + struct grid_cell gc, current_default; + struct style sy, saved_sy; struct utf8_data *ud = &sy.gc.data; const char *cp, *end; enum utf8_state more; @@ -523,7 +523,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, struct format_ranges frs; struct style_range *sr; - style_set(&sy, base); + memcpy(¤t_default, base, sizeof current_default); + style_set(&sy, ¤t_default); TAILQ_INIT(&frs); log_debug("%s: %s", __func__, expanded); @@ -535,7 +536,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, for (i = 0; i < TOTAL; i++) { screen_init(&s[i], size, 1, 0); screen_write_start(&ctx[i], NULL, &s[i]); - screen_write_clearendofline(&ctx[i], base->bg); + screen_write_clearendofline(&ctx[i], current_default.bg); width[i] = 0; } @@ -581,7 +582,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, goto out; } tmp = xstrndup(cp + 2, end - (cp + 2)); - if (style_parse(&sy, base, tmp) != 0) { + style_copy(&saved_sy, &sy); + if (style_parse(&sy, ¤t_default, tmp) != 0) { log_debug("%s: invalid style '%s'", __func__, tmp); free(tmp); cp = end + 1; @@ -595,6 +597,15 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, if (sy.fill != 8) fill = sy.fill; + /* If this style pushed or popped the default, update it. */ + if (sy.default_type == STYLE_DEFAULT_PUSH) { + memcpy(¤t_default, &saved_sy.gc, sizeof current_default); + sy.default_type = STYLE_DEFAULT_BASE; + } else if (sy.default_type == STYLE_DEFAULT_POP) { + memcpy(¤t_default, base, sizeof current_default); + sy.default_type = STYLE_DEFAULT_BASE; + } + /* Check the list state. */ switch (sy.list) { case STYLE_LIST_ON: @@ -574,7 +574,7 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe) struct window_pane *wp = ft->wp; char *cmd; - if (wp == NULL) + if (wp == NULL || wp->shell == NULL) return; cmd = osdep_get_name(wp->fd, wp->tty); @@ -718,30 +718,19 @@ format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe) xasprintf(&fe->value, "%.*s", (int)gc.data.size, gc.data.data); } -/* Callback for mouse_word. */ -static void -format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) +/* Return word at given coordinates. Caller frees. */ +char * +format_grid_word(struct grid *gd, u_int x, u_int y) { - struct window_pane *wp; - u_int x, y, end; - struct grid *gd; struct grid_line *gl; struct grid_cell gc; const char *ws; struct utf8_data *ud = NULL; + u_int end; size_t size = 0; int found = 0; + char *s = NULL; - if (!ft->m.valid) - return; - wp = cmd_mouse_pane(&ft->m, NULL, NULL); - if (wp == NULL) - return; - if (!TAILQ_EMPTY (&wp->modes)) - return; - if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) - return; - gd = wp->base.grid; ws = options_get_string(global_s_options, "word-separators"); y = gd->hsize + y; @@ -794,21 +783,19 @@ format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) } if (size != 0) { ud[size].size = 0; - fe->value = utf8_tocstr(ud); + s = utf8_tocstr(ud); free(ud); } + return (s); } -/* Callback for mouse_line. */ +/* Callback for mouse_word. */ static void -format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) +format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp; u_int x, y; - struct grid *gd; - struct grid_cell gc; - struct utf8_data *ud = NULL; - size_t size = 0; + char *s; if (!ft->m.valid) return; @@ -819,7 +806,21 @@ format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) return; if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) return; - gd = wp->base.grid; + + s = format_grid_word(wp->base.grid, x, y); + if (s != NULL) + fe->value = s; +} + +/* Return line at given coordinates. Caller frees. */ +char * +format_grid_line(struct grid *gd, u_int y) +{ + struct grid_cell gc; + struct utf8_data *ud = NULL; + u_int x; + size_t size = 0; + char *s = NULL; y = gd->hsize + y; for (x = 0; x < grid_line_length(gd, y); x++) { @@ -832,9 +833,33 @@ format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) } if (size != 0) { ud[size].size = 0; - fe->value = utf8_tocstr(ud); + s = utf8_tocstr(ud); free(ud); } + return (s); +} + +/* Callback for mouse_line. */ +static void +format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp; + u_int x, y; + char *s; + + if (!ft->m.valid) + return; + wp = cmd_mouse_pane(&ft->m, NULL, NULL); + if (wp == NULL) + return; + if (!TAILQ_EMPTY (&wp->modes)) + return; + if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) + return; + + s = format_grid_line(wp->base.grid, y); + if (s != NULL) + fe->value = s; } /* Merge a format tree. */ @@ -966,7 +991,6 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *, } } - /* Add a key-value pair. */ void format_add(struct format_tree *ft, const char *key, const char *fmt, ...) @@ -1290,7 +1314,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) } /* Now try single character with arguments. */ - if (strchr("mCs=", cp[0]) == NULL) + if (strchr("mCs=p", cp[0]) == NULL) break; c = cp[0]; @@ -1551,15 +1575,15 @@ static int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - struct window_pane *wp = ft->wp; - const char *errptr, *copy, *cp, *marker = NULL; - char *copy0, *condition, *found, *new; - char *value, *left, *right; - size_t valuelen; - int modifiers = 0, limit = 0, j; - struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; - struct format_modifier *sub = NULL; - u_int i, count; + struct window_pane *wp = ft->wp; + const char *errptr, *copy, *cp, *marker = NULL; + char *copy0, *condition, *found, *new; + char *value, *left, *right; + size_t valuelen; + int modifiers = 0, limit = 0, width = 0, j; + struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; + struct format_modifier **sub = NULL; + u_int i, count, nsub = 0; /* Make a copy of the key. */ copy = copy0 = xstrndup(key, keylen); @@ -1588,7 +1612,9 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, case 's': if (fm->argc < 2) break; - sub = fm; + sub = xreallocarray (sub, nsub + 1, + sizeof *sub); + sub[nsub++] = fm; break; case '=': if (fm->argc < 1) @@ -1600,6 +1626,14 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, if (fm->argc >= 2 && fm->argv[1] != NULL) marker = fm->argv[1]; break; + case 'p': + if (fm->argc < 1) + break; + width = strtonum(fm->argv[0], INT_MIN, INT_MAX, + &errptr); + if (errptr != NULL) + width = 0; + break; case 'l': modifiers |= FORMAT_LITERAL; break; @@ -1800,10 +1834,10 @@ done: } /* Perform substitution if any. */ - if (sub != NULL) { - left = format_expand(ft, sub->argv[0]); - right = format_expand(ft, sub->argv[1]); - new = format_sub(sub, value, left, right); + for (i = 0; i < nsub; i++) { + left = format_expand(ft, sub[i]->argv[0]); + right = format_expand(ft, sub[i]->argv[1]); + new = format_sub(sub[i], value, left, right); format_log(ft, "substitute '%s' to '%s': %s", left, right, new); free(value); value = new; @@ -1834,6 +1868,19 @@ done: format_log(ft, "applied length limit %d: %s", limit, value); } + /* Pad the value if needed. */ + if (width > 0) { + new = utf8_padcstr(value, width); + free(value); + value = new; + format_log(ft, "applied padding width %d: %s", width, value); + } else if (width < 0) { + new = utf8_rpadcstr(value, -width); + free(value); + value = new; + format_log(ft, "applied padding width %d: %s", width, value); + } + /* Expand the buffer and copy in the value. */ valuelen = strlen(value); while (*len - *off < valuelen + 1) { @@ -1846,12 +1893,15 @@ done: format_log(ft, "replaced '%s' with '%s'", copy0, value); free(value); + free(sub); format_free_modifiers(list, count); free(copy0); return (0); fail: format_log(ft, "failed %s", copy0); + + free(sub); format_free_modifiers(list, count); free(copy0); return (-1); @@ -2123,6 +2173,8 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_pid", "%ld", (long) c->pid); format_add(ft, "client_height", "%u", tty->sy); format_add(ft, "client_width", "%u", tty->sx); + format_add(ft, "client_cell_width", "%u", tty->xpixel); + format_add(ft, "client_cell_height", "%u", tty->ypixel); format_add(ft, "client_tty", "%s", c->ttyname); format_add(ft, "client_control_mode", "%d", !!(c->flags & CLIENT_CONTROL)); @@ -2174,6 +2226,8 @@ format_defaults_window(struct format_tree *ft, struct window *w) format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); format_add(ft, "window_height", "%u", w->sy); + format_add(ft, "window_cell_width", "%u", w->xpixel); + format_add(ft, "window_cell_height", "%u", w->ypixel); format_add_cb(ft, "window_layout", format_cb_window_layout); format_add_cb(ft, "window_visible_layout", format_cb_window_visible_layout); @@ -2217,6 +2271,11 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl) format_add(ft, "window_end_flag", "%d", !!(wl == RB_MAX(winlinks, &s->windows))); + if (server_check_marked() && marked_pane.wl == wl) + format_add(ft, "window_marked_flag", "1"); + else + format_add(ft, "window_marked_flag", "0"); + format_add(ft, "window_bell_flag", "%d", !!(wl->flags & WINLINK_BELL)); format_add(ft, "window_activity_flag", "%d", @@ -2253,6 +2312,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_width", "%u", wp->sx); format_add(ft, "pane_height", "%u", wp->sy); format_add(ft, "pane_title", "%s", wp->base.title); + if (wp->base.path != NULL) + format_add(ft, "pane_path", "%s", wp->base.path); format_add(ft, "pane_id", "%%%u", wp->id); format_add(ft, "pane_active", "%d", wp == w->active); format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); @@ -186,17 +186,19 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) struct grid_cell *gc; memcpy(gce, &grid_cleared_entry, sizeof *gce); - if (bg & COLOUR_FLAG_RGB) { - grid_get_extended_cell(gl, gce, gce->flags); - gl->flags |= GRID_LINE_EXTENDED; + if (bg != 8) { + if (bg & COLOUR_FLAG_RGB) { + grid_get_extended_cell(gl, gce, gce->flags); + gl->flags |= GRID_LINE_EXTENDED; - gc = &gl->extddata[gce->offset]; - memcpy(gc, &grid_cleared_cell, sizeof *gc); - gc->bg = bg; - } else { - if (bg & COLOUR_FLAG_256) - gce->flags |= GRID_FLAG_BG256; - gce->data.bg = bg; + gc = &gl->extddata[gce->offset]; + memcpy(gc, &grid_cleared_cell, sizeof *gc); + gc->bg = bg; + } else { + if (bg & COLOUR_FLAG_256) + gce->flags |= GRID_FLAG_BG256; + gce->data.bg = bg; + } } } diff --git a/input-keys.c b/input-keys.c index 9e47a553..b0ea5104 100644 --- a/input-keys.c +++ b/input-keys.c @@ -42,9 +42,6 @@ struct input_key_ent { }; static const struct input_key_ent input_keys[] = { - /* Backspace key. */ - { KEYC_BSPACE, "\177", 0 }, - /* Paste keys. */ { KEYC_PASTE_START, "\033[200~", 0 }, { KEYC_PASTE_END, "\033[201~", 0 }, @@ -159,7 +156,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) u_int i; size_t dlen; char *out; - key_code justkey; + key_code justkey, newkey; struct utf8_data ud; log_debug("writing key 0x%llx (%s) to %%%u", key, @@ -179,6 +176,14 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) return; } + /* Is this backspace? */ + if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) { + newkey = options_get_number(global_options, "backspace"); + if (newkey >= 0x7f) + newkey = '\177'; + key = newkey|(key & KEYC_MASK_MOD); + } + /* * 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. @@ -740,7 +740,7 @@ input_timer_callback(__unused int fd, __unused short events, void *arg) static void input_start_timer(struct input_ctx *ictx) { - struct timeval tv = { .tv_usec = 100000 }; + struct timeval tv = { .tv_sec = 5, .tv_usec = 0 }; event_del(&ictx->timer); event_add(&ictx->timer, &tv); @@ -876,7 +876,7 @@ input_set_state(struct window_pane *wp, const struct input_transition *itr) void input_parse(struct window_pane *wp) { - struct evbuffer *evb = wp->event->input; + struct evbuffer *evb = wp->event->input; input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb)); evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); @@ -888,7 +888,8 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) { struct input_ctx *ictx = wp->ictx; struct screen_write_ctx *sctx = &ictx->ctx; - const struct input_transition *itr; + const struct input_state *state = NULL; + const struct input_transition *itr = NULL; size_t off = 0; if (len == 0) @@ -916,16 +917,23 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) ictx->ch = buf[off++]; /* Find the transition. */ - itr = ictx->state->transitions; - while (itr->first != -1 && itr->last != -1) { - if (ictx->ch >= itr->first && ictx->ch <= itr->last) - break; - itr++; - } - if (itr->first == -1 || itr->last == -1) { - /* No transition? Eh? */ - fatalx("no transition from state"); + if (ictx->state != state || + itr == NULL || + ictx->ch < itr->first || + ictx->ch > itr->last) { + itr = ictx->state->transitions; + while (itr->first != -1 && itr->last != -1) { + if (ictx->ch >= itr->first && + ictx->ch <= itr->last) + break; + itr++; + } + if (itr->first == -1 || itr->last == -1) { + /* No transition? Eh? */ + fatalx("no transition from state"); + } } + state = ictx->state; /* * Any state except print stops the current collection. This is @@ -2203,6 +2211,12 @@ input_exit_osc(struct input_ctx *ictx) case 4: input_osc_4(ictx, p); break; + case 7: + if (utf8_isvalid(p)) { + screen_set_path(sctx->s, p); + server_status_window(ictx->wp->window); + } + break; case 10: input_osc_10(ictx, p); break; @@ -2271,6 +2285,9 @@ input_enter_rename(struct input_ctx *ictx) static void input_exit_rename(struct input_ctx *ictx) { + struct window_pane *wp = ictx->wp; + struct options_entry *oe; + if (ictx->flags & INPUT_DISCARD) return; if (!options_get_number(ictx->wp->options, "allow-rename")) @@ -2279,6 +2296,13 @@ input_exit_rename(struct input_ctx *ictx) if (!utf8_isvalid(ictx->input_buf)) return; + + if (ictx->input_len == 0) { + oe = options_get(wp->window->options, "automatic-rename"); + if (oe != NULL) + options_remove(oe); + return; + } window_set_name(ictx->wp->window, ictx->input_buf); options_set_number(ictx->wp->window->options, "automatic-rename", 0); server_status_window(ictx->wp->window); @@ -2320,6 +2344,54 @@ input_top_bit_set(struct input_ctx *ictx) return (0); } +/* Parse colour from OSC. */ +static int +input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b) +{ + u_int rsize, gsize, bsize; + const char *cp, *s = p; + + if (sscanf(p, "rgb:%x/%x/%x", r, g, b) != 3) + return (0); + p += 4; + + cp = strchr(p, '/'); + rsize = cp - p; + if (rsize == 1) + (*r) = (*r) | ((*r) << 4); + else if (rsize == 3) + (*r) >>= 4; + else if (rsize == 4) + (*r) >>= 8; + else if (rsize != 2) + return (0); + + p = cp + 1; + cp = strchr(p, '/'); + gsize = cp - p; + if (gsize == 1) + (*g) = (*g) | ((*g) << 4); + else if (gsize == 3) + (*g) >>= 4; + else if (gsize == 4) + (*g) >>= 8; + else if (gsize != 2) + return (0); + + bsize = strlen(cp + 1); + if (bsize == 1) + (*b) = (*b) | ((*b) << 4); + else if (bsize == 3) + (*b) >>= 4; + else if (bsize == 4) + (*b) >>= 8; + else if (bsize != 2) + return (0); + + log_debug("%s: %s = %02x%02x%02x", __func__, s, *r, *g, *b); + return (1); +} + /* Handle the OSC 4 sequence for setting (multiple) palette entries. */ static void input_osc_4(struct input_ctx *ictx, const char *p) @@ -2338,7 +2410,7 @@ input_osc_4(struct input_ctx *ictx, const char *p) goto bad; s = strsep(&next, ";"); - if (sscanf(s, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) { + if (!input_osc_parse_colour(s, &r, &g, &b)) { s = next; continue; } @@ -2363,8 +2435,11 @@ input_osc_10(struct input_ctx *ictx, const char *p) u_int r, g, b; char tmp[16]; - if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) - goto bad; + if (strcmp(p, "?") == 0) + return; + + if (!input_osc_parse_colour(p, &r, &g, &b)) + goto bad; 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); @@ -2384,7 +2459,10 @@ input_osc_11(struct input_ctx *ictx, const char *p) u_int r, g, b; char tmp[16]; - if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) + if (strcmp(p, "?") == 0) + return; + + if (!input_osc_parse_colour(p, &r, &g, &b)) goto bad; xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x", r, g, b); options_set_style(wp->options, "window-style", 1, tmp); diff --git a/key-bindings.c b/key-bindings.c index ea793a2f..175af8f4 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -69,7 +69,6 @@ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ " '#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}" - static int key_bindings_cmp(struct key_binding *, struct key_binding *); RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp); static int key_table_cmp(struct key_table *, struct key_table *); @@ -403,6 +402,8 @@ key_bindings_init(void) "bind -Tcopy-mode C-Up send -X scroll-up", "bind -Tcopy-mode C-Down send -X scroll-down", + "bind -Tcopy-mode-vi '#' send -FX search-backward '#{copy_cursor_word}'", + "bind -Tcopy-mode-vi * send -FX search-forward '#{copy_cursor_word}'", "bind -Tcopy-mode-vi C-c send -X cancel", "bind -Tcopy-mode-vi C-d send -X halfpage-down", "bind -Tcopy-mode-vi C-e send -X scroll-down", @@ -509,12 +510,16 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, struct cmdq_item *new_item; int readonly; - readonly = 1; - TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { - if (!(cmd->entry->flags & CMD_READONLY)) - readonly = 0; + if (c == NULL || (~c->flags & CLIENT_READONLY)) + readonly = 1; + else { + readonly = 1; + TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { + if (~cmd->entry->flags & CMD_READONLY) + readonly = 0; + } } - if (!readonly && (c->flags & CLIENT_READONLY)) + if (!readonly) new_item = cmdq_get_callback(key_bindings_read_only, NULL); else { new_item = cmdq_get_command(bd->cmdlist, fs, m, 0); diff --git a/key-string.c b/key-string.c index a1ef4f51..0505623e 100644 --- a/key-string.c +++ b/key-string.c @@ -159,7 +159,7 @@ key_string_get_modifiers(const char **string) key_code key_string_lookup_string(const char *string) { - static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; + static const char *other = "!#()+,-.0123456789:;<=>'\r\t"; key_code key; u_int u; key_code modifiers; @@ -196,7 +196,7 @@ key_string_lookup_string(const char *string) /* Is this a standard ASCII key? */ if (string[1] == '\0' && (u_char)string[0] <= 127) { key = (u_char)string[0]; - if (key < 32 || key == 127) + if (key < 32) return (KEYC_UNKNOWN); } else { /* Try as a UTF-8 key. */ @@ -226,6 +226,8 @@ key_string_lookup_string(const char *string) key -= 64; else if (key == 32) key = 0; + else if (key == '?') + key = 127; else if (key == 63) key = KEYC_BSPACE; else @@ -329,7 +331,7 @@ key_string_lookup_key(key_code key) } /* Invalid keys are errors. */ - if (key == 127 || key > 255) { + if (key > 255) { snprintf(out, sizeof out, "Invalid#%llx", key); return (out); } @@ -343,7 +345,9 @@ key_string_lookup_key(key_code key) } else if (key >= 32 && key <= 126) { tmp[0] = key; tmp[1] = '\0'; - } else if (key >= 128) + } else if (key == 127) + xsnprintf(tmp, sizeof tmp, "C-?"); + else if (key >= 128) xsnprintf(tmp, sizeof tmp, "\\%llo", key); strlcat(out, tmp, sizeof out); diff --git a/layout-custom.c b/layout-custom.c index d7371292..097dabe6 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -221,7 +221,7 @@ layout_parse(struct window *w, const char *layout) return (-1); /* Resize to the layout size. */ - window_resize(w, lc->sx, lc->sy); + window_resize(w, lc->sx, lc->sy, -1, -1); /* Destroy the old layout and swap to the new. */ layout_free_cell(w->layout_root); diff --git a/layout-set.c b/layout-set.c index 82247149..f712b059 100644 --- a/layout-set.c +++ b/layout-set.c @@ -163,7 +163,7 @@ layout_set_even(struct window *w, enum layout_type type) layout_print_cell(w->layout_root, __func__, 1); - window_resize(w, lc->sx, lc->sy); + window_resize(w, lc->sx, lc->sy, -1, -1); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -262,7 +262,7 @@ layout_set_main_h(struct window *w) layout_print_cell(w->layout_root, __func__, 1); - window_resize(w, lc->sx, lc->sy); + window_resize(w, lc->sx, lc->sy, -1, -1); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -349,7 +349,7 @@ layout_set_main_v(struct window *w) layout_print_cell(w->layout_root, __func__, 1); - window_resize(w, lc->sx, lc->sy); + window_resize(w, lc->sx, lc->sy, -1, -1); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -458,7 +458,7 @@ layout_set_tiled(struct window *w) layout_print_cell(w->layout_root, __func__, 1); - window_resize(w, lc->sx, lc->sy); + window_resize(w, lc->sx, lc->sy, -1, -1); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -130,6 +130,9 @@ log_debug(const char *msg, ...) { va_list ap; + if (log_file == NULL) + return; + va_start(ap, msg); log_vwrite(msg, ap); va_end(ap); diff --git a/mode-tree.c b/mode-tree.c index 03a91ef8..054989fb 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -39,7 +39,7 @@ struct mode_tree_data { const char **sort_list; u_int sort_size; - u_int sort_type; + struct mode_tree_sort_criteria sort_crit; mode_tree_build_cb buildcb; mode_tree_draw_cb drawcb; @@ -334,7 +334,6 @@ mode_tree_start(struct window_pane *wp, struct args *args, mtd->sort_list = sort_list; mtd->sort_size = sort_size; - mtd->sort_type = 0; mtd->preview = !args_has(args, 'N'); @@ -342,9 +341,10 @@ mode_tree_start(struct window_pane *wp, struct args *args, if (sort != NULL) { for (i = 0; i < sort_size; i++) { if (strcasecmp(sort, sort_list[i]) == 0) - mtd->sort_type = i; + mtd->sort_crit.field = i; } } + mtd->sort_crit.reversed = args_has(args, 'r'); if (args_has(args, 'f')) mtd->filter = xstrdup(args_get(args, 'f')); @@ -392,10 +392,10 @@ mode_tree_build(struct mode_tree_data *mtd) TAILQ_CONCAT(&mtd->saved, &mtd->children, entry); TAILQ_INIT(&mtd->children); - mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter); + mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter); mtd->no_matches = TAILQ_EMPTY(&mtd->children); if (mtd->no_matches) - mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL); + mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, NULL); mode_tree_free_items(&mtd->saved); TAILQ_INIT(&mtd->saved); @@ -634,8 +634,9 @@ mode_tree_draw(struct mode_tree_data *mtd) screen_write_cursormove(&ctx, 0, h, 0); screen_write_box(&ctx, w, sy - h); - xasprintf(&text, " %s (sort: %s)", mti->name, - mtd->sort_list[mtd->sort_type]); + xasprintf(&text, " %s (sort: %s%s)", mti->name, + mtd->sort_list[mtd->sort_crit.field], + mtd->sort_crit.reversed ? ", reversed" : ""); if (w - 2 >= strlen(text)) { screen_write_cursormove(&ctx, 1, h, 0); screen_write_puts(&ctx, &gc0, "%s", text); @@ -993,9 +994,13 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, } break; case 'O': - mtd->sort_type++; - if (mtd->sort_type == mtd->sort_size) - mtd->sort_type = 0; + mtd->sort_crit.field++; + if (mtd->sort_crit.field == mtd->sort_size) + mtd->sort_crit.field = 0; + mode_tree_build(mtd); + break; + case 'r': + mtd->sort_crit.reversed = !mtd->sort_crit.reversed; mode_tree_build(mtd); break; case KEYC_LEFT: diff --git a/options-table.c b/options-table.c index 67fe553a..be0d220d 100644 --- a/options-table.c +++ b/options-table.c @@ -63,13 +63,16 @@ static const char *options_table_set_clipboard_list[] = { "off", "external", "on", NULL }; static const char *options_table_window_size_list[] = { - "largest", "smallest", "manual", NULL + "largest", "smallest", "manual", "latest", NULL }; /* Status line format. */ #define OPTIONS_TABLE_STATUS_FORMAT1 \ "#[align=left range=left #{status-left-style}]" \ - "#{T;=/#{status-left-length}:status-left}#[norange default]" \ + "#[push-default]" \ + "#{T;=/#{status-left-length}:status-left}" \ + "#[pop-default]" \ + "#[norange default]" \ "#[list=on align=#{status-justify}]" \ "#[list=left-marker]<#[list=right-marker]>#[list=on]" \ "#{W:" \ @@ -91,7 +94,9 @@ static const char *options_table_window_size_list[] = { "}" \ "}" \ "]" \ + "#[push-default]" \ "#{T:window-status-format}" \ + "#[pop-default]" \ "#[norange default]" \ "#{?window_end_flag,,#{window-status-separator}}" \ "," \ @@ -116,12 +121,17 @@ static const char *options_table_window_size_list[] = { "}" \ "}" \ "]" \ + "#[push-default]" \ "#{T:window-status-current-format}" \ + "#[pop-default]" \ "#[norange list=on default]" \ "#{?window_end_flag,,#{window-status-separator}}" \ "}" \ "#[nolist align=right range=right #{status-right-style}]" \ - "#{T;=/#{status-right-length}:status-right}#[norange default]" + "#[push-default]" \ + "#{T;=/#{status-right-length}:status-right}" \ + "#[pop-default]" \ + "#[norange default]" #define OPTIONS_TABLE_STATUS_FORMAT2 \ "#[align=centre]#{P:#{?pane_active,#[reverse],}" \ "#{pane_index}[#{pane_width}x#{pane_height}]#[default] }" @@ -142,6 +152,12 @@ static const char *options_table_status_format_default[] = { /* Top-level options. */ const struct options_table_entry options_table[] = { /* Server options. */ + { .name = "backspace", + .type = OPTIONS_TABLE_KEY, + .scope = OPTIONS_TABLE_SERVER, + .default_num = '\177', + }, + { .name = "buffer-limit", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, @@ -719,7 +735,7 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_window_size_list, - .default_num = WINDOW_SIZE_SMALLEST + .default_num = WINDOW_SIZE_LATEST }, { .name = "window-style", @@ -296,6 +296,7 @@ options_remove(struct options_entry *o) else options_value_free(o, &o->value); RB_REMOVE(options_tree, &oo->tree, o); + free((void *)o->name); free(o); } @@ -320,6 +321,17 @@ options_array_item(struct options_entry *o, u_int idx) return (RB_FIND(options_array, &o->value.array, &a)); } +static struct options_array_item * +options_array_new(struct options_entry *o, u_int idx) +{ + struct options_array_item *a; + + a = xcalloc(1, sizeof *a); + a->index = idx; + RB_INSERT(options_array, &o->value.array, a); + return (a); +} + static void options_array_free(struct options_entry *o, struct options_array_item *a) { @@ -367,7 +379,14 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, return (-1); } - if (OPTIONS_IS_COMMAND(o) && value != NULL) { + if (value == NULL) { + a = options_array_item(o, idx); + if (a != NULL) + options_array_free(o, a); + return (0); + } + + if (OPTIONS_IS_COMMAND(o)) { pr = cmd_parse_from_string(value, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: @@ -383,34 +402,33 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, case CMD_PARSE_SUCCESS: break; } - } - a = options_array_item(o, idx); - if (value == NULL) { - if (a != NULL) - options_array_free(o, a); + a = options_array_item(o, idx); + if (a == NULL) + a = options_array_new(o, idx); + else + options_value_free(o, &a->value); + a->value.cmdlist = pr->cmdlist; return (0); } if (OPTIONS_IS_STRING(o)) { + a = options_array_item(o, idx); if (a != NULL && append) xasprintf(&new, "%s%s", a->value.string, value); else new = xstrdup(value); + if (a == NULL) + a = options_array_new(o, idx); + else + options_value_free(o, &a->value); + a->value.string = new; + return (0); } - if (a == NULL) { - a = xcalloc(1, sizeof *a); - a->index = idx; - RB_INSERT(options_array, &o->value.array, a); - } else - options_value_free(o, &a->value); - - if (OPTIONS_IS_STRING(o)) - a->value.string = new; - else if (OPTIONS_IS_COMMAND(o)) - a->value.cmdlist = pr->cmdlist; - return (0); + if (cause != NULL) + *cause = xstrdup("wrong array type"); + return (-1); } int @@ -107,6 +107,12 @@ regsub(const char *pattern, const char *with, const char *text, int flags) start += m[0].rm_eo + 1; empty = 1; } + + /* Stop now if anchored to start. */ + if (*pattern == '^') { + regsub_copy(&buf, &len, text, start, end); + break; + } } buf[len] = '\0'; @@ -23,7 +23,7 @@ #include "tmux.h" void -resize_window(struct window *w, u_int sx, u_int sy) +resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) { int zoomed; @@ -50,7 +50,7 @@ resize_window(struct window *w, u_int sx, u_int sy) sx = w->layout_root->sx; if (sy < w->layout_root->sy) sy = w->layout_root->sy; - window_resize(w, sx, sy); + window_resize(w, sx, sy, xpixel, ypixel); log_debug("%s: @%u resized to %u,%u; layout %u,%u", __func__, w->id, sx, sy, w->layout_root->sx, w->layout_root->sy); @@ -76,58 +76,118 @@ ignore_client_size(struct client *c) } void -default_window_size(struct session *s, struct window *w, u_int *sx, u_int *sy, - int type) +default_window_size(struct client *c, struct session *s, struct window *w, + u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type) { - struct client *c; - u_int cx, cy; + struct client *loop; + u_int cx, cy, n; const char *value; if (type == -1) type = options_get_number(global_w_options, "window-size"); - if (type == WINDOW_SIZE_MANUAL) - goto manual; - - if (type == WINDOW_SIZE_LARGEST) { + switch (type) { + case WINDOW_SIZE_LARGEST: *sx = *sy = 0; - TAILQ_FOREACH(c, &clients, entry) { - if (ignore_client_size(c)) + *xpixel = *ypixel = 0; + TAILQ_FOREACH(loop, &clients, entry) { + if (ignore_client_size(loop)) continue; - if (w != NULL && !session_has(c->session, w)) + if (w != NULL && !session_has(loop->session, w)) continue; - if (w == NULL && c->session != s) + if (w == NULL && loop->session != s) continue; - cx = c->tty.sx; - cy = c->tty.sy - status_line_size(c); + cx = loop->tty.sx; + cy = loop->tty.sy - status_line_size(loop); if (cx > *sx) *sx = cx; if (cy > *sy) *sy = cy; + + if (loop->tty.xpixel > *xpixel && + loop->tty.ypixel > *ypixel) { + *xpixel = loop->tty.xpixel; + *ypixel = loop->tty.ypixel; + } } if (*sx == 0 || *sy == 0) goto manual; - } else { + break; + case WINDOW_SIZE_SMALLEST: *sx = *sy = UINT_MAX; - TAILQ_FOREACH(c, &clients, entry) { - if (ignore_client_size(c)) + *xpixel = *ypixel = 0; + TAILQ_FOREACH(loop, &clients, entry) { + if (ignore_client_size(loop)) continue; - if (w != NULL && !session_has(c->session, w)) + if (w != NULL && !session_has(loop->session, w)) continue; - if (w == NULL && c->session != s) + if (w == NULL && loop->session != s) continue; - cx = c->tty.sx; - cy = c->tty.sy - status_line_size(c); + cx = loop->tty.sx; + cy = loop->tty.sy - status_line_size(loop); if (cx < *sx) *sx = cx; if (cy < *sy) *sy = cy; + + if (loop->tty.xpixel > *xpixel && + loop->tty.ypixel > *ypixel) { + *xpixel = loop->tty.xpixel; + *ypixel = loop->tty.ypixel; + } } if (*sx == UINT_MAX || *sy == UINT_MAX) goto manual; + break; + case WINDOW_SIZE_LATEST: + if (c != NULL && !ignore_client_size(c)) { + *sx = c->tty.sx; + *sy = c->tty.sy - status_line_size(c); + *xpixel = c->tty.xpixel; + *ypixel = c->tty.ypixel; + } else { + if (w == NULL) + goto manual; + n = 0; + TAILQ_FOREACH(loop, &clients, entry) { + if (!ignore_client_size(loop) && + session_has(loop->session, w)) { + if (++n > 1) + break; + } + } + *sx = *sy = UINT_MAX; + *xpixel = *ypixel = 0; + TAILQ_FOREACH(loop, &clients, entry) { + if (ignore_client_size(loop)) + continue; + if (n > 1 && loop != w->latest) + continue; + s = loop->session; + + cx = loop->tty.sx; + cy = loop->tty.sy - status_line_size(loop); + + if (cx < *sx) + *sx = cx; + if (cy < *sy) + *sy = cy; + + if (loop->tty.xpixel > *xpixel && + loop->tty.ypixel > *ypixel) { + *xpixel = loop->tty.xpixel; + *ypixel = loop->tty.ypixel; + } + } + if (*sx == UINT_MAX || *sy == UINT_MAX) + goto manual; + } + break; + case WINDOW_SIZE_MANUAL: + goto manual; } goto done; @@ -150,13 +210,144 @@ done: } void +recalculate_size(struct window *w) +{ + struct session *s; + struct client *c; + u_int sx, sy, cx, cy, xpixel = 0, ypixel = 0, n; + int type, current, has, changed; + + if (w->active == NULL) + return; + log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy); + + type = options_get_number(w->options, "window-size"); + current = options_get_number(w->options, "aggressive-resize"); + + changed = 1; + switch (type) { + case WINDOW_SIZE_LARGEST: + sx = sy = 0; + TAILQ_FOREACH(c, &clients, entry) { + if (ignore_client_size(c)) + continue; + s = c->session; + + if (current) + has = (s->curw->window == w); + else + has = session_has(s, w); + if (!has) + continue; + + cx = c->tty.sx; + cy = c->tty.sy - status_line_size(c); + + if (cx > sx) + sx = cx; + if (cy > sy) + sy = cy; + + if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) { + xpixel = c->tty.xpixel; + ypixel = c->tty.ypixel; + } + } + if (sx == 0 || sy == 0) + changed = 0; + break; + case WINDOW_SIZE_SMALLEST: + sx = sy = UINT_MAX; + TAILQ_FOREACH(c, &clients, entry) { + if (ignore_client_size(c)) + continue; + s = c->session; + + if (current) + has = (s->curw->window == w); + else + has = session_has(s, w); + if (!has) + continue; + + cx = c->tty.sx; + cy = c->tty.sy - status_line_size(c); + + if (cx < sx) + sx = cx; + if (cy < sy) + sy = cy; + + if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) { + xpixel = c->tty.xpixel; + ypixel = c->tty.ypixel; + } + } + if (sx == UINT_MAX || sy == UINT_MAX) + changed = 0; + break; + case WINDOW_SIZE_LATEST: + n = 0; + TAILQ_FOREACH(c, &clients, entry) { + if (!ignore_client_size(c) && + session_has(c->session, w)) { + if (++n > 1) + break; + } + } + sx = sy = UINT_MAX; + TAILQ_FOREACH(c, &clients, entry) { + if (ignore_client_size(c)) + continue; + if (n > 1 && c != w->latest) + continue; + s = c->session; + + if (current) + has = (s->curw->window == w); + else + has = session_has(s, w); + if (!has) + continue; + + cx = c->tty.sx; + cy = c->tty.sy - status_line_size(c); + + if (cx < sx) + sx = cx; + if (cy < sy) + sy = cy; + + if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) { + xpixel = c->tty.xpixel; + ypixel = c->tty.ypixel; + } + } + if (sx == UINT_MAX || sy == UINT_MAX) + changed = 0; + break; + case WINDOW_SIZE_MANUAL: + changed = 0; + break; + } + if (changed && w->sx == sx && w->sy == sy) + changed = 0; + + if (!changed) { + tty_update_window_offset(w); + return; + } + log_debug("%s: @%u changed to %u,%u (%ux%u)", __func__, w->id, sx, sy, + xpixel, ypixel); + resize_window(w, sx, sy, xpixel, ypixel); +} + +void recalculate_sizes(void) { struct session *s; struct client *c; struct window *w; - u_int sx, sy, cx, cy; - int type, current, has, changed; /* * Clear attached count and update saved status line information for @@ -183,74 +374,6 @@ recalculate_sizes(void) } /* Walk each window and adjust the size. */ - RB_FOREACH(w, windows, &windows) { - if (w->active == NULL) - continue; - log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy); - - type = options_get_number(w->options, "window-size"); - if (type == WINDOW_SIZE_MANUAL) - continue; - current = options_get_number(w->options, "aggressive-resize"); - - changed = 1; - if (type == WINDOW_SIZE_LARGEST) { - sx = sy = 0; - TAILQ_FOREACH(c, &clients, entry) { - if (ignore_client_size(c)) - continue; - s = c->session; - - if (current) - has = (s->curw->window == w); - else - has = session_has(s, w); - if (!has) - continue; - - cx = c->tty.sx; - cy = c->tty.sy - status_line_size(c); - - if (cx > sx) - sx = cx; - if (cy > sy) - sy = cy; - } - if (sx == 0 || sy == 0) - changed = 0; - } else { - sx = sy = UINT_MAX; - TAILQ_FOREACH(c, &clients, entry) { - if (ignore_client_size(c)) - continue; - s = c->session; - - if (current) - has = (s->curw->window == w); - else - has = session_has(s, w); - if (!has) - continue; - - cx = c->tty.sx; - cy = c->tty.sy - status_line_size(c); - - if (cx < sx) - sx = cx; - if (cy < sy) - sy = cy; - } - if (sx == UINT_MAX || sy == UINT_MAX) - changed = 0; - } - if (w->sx == sx && w->sy == sy) - changed = 0; - - if (!changed) { - tty_update_window_offset(w); - continue; - } - log_debug("%s: @%u changed to %u,%u", __func__, w->id, sx, sy); - resize_window(w, sx, sy); - } + RB_FOREACH(w, windows, &windows) + recalculate_size(w); } diff --git a/screen-write.c b/screen-write.c index 66053eaf..43cb42b4 100644 --- a/screen-write.c +++ b/screen-write.c @@ -100,7 +100,6 @@ void screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) { - char tmp[32]; u_int y; memset(ctx, 0, sizeof *ctx); @@ -119,12 +118,17 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, ctx->scrolled = 0; ctx->bg = 8; - if (wp != NULL) { - snprintf(tmp, sizeof tmp, "pane %%%u (at %u,%u)", wp->id, - wp->xoff, wp->yoff); + if (log_get_level() != 0) { + if (wp != NULL) { + log_debug("%s: size %ux%u, pane %%%u (at %u,%u)", + __func__, screen_size_x(ctx->s), + screen_size_y(ctx->s), wp->id, wp->xoff, wp->yoff); + } else { + log_debug("%s: size %ux%u, no pane", + __func__, screen_size_x(ctx->s), + screen_size_y(ctx->s)); + } } - log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s), - screen_size_y(ctx->s), wp == NULL ? "no pane" : tmp); } /* Finish writing. */ @@ -1234,7 +1238,6 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx) for (y = s->rupper; y < s->rlower; y++) { cl = &ctx->list[y + 1]; TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); - TAILQ_INIT(&cl->items); } } @@ -1323,8 +1326,7 @@ screen_write_collect_end(struct screen_write_ctx *ctx) } } - memcpy(&gc, &ci->gc, sizeof gc); - grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used); + grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, ci->data, ci->used); screen_write_set_cursor(ctx, s->cx + ci->used, -1); for (xx = s->cx; xx < screen_size_x(s); xx++) { @@ -1348,8 +1350,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx, /* * Don't need to check that the attributes and whatnot are still the * same - input_parse will end the collection when anything that isn't - * a plain character is encountered. Also nothing should make it here - * that isn't a single ASCII character. + * a plain character is encountered. */ collect = 1; @@ -1635,7 +1636,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, grid_view_get_cell(gd, xx, s->cy, &tmp_gc); if (~tmp_gc.flags & GRID_FLAG_PADDING) break; - log_debug("%s: overwrite at %u,%u", __func__, xx, s->cy); + log_debug("%s: overwrite at %u,%u", __func__, xx, + s->cy); grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); done = 1; } @@ -158,6 +158,14 @@ screen_set_title(struct screen *s, const char *title) utf8_stravis(&s->title, title, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); } +/* Set screen path. */ +void +screen_set_path(struct screen *s, const char *path) +{ + free(s->path); + utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); +} + /* Push the current title onto the stack. */ void screen_push_title(struct screen *s) diff --git a/server-client.c b/server-client.c index f44631c9..2cfd8838 100644 --- a/server-client.c +++ b/server-client.c @@ -42,6 +42,7 @@ static void server_client_set_title(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); static void server_client_clear_overlay(struct client *); +static void server_client_resize_event(int, short, void *); static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch_command(struct client *, struct imsg *); @@ -540,7 +541,8 @@ have_event: where = STATUS_RIGHT; break; case STYLE_RANGE_WINDOW: - wl = winlink_find_by_index(&s->windows, sr->argument); + wl = winlink_find_by_index(&s->windows, + sr->argument); if (wl == NULL) return (KEYC_UNKNOWN); m->w = wl->window->id; @@ -993,6 +995,24 @@ server_client_assume_paste(struct session *s) return (0); } +/* Has the latest client changed? */ +static void +server_client_update_latest(struct client *c) +{ + struct window *w; + + if (c->session == NULL) + return; + w = c->session->curw->window; + + if (w->latest == c) + return; + w->latest = c; + + if (options_get_number(w->options, "window-size") == WINDOW_SIZE_LATEST) + recalculate_size(w); +} + /* * Handle data key input from client. This owns and can modify the key event it * is given and is responsible for freeing it. @@ -1189,6 +1209,8 @@ forward_key: window_pane_key(wp, c, s, wl, key, m); out: + if (s != NULL) + server_client_update_latest(c); free(event); return (CMD_RETURN_NORMAL); } @@ -1245,7 +1267,7 @@ server_client_loop(void) struct window_pane *wp; struct winlink *wl; struct session *s; - int focus; + int focus, attached, resize; TAILQ_FOREACH(c, &clients, entry) { server_client_check_exit(c); @@ -1258,19 +1280,33 @@ server_client_loop(void) /* * Any windows will have been redrawn as part of clients, so clear * their flags now. Also check pane focus and resize. + * + * As an optimization, panes in windows that are in an attached session + * but not the current window are not resized (this reduces the amount + * of work needed when, for example, resizing an X terminal a + * lot). Windows in no attached session are resized immediately since + * that is likely to have come from a command like split-window and be + * what the user wanted. */ focus = options_get_number(global_options, "focus-events"); RB_FOREACH(w, windows, &windows) { + attached = resize = 0; TAILQ_FOREACH(wl, &w->winlinks, wentry) { s = wl->session; - if (s->attached != 0 && s->curw == wl) + if (s->attached != 0) + attached = 1; + if (s->attached != 0 && s->curw == wl) { + resize = 1; break; + } } + if (!attached) + resize = 1; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { if (focus) server_client_check_focus(wp); - if (wl != NULL) + if (resize) server_client_check_resize(wp); } wp->flags &= ~PANE_REDRAW; @@ -1284,7 +1320,6 @@ static int server_client_resize_force(struct window_pane *wp) { struct timeval tv = { .tv_usec = 100000 }; - struct winsize ws; /* * If we are resizing to the same size as when we entered the loop @@ -1305,86 +1340,76 @@ server_client_resize_force(struct window_pane *wp) wp->sy <= 1) return (0); - memset(&ws, 0, sizeof ws); - ws.ws_col = wp->sx; - ws.ws_row = wp->sy - 1; - if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) -#ifdef __sun - if (errno != EINVAL && errno != ENXIO) -#endif - fatal("ioctl failed"); log_debug("%s: %%%u forcing resize", __func__, wp->id); + window_pane_send_resize(wp, -1); evtimer_add(&wp->resize_timer, &tv); wp->flags |= PANE_RESIZEFORCE; return (1); } +/* Resize a pane. */ +static void +server_client_resize_pane(struct window_pane *wp) +{ + log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); + window_pane_send_resize(wp, 0); + + wp->flags &= ~PANE_RESIZE; + + wp->osx = wp->sx; + wp->osy = wp->sy; +} + +/* Start the resize timer. */ +static void +server_client_start_resize_timer(struct window_pane *wp) +{ + struct timeval tv = { .tv_usec = 250000 }; + + if (!evtimer_pending(&wp->resize_timer, NULL)) + evtimer_add(&wp->resize_timer, &tv); +} + /* Resize timer event. */ static void server_client_resize_event(__unused int fd, __unused short events, void *data) { struct window_pane *wp = data; - struct winsize ws; evtimer_del(&wp->resize_timer); - if (!(wp->flags & PANE_RESIZE)) + if (~wp->flags & PANE_RESIZE) return; - if (server_client_resize_force(wp)) - return; - - memset(&ws, 0, sizeof ws); - ws.ws_col = wp->sx; - ws.ws_row = wp->sy; - if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) -#ifdef __sun - /* - * Some versions of Solaris apparently can return an error when - * resizing; don't know why this happens, can't reproduce on - * other platforms and ignoring it doesn't seem to cause any - * issues. - */ - if (errno != EINVAL && errno != ENXIO) -#endif - fatal("ioctl failed"); - log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); - - wp->flags &= ~PANE_RESIZE; - - wp->osx = wp->sx; - wp->osy = wp->sy; + log_debug("%s: %%%u timer fired (was%s resized)", __func__, wp->id, + (wp->flags & PANE_RESIZED) ? "" : " not"); + + if (wp->saved_grid == NULL && (wp->flags & PANE_RESIZED)) { + log_debug("%s: %%%u deferring timer", __func__, wp->id); + server_client_start_resize_timer(wp); + } else if (!server_client_resize_force(wp)) { + log_debug("%s: %%%u resizing pane", __func__, wp->id); + server_client_resize_pane(wp); + } + wp->flags &= ~PANE_RESIZED; } /* Check if pane should be resized. */ static void server_client_check_resize(struct window_pane *wp) { - struct timeval tv = { .tv_usec = 250000 }; - - if (!(wp->flags & PANE_RESIZE)) + if (~wp->flags & PANE_RESIZE) return; - log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); if (!event_initialized(&wp->resize_timer)) evtimer_set(&wp->resize_timer, server_client_resize_event, wp); - /* - * The first resize should happen immediately, so if the timer is not - * running, do it now. - */ - if (!evtimer_pending(&wp->resize_timer, NULL)) - server_client_resize_event(-1, 0, wp); - - /* - * If the pane is in the alternate screen, let the timer expire and - * resize to give the application a chance to redraw. If not, keep - * pushing the timer back. - */ - if (wp->saved_grid != NULL && evtimer_pending(&wp->resize_timer, NULL)) - return; - evtimer_del(&wp->resize_timer); - evtimer_add(&wp->resize_timer, &tv); + if (!evtimer_pending(&wp->resize_timer, NULL)) { + log_debug("%s: %%%u starting timer", __func__, wp->id); + server_client_resize_pane(wp); + server_client_start_resize_timer(wp); + } else + log_debug("%s: %%%u timer running", __func__, wp->id); } /* Check whether pane should be focused. */ @@ -1734,6 +1759,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) if (c->flags & CLIENT_CONTROL) break; + server_client_update_latest(c); server_client_clear_overlay(c); tty_resize(&c->tty); recalculate_sizes(); @@ -83,7 +83,7 @@ spawn_window(struct spawn_context *sc, char **cause) struct window_pane *wp; struct winlink *wl; int idx = sc->idx; - u_int sx, sy; + u_int sx, sy, xpixel, ypixel; spawn_log(__func__, sc); @@ -153,8 +153,9 @@ spawn_window(struct spawn_context *sc, char **cause) xasprintf(cause, "couldn't add window %d", idx); return (NULL); } - default_window_size(s, NULL, &sx, &sy, -1); - if ((w = window_create(sx, sy)) == NULL) { + default_window_size(sc->c, s, NULL, &sx, &sy, &xpixel, &ypixel, + -1); + if ((w = window_create(sx, sy, xpixel, ypixel)) == NULL) { winlink_remove(&s->windows, sc->wl); xasprintf(cause, "couldn't create window %d", idx); return (NULL); @@ -162,6 +163,7 @@ spawn_window(struct spawn_context *sc, char **cause) if (s->curw == NULL) s->curw = sc->wl; sc->wl->session = s; + w->latest = sc->c; winlink_set_window(sc->wl, w); } else w = NULL; @@ -214,6 +216,7 @@ spawn_pane(struct spawn_context *sc, char **cause) u_int hlimit; struct winsize ws; sigset_t set, oldset; + key_code key; spawn_log(__func__, sc); @@ -334,6 +337,8 @@ spawn_pane(struct spawn_context *sc, char **cause) memset(&ws, 0, sizeof ws); ws.ws_col = screen_size_x(&new_wp->base); ws.ws_row = screen_size_y(&new_wp->base); + ws.ws_xpixel = w->xpixel * ws.ws_col; + ws.ws_ypixel = w->ypixel * ws.ws_row; /* Block signals until fork has completed. */ sigfillset(&set); @@ -375,13 +380,17 @@ spawn_pane(struct spawn_context *sc, char **cause) /* * Update terminal escape characters from the session if available and - * force VERASE to tmux's \177. + * force VERASE to tmux's backspace. */ if (tcgetattr(STDIN_FILENO, &now) != 0) _exit(1); if (s->tio != NULL) memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc); - now.c_cc[VERASE] = '\177'; + key = options_get_number(global_options, "backspace"); + if (key >= 0x7f) + now.c_cc[VERASE] = '\177'; + else + now.c_cc[VERASE] = key; if (tcsetattr(STDIN_FILENO, TCSANOW, &now) != 0) _exit(1); @@ -36,13 +36,15 @@ static struct style style_default = { STYLE_ALIGN_DEFAULT, STYLE_LIST_OFF, - STYLE_RANGE_NONE, 0 + STYLE_RANGE_NONE, 0, + + STYLE_DEFAULT_BASE }; /* - * Parse an embedded style of the form "fg=colour,bg=colour,bright,...". - * Note that this adds onto the given style, so it must have been initialized - * alredy. + * Parse an embedded style of the form "fg=colour,bg=colour,bright,...". Note + * that this adds onto the given style, so it must have been initialized + * already. */ int style_parse(struct style *sy, const struct grid_cell *base, const char *in) @@ -74,7 +76,11 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->gc.bg = base->bg; sy->gc.attr = base->attr; sy->gc.flags = base->flags; - } else if (strcasecmp(tmp, "nolist") == 0) + } else if (strcasecmp(tmp, "push-default") == 0) + sy->default_type = STYLE_DEFAULT_PUSH; + else if (strcasecmp(tmp, "pop-default") == 0) + sy->default_type = STYLE_DEFAULT_POP; + else if (strcasecmp(tmp, "nolist") == 0) sy->list = STYLE_LIST_OFF; else if (strncasecmp(tmp, "list=", 5) == 0) { if (strcasecmp(tmp + 5, "on") == 0) @@ -218,6 +224,14 @@ style_tostring(struct style *sy) tmp); comma = ","; } + if (sy->default_type != STYLE_DEFAULT_BASE) { + if (sy->default_type == STYLE_DEFAULT_PUSH) + tmp = "push-default"; + else if (sy->default_type == STYLE_DEFAULT_POP) + tmp = "pop-default"; + off += xsnprintf(s + off, sizeof s - off, "%s%s", comma, tmp); + comma = ","; + } if (sy->fill != 8) { off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma, colour_tostring(sy->fill)); @@ -257,21 +271,6 @@ style_apply(struct grid_cell *gc, struct options *oo, const char *name) gc->attr |= sy->gc.attr; } -/* Apply a style, updating if default. */ -void -style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) -{ - struct style *sy; - - sy = options_get_style(oo, name); - if (sy->gc.fg != 8) - gc->fg = sy->gc.fg; - if (sy->gc.bg != 8) - gc->bg = sy->gc.bg; - if (sy->gc.attr != 0) - gc->attr |= sy->gc.attr; -} - /* Initialize style from cell. */ void style_set(struct style *sy, const struct grid_cell *gc) @@ -296,6 +296,12 @@ Prompt to search for text in open windows. Display some information about the current window. .It l Move to the previously selected window. +.It m +Mark the current pane (see +.Ic select-pane +.Fl m ) . +.It M +Clear the marked pane. .It n Change to the next window. .It o @@ -306,12 +312,6 @@ Change to the previous window. Briefly display pane indexes. .It r Force redraw of the attached client. -.It m -Mark the current pane (see -.Ic select-pane -.Fl m ) . -.It M -Clear the marked pane. .It s Select a new session for the attached client interactively. .It t @@ -1156,7 +1156,7 @@ The .Fl P option prints information about the new session after it has been created. By default, it uses the format -.Ql #{session_name}: +.Ql #{session_name}:\& but a different format may be specified with .Fl F . .Pp @@ -1296,7 +1296,7 @@ Suspend a client by sending .Dv SIGTSTP (tty stop). .It Xo Ic switch-client -.Op Fl Elnpr +.Op Fl ElnprZ .Op Fl c Ar target-client .Op Fl t Ar target-session .Op Fl T Ar key-table @@ -1313,7 +1313,10 @@ may refer to a pane (a target that contains .Ql \&. or .Ql % ) , -in which case the session, window and pane are all changed. +to change session, window and pane. +In that case, +.Fl Z +keeps the window zoomed if it was zoomed. If .Fl l , .Fl n @@ -1349,11 +1352,41 @@ bind-key -Troot a switch-client -Ttable1 .Ed .El .Sh WINDOWS AND PANES -A +Each window displayed by .Nm -window may be in one of two modes. -The default permits direct access to the terminal attached to the window. -The other is copy mode, which permits a section of a window or its +may be split into one or more +.Em panes ; +each pane takes up a certain area of the display and is a separate terminal. +A window may be split into panes using the +.Ic split-window +command. +Windows may be split horizontally (with the +.Fl h +flag) or vertically. +Panes may be resized with the +.Ic resize-pane +command (bound to +.Ql C-Up , +.Ql C-Down +.Ql C-Left +and +.Ql C-Right +by default), the current pane may be changed with the +.Ic select-pane +command and the +.Ic rotate-window +and +.Ic swap-pane +commands may be used to swap panes without changing their position. +Panes are numbered beginning from zero in the order they are created. +.Pp +By default, a +.Nm +pane permits direct access to the terminal contained in the pane. +A pane may also be put into one of several modes: +.Bl -dash -offset indent +.It +Copy mode, which permits a section of a window or its history to be copied to a .Em paste buffer for later insertion into another window. @@ -1362,9 +1395,21 @@ This mode is entered with the command, bound to .Ql \&[ by default. -It is also entered when a command that produces output, such as +.It +View mode, which is like copy mode but is entered when a command that produces +output, such as .Ic list-keys , is executed from a key binding. +.It +Choose mode, which allows an item to be chosen from a list. +This may be a client, a session or window or pane, or a buffer. +This mode is entered with the +.Ic choose-buffer , +.Ic choose-client +and +.Ic choose-tree +commands. +.El .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. @@ -1405,6 +1450,7 @@ The following commands are supported in copy mode: .It Li "copy-selection-no-clear [<prefix>]" Ta "" Ta "" .It Li "copy-selection-and-cancel [<prefix>]" Ta "Enter" Ta "M-w" .It Li "cursor-down" Ta "j" Ta "Down" +.It Li "cursor-down-and-cancel" Ta "" Ta "" .It Li "cursor-left" Ta "h" Ta "Left" .It Li "cursor-right" Ta "l" Ta "Right" .It Li "cursor-up" Ta "k" Ta "Up" @@ -1526,37 +1572,7 @@ bind PageUp copy-mode -eu .Ed .El .Pp -Each window displayed by -.Nm -may be split into one or more -.Em panes ; -each pane takes up a certain area of the display and is a separate terminal. -A window may be split into panes using the -.Ic split-window -command. -Windows may be split horizontally (with the -.Fl h -flag) or vertically. -Panes may be resized with the -.Ic resize-pane -command (bound to -.Ql C-Up , -.Ql C-Down -.Ql C-Left -and -.Ql C-Right -by default), the current pane may be changed with the -.Ic select-pane -command and the -.Ic rotate-window -and -.Ic swap-pane -commands may be used to swap panes without changing their position. -Panes are numbered beginning from zero in the order they are created. -.Pp -A number of preset -.Em layouts -are available. +A number of preset arrangements of panes are available, these are called layouts. These may be selected with the .Ic select-layout command or cycled with @@ -1635,7 +1651,7 @@ By default, it uses the format but a different format may be specified with .Fl F . .It Xo Ic capture-pane -.Op Fl aepPqCJ +.Op Fl aepPqCJN .Op Fl b Ar buffer-name .Op Fl E Ar end-line .Op Fl S Ar start-line @@ -1660,8 +1676,10 @@ is given, the output includes escape sequences for text and background attributes. .Fl C also escapes non-printable characters as octal \exxx. +.Fl N +preserves trailing spaces at each line's end and .Fl J -joins wrapped lines and preserves trailing spaces at each line's end. +preserves trailing spaces and joins any wrapped lines. .Fl P captures only any output that the pane has received that is the beginning of an as-yet incomplete escape sequence. @@ -1680,7 +1698,7 @@ the end of the visible pane. The default is to capture only the visible contents of the pane. .It Xo .Ic choose-client -.Op Fl NZ +.Op Fl NrZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl O Ar sort-order @@ -1709,7 +1727,8 @@ The following keys may be used in client mode: .It Li "z" Ta "Suspend selected client" .It Li "Z" Ta "Suspend tagged clients" .It Li "f" Ta "Enter a format to filter items" -.It Li "O" Ta "Change sort order" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" .El @@ -1724,12 +1743,14 @@ If is not given, "detach-client -t '%%'" is used. .Pp .Fl O -specifies the initial sort order: one of +specifies the initial sort field: one of .Ql name , .Ql size , .Ql creation , or .Ql activity . +.Fl r +reverses the sort order. .Fl f specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. @@ -1741,7 +1762,7 @@ starts without the preview. This command works only if at least one client is attached. .It Xo .Ic choose-tree -.Op Fl GNswZ +.Op Fl GNrswZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl O Ar sort-order @@ -1773,7 +1794,8 @@ The following keys may be used in tree mode: .It Li "C-t" Ta "Tag all items" .It Li "\&:" Ta "Run a command for each tagged item" .It Li "f" Ta "Enter a format to filter items" -.It Li "O" Ta "Change sort order" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" .El @@ -1788,11 +1810,13 @@ If is not given, "switch-client -t '%%'" is used. .Pp .Fl O -specifies the initial sort order: one of +specifies the initial sort field: one of .Ql index , .Ql name , or .Ql time . +.Fl r +reverses the sort order. .Fl f specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. @@ -1871,10 +1895,8 @@ zooms the pane. .Pp This command works only if at least one client is attached. .It Xo Ic join-pane -.Op Fl bdhv -.Oo Fl l -.Ar size | -.Fl p Ar percentage Oc +.Op Fl bdfhv +.Op Fl l Ar size .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc @@ -1925,11 +1947,13 @@ The option kills all but the window given with .Fl t . .It Xo Ic last-pane -.Op Fl de +.Op Fl deZ .Op Fl t Ar target-window .Xc .D1 (alias: Ic lastp ) Select the last (previously selected) pane. +.Fl Z +keeps the window zoomed if it was zoomed. .Fl e enables or .Fl d @@ -2009,9 +2033,7 @@ flag, see the section. .It Xo Ic move-pane .Op Fl bdhv -.Oo Fl l -.Ar size | -.Fl p Ar percentage Oc +.Op Fl l Ar size .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc @@ -2220,8 +2242,14 @@ or .Fl y . The .Ar adjustment -is given in lines or cells (the default is 1). -.Pp +is given in lines or columns (the default is 1); +.Fl x +and +.Fl y +may be a given as a number of lines or columns or followed by +.Ql % +for a percentage of the window size (for example +.Ql -x 10% ) . With .Fl Z , the active pane is toggled between zoomed (occupying the whole of the window) @@ -2311,7 +2339,7 @@ option has the same meaning as for the .Ic new-window command. .It Xo Ic rotate-window -.Op Fl DU +.Op Fl DUZ .Op Fl t Ar target-window .Xc .D1 (alias: Ic rotatew ) @@ -2319,6 +2347,8 @@ Rotate the positions of the panes within a window, either upward (numerically lower) with .Fl U or downward (numerically higher). +.Fl Z +keeps the window zoomed if it was zoomed. .It Xo Ic select-layout .Op Fl Enop .Op Fl t Ar target-pane @@ -2342,7 +2372,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 DdeLlMmRU +.Op Fl DdeLlMmRUZ .Op Fl T Ar title .Op Fl t Ar target-pane .Xc @@ -2359,6 +2389,8 @@ or .Fl U is used, respectively the pane below, to the left, to the right, or above the target pane is used. +.Fl Z +keeps the window zoomed if it was zoomed. .Fl l is the same as using the .Ic last-pane @@ -2409,9 +2441,7 @@ the command behaves like .Op Fl bdfhIvP .Op Fl c Ar start-directory .Op Fl e Ar environment -.Oo Fl l -.Ar size | -.Fl p Ar percentage Oc +.Op Fl l Ar size .Op Fl t Ar target-pane .Op Ar shell-command .Op Fl F Ar format @@ -2427,10 +2457,12 @@ a vertical split; if neither is specified, is assumed. The .Fl l -and -.Fl p -options specify the size of the new pane in lines (for vertical split) or in -cells (for horizontal split), or as a percentage, respectively. +option specifies the size of the new pane in lines (for vertical split) or in +columns (for horizontal split); +.Ar size +may be followed by +.Ql % +to specify a percentage of the available space. The .Fl b option causes the new pane to be created to the left of or above @@ -2464,7 +2496,7 @@ All other options have the same meaning as for the .Ic new-window command. .It Xo Ic swap-pane -.Op Fl dDU +.Op Fl dDUZ .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc @@ -2481,7 +2513,9 @@ swaps with the next pane (after it numerically). .Fl d instructs .Nm -not to change the active pane. +not to change the active pane and +.Fl Z +keeps the window zoomed if it was zoomed. .Pp If .Fl s @@ -2571,6 +2605,10 @@ bind-key '"' split-window bind-key "'" new-window .Ed .Pp +A command bound to the +.Em Any +key will execute for all keys which do not have a more specific binding. +.Pp Commands related to key bindings are as follows: .Bl -tag -width Ds .It Xo Ic bind-key @@ -2639,7 +2677,7 @@ With only .Ar key-table . .It Xo Ic send-keys -.Op Fl HlMRX +.Op Fl FHlMRX .Op Fl N Ar repeat-count .Op Fl t Ar target-pane .Ar key Ar ... @@ -2678,7 +2716,9 @@ the .Sx WINDOWS AND PANES section. .Fl N -specifies a repeat count. +specifies a repeat count and +.Fl F +expands formats in arguments where appropriate. .It Xo Ic send-prefix .Op Fl 2 .Op Fl t Ar target-pane @@ -2716,7 +2756,7 @@ and .Pp The .Nm -server has a set of global options which do not apply to any particular +server has a set of global server options which do not apply to any particular window or session or pane. These are altered with the .Ic set-option @@ -2890,6 +2930,10 @@ omitted to toggle). .Pp Available server options are: .Bl -tag -width Ds +.It Ic backspace Ar key +Set the key sent by +.Nm +for backspace. .It Ic buffer-limit Ar number Set the number of buffers; as new buffers are added to the top of the stack, old ones are removed from the bottom if necessary to maintain this maximum @@ -3632,7 +3676,7 @@ see the section. .Pp .It Xo Ic window-size -.Ar largest | Ar smallest | Ar manual +.Ar largest | Ar smallest | Ar manual | Ar latest .Xc Configure how .Nm @@ -3647,6 +3691,10 @@ If the size of a new window is set from the .Ic default-size option and windows are resized automatically. +With +.Ar latest , +.Nm +uses the size of the client that had the most recent activity. See also the .Ic resize-window command and the @@ -4058,9 +4106,15 @@ appended or prepended to the string if the length has been trimmed, for example will append .Ql ... if the pane title is more than five characters. +Similarly, +.Ql p +pads the string to a given width, for example +.Ql #{p10:pane_title} +will result in a width of at least 10 characters. +A positive width pads on the left, a negative on the right. .Pp Prefixing a time variable with -.Ql t: +.Ql t:\& will convert it to a string, so if .Ql #{window_activity} gives @@ -4069,34 +4123,34 @@ gives gives .Ql Sun Oct 25 09:25:02 2015 . The -.Ql b: +.Ql b:\& and -.Ql d: +.Ql d:\& prefixes are .Xr basename 3 and .Xr dirname 3 of the variable respectively. -.Ql q: +.Ql q:\& will escape .Xr sh 1 special characters. -.Ql E: +.Ql E:\& 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 option itself. -.Ql T: +.Ql T:\& is like -.Ql E: +.Ql E:\& but also expands .Xr strftime 3 specifiers. -.Ql S: , -.Ql W: +.Ql S:\& , +.Ql W:\& or -.Ql P: +.Ql P:\& will loop over each session, window or pane and insert the format once for each. For windows and panes, two comma-separated formats may be given: @@ -4107,7 +4161,7 @@ For example, to get a list of windows formatted like the status line: .Ed .Pp A prefix of the form -.Ql s/foo/bar/: +.Ql s/foo/bar/:\& will substitute .Ql foo with @@ -4116,7 +4170,7 @@ throughout. 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: +.Ql s/a(.)/\e1x/i:\& would change .Ql abABab into @@ -4160,6 +4214,8 @@ The following variables are available, where appropriate: .It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" .It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" .It Li "client_activity" Ta "" Ta "Time client last had activity" +.It Li "client_cell_height" Ta "" Ta "Height of each client cell in pixels" +.It Li "client_cell_width" Ta "" Ta "Width of each client cell in pixels" .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_created" Ta "" Ta "Time client created" .It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind" @@ -4174,13 +4230,17 @@ The following variables are available, where appropriate: .It Li "client_termname" Ta "" Ta "Terminal name of client" .It Li "client_termtype" Ta "" Ta "Terminal type of client" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" -.It Li "client_utf8" Ta "" Ta "1 if client supports utf8" +.It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" .It Li "client_written" Ta "" Ta "Bytes written to client" .It Li "command" Ta "" Ta "Name of command in use, if any" .It Li "command_list_alias" Ta "" Ta "Command alias if listing commands" .It Li "command_list_name" Ta "" Ta "Command name if listing commands" .It Li "command_list_usage" Ta "" Ta "Command usage if listing commands" +.It Li "copy_cursor_line" Ta "" Ta "Line the cursor is on in copy mode" +.It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode" +.It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode" +.It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode" .It Li "cursor_character" Ta "" Ta "Character at cursor in pane" .It Li "cursor_flag" Ta "" Ta "Pane cursor flag" .It Li "cursor_x" Ta "" Ta "Cursor X position in pane" @@ -4221,7 +4281,7 @@ The following variables are available, where appropriate: .It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" .It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" -.It Li "pane_format" Ta "" Ta "1 if format is for a pane (not assuming the current)" +.It Li "pane_format" Ta "" Ta "1 if format is for a pane" .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 "1 if pane is in a mode" @@ -4231,6 +4291,7 @@ The following variables are available, where appropriate: .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_path" Ta "#T" Ta "Path of pane (can be set by application)" .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" @@ -4238,7 +4299,7 @@ The following variables are available, where appropriate: .It Li "pane_start_command" Ta "" Ta "Command pane started with" .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" -.It Li "pane_title" Ta "#T" Ta "Title of pane" +.It Li "pane_title" Ta "#T" Ta "Title of pane (can be set by application)" .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" @@ -4247,12 +4308,16 @@ The following variables are available, where appropriate: .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" +.It Li "selection_end_x" Ta "" Ta "X position of the end of the selection" +.It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection" .It Li "selection_present" Ta "" Ta "1 if selection started in copy mode" +.It Li "selection_start_x" Ta "" Ta "X position of the start of the selection" +.It Li "selection_start_y" Ta "" Ta "Y position of the start of the selection" .It Li "session_activity" Ta "" Ta "Time of session last activity" .It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" .It Li "session_created" Ta "" Ta "Time session created" -.It Li "session_format" Ta "" Ta "1 if format is for a session (not assuming the current)" +.It Li "session_format" Ta "" Ta "1 if format is for a session" .It Li "session_group" Ta "" Ta "Name of session group" .It Li "session_group_list" Ta "" Ta "List of sessions in group" .It Li "session_group_size" Ta "" Ta "Size of session group" @@ -4271,15 +4336,18 @@ The following variables are available, where appropriate: .It Li "window_activity_flag" Ta "" Ta "1 if window has activity" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" .It Li "window_bigger" Ta "" Ta "1 if window is larger than client" +.It Li "window_cell_height" Ta "" Ta "Height of each cell in pixels" +.It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels" .It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" .It Li "window_flags" Ta "#F" Ta "Window flags" -.It Li "window_format" Ta "" Ta "1 if format is for a window (not assuming the current)" +.It Li "window_format" Ta "" Ta "1 if format is for a window" .It Li "window_height" Ta "" Ta "Height of window" .It Li "window_id" Ta "" Ta "Unique window ID" .It Li "window_index" Ta "#I" Ta "Index of window" .It Li "window_last_flag" Ta "" Ta "1 if window is the last used" .It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" .It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" +.It Li "window_marked_flag" Ta "" Ta "1 if window contains the marked pane" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" .It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" @@ -4307,7 +4375,9 @@ and .Pp A style may be the single term .Ql default -to specify the default style (which may inherit from another option) or a space +to specify the default style (which may come from an option, for example +.Ic status-style +in the status line) or a space or comma separated list of the following: .Bl -tag -width Ds .It Ic fg=colour @@ -4386,6 +4456,20 @@ and .Ic list=right-marker mark the text to be used to mark that text has been trimmed from the left or right of the list if there is not enough space. +.It Xo Ic push-default , +.Ic pop-default +.Xc +Store the current colours and attributes as the default or reset to the previous +default. +A +.Ic push-default +affects any subsequent use of the +.Ic default +term until a +.Ic pop-default . +Only one default may be pushed (each +.Ic push-default +replaces the previous saved default). .It Xo Ic range=left , .Ic range=right , .Ic range=window|X , @@ -4842,7 +4926,7 @@ The buffer commands are as follows: .Bl -tag -width Ds .It Xo .Ic choose-buffer -.Op Fl NZ +.Op Fl NZr .Op Fl F Ar format .Op Fl f Ar filter .Op Fl O Ar sort-order @@ -4869,7 +4953,8 @@ The following keys may be used in buffer mode: .It Li "d" Ta "Delete selected buffer" .It Li "D" Ta "Delete tagged buffers" .It Li "f" Ta "Enter a format to filter items" -.It Li "O" Ta "Change sort order" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" .El @@ -4884,11 +4969,13 @@ If is not given, "paste-buffer -b '%%'" is used. .Pp .Fl O -specifies the initial sort order: one of +specifies the initial sort field: one of .Ql time , .Ql name or .Ql size . +.Fl r +reverses the sort order. .Fl f specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. @@ -127,6 +127,7 @@ make_label(const char *label, char **cause) free(base); goto fail; } + free(base); if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST) goto fail; @@ -80,6 +80,10 @@ struct winlink; /* Maximum size of data to hold from a pane. */ #define READ_SIZE 4096 +/* Default pixel cell sizes. */ +#define DEFAULT_XPIXEL 16 +#define DEFAULT_YPIXEL 32 + /* Attribute to make GCC check printf-like arguments. */ #define printflike(a, b) __attribute__ ((format (printf, a, b))) @@ -682,6 +686,13 @@ struct style_range { }; TAILQ_HEAD(style_ranges, style_range); +/* Style default. */ +enum style_default_type { + STYLE_DEFAULT_BASE, + STYLE_DEFAULT_PUSH, + STYLE_DEFAULT_POP +}; + /* Style option. */ struct style { struct grid_cell gc; @@ -692,6 +703,8 @@ struct style { enum style_range_type range_type; u_int range_argument; + + enum style_default_type default_type; }; /* Virtual screen. */ @@ -699,6 +712,7 @@ struct screen_sel; struct screen_titles; struct screen { char *title; + char *path; struct screen_titles *titles; struct grid *grid; /* grid data */ @@ -845,6 +859,7 @@ struct window_pane { #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 #define PANE_STYLECHANGED 0x1000 +#define PANE_RESIZED 0x2000 int argc; char **argv; @@ -897,6 +912,7 @@ RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ struct window { u_int id; + void *latest; char *name; struct event name_event; @@ -918,12 +934,15 @@ struct window { u_int sx; u_int sy; + u_int xpixel; + u_int ypixel; int flags; #define WINDOW_BELL 0x1 #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 #define WINDOW_ZOOMED 0x8 +#define WINDOW_WASZOOMED 0x10 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) int alerts_queued; @@ -961,6 +980,7 @@ TAILQ_HEAD(winlink_stack, winlink); #define WINDOW_SIZE_LARGEST 0 #define WINDOW_SIZE_SMALLEST 1 #define WINDOW_SIZE_MANUAL 2 +#define WINDOW_SIZE_LATEST 3 /* Pane border status option. */ #define PANE_STATUS_OFF 0 @@ -1136,6 +1156,8 @@ struct tty { u_int sx; u_int sy; + u_int xpixel; + u_int ypixel; u_int cx; u_int cy; @@ -1191,6 +1213,7 @@ struct tty { TTY_VT220, TTY_VT320, TTY_VT420, + TTY_VT520, TTY_UNKNOWN } term_type; @@ -1207,7 +1230,14 @@ struct tty { struct tty_key *key_tree; }; #define TTY_TYPES \ - { "VT100", "VT101", "VT102", "VT220", "VT320", "VT420", "Unknown" } + { "VT100", \ + "VT101", \ + "VT102", \ + "VT220", \ + "VT320", \ + "VT420", \ + "VT520", \ + "Unknown" } /* TTY command context. */ struct tty_ctx { @@ -1216,8 +1246,8 @@ struct tty_ctx { const struct grid_cell *cell; int wrapped; - u_int num; - void *ptr; + u_int num; + void *ptr; /* * Cursor and region position before the screen was updated - this is @@ -1661,6 +1691,7 @@ struct spawn_context { struct session *s; struct winlink *wl; + struct client *c; struct window_pane *wp0; struct layout_cell *lc; @@ -1683,6 +1714,12 @@ struct spawn_context { #define SPAWN_EMPTY 0x40 }; +/* Mode tree sort order. */ +struct mode_tree_sort_criteria { + u_int field; + int reversed; +}; + /* tmux.c */ extern struct options *global_options; extern struct options *global_s_options; @@ -1769,6 +1806,8 @@ void format_defaults_pane(struct format_tree *, void format_defaults_paste_buffer(struct format_tree *, struct paste_buffer *); void format_lost_client(struct client *); +char *format_grid_word(struct grid *, u_int, u_int); +char *format_grid_line(struct grid *, u_int); /* format-draw.c */ void format_draw(struct screen_write_ctx *, @@ -1898,7 +1937,7 @@ void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); int tty_init(struct tty *, struct client *, int, char *); void tty_resize(struct tty *); -void tty_set_size(struct tty *, u_int, u_int); +void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); void tty_start_tty(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); @@ -2177,9 +2216,10 @@ void status_prompt_load_history(void); void status_prompt_save_history(void); /* resize.c */ -void resize_window(struct window *, u_int, u_int); -void default_window_size(struct session *, struct window *, u_int *, - u_int *, int); +void resize_window(struct window *, u_int, u_int, int, int); +void default_window_size(struct client *, struct session *, struct window *, + u_int *, u_int *, u_int *, u_int *, int); +void recalculate_size(struct window *); void recalculate_sizes(void); /* input.c */ @@ -2330,6 +2370,7 @@ void screen_reset_tabs(struct screen *); void screen_set_cursor_style(struct screen *, u_int); void screen_set_cursor_colour(struct screen *, const char *); void screen_set_title(struct screen *, const char *); +void screen_set_path(struct screen *, const char *); void screen_push_title(struct screen *); void screen_pop_title(struct screen *); void screen_resize(struct screen *, u_int, u_int, int); @@ -2369,7 +2410,7 @@ void winlink_stack_remove(struct winlink_stack *, struct winlink *); 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); +struct window *window_create(u_int, u_int, u_int, u_int); 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 *); @@ -2380,9 +2421,12 @@ void window_redraw_active_switch(struct window *, struct window_pane *); struct window_pane *window_add_pane(struct window *, struct window_pane *, u_int, int); -void window_resize(struct window *, u_int, u_int); +void window_resize(struct window *, u_int, u_int, int, int); +void window_pane_send_resize(struct window_pane *, int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); +int window_push_zoom(struct window *, int); +int window_pop_zoom(struct window *); void window_lost_pane(struct window *, struct window_pane *); void window_remove_pane(struct window *, struct window_pane *); struct window_pane *window_pane_at_index(struct window *, u_int); @@ -2472,7 +2516,8 @@ u_int layout_set_next(struct window *); u_int layout_set_previous(struct window *); /* mode-tree.c */ -typedef void (*mode_tree_build_cb)(void *, u_int, uint64_t *, const char *); +typedef void (*mode_tree_build_cb)(void *, struct mode_tree_sort_criteria *, + uint64_t *, const char *); typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *, u_int, u_int); typedef int (*mode_tree_search_cb)(void *, void *, const char *); @@ -2604,6 +2649,7 @@ struct utf8_data *utf8_fromcstr(const char *); char *utf8_tocstr(struct utf8_data *); u_int utf8_cstrwidth(const char *); char *utf8_padcstr(const char *, u_int); +char *utf8_rpadcstr(const char *, u_int); int utf8_cstrhas(const char *, const struct utf8_data *); /* osdep-*.c */ @@ -2641,8 +2687,6 @@ int style_parse(struct style *,const struct grid_cell *, const char *style_tostring(struct style *); void style_apply(struct grid_cell *, struct options *, const char *); -void style_apply_update(struct grid_cell *, struct options *, - const char *); int style_equal(struct style *, struct style *); void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); @@ -1002,8 +1002,8 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, size_t *size) { struct client *c = tty->client; - u_int i, a, b; - char tmp[64], *endptr; + u_int i, n = 0; + char tmp[64], *endptr, p[32] = { 0 }, *cp, *next; static const char *types[] = TTY_TYPES; int type; @@ -1035,21 +1035,21 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, *size = 4 + i; /* Convert version numbers. */ - a = strtoul(tmp, &endptr, 10); - if (*endptr == ';') { - b = strtoul(endptr + 1, &endptr, 10); + cp = tmp; + while ((next = strsep(&cp, ";")) != NULL) { + p[n] = strtoul(next, &endptr, 10); if (*endptr != '\0' && *endptr != ';') - b = 0; - } else - a = b = 0; + p[n] = 0; + n++; + } /* Store terminal type. */ type = TTY_UNKNOWN; - switch (a) { + switch (p[0]) { case 1: - if (b == 2) + if (p[1] == 2) type = TTY_VT100; - else if (b == 0) + else if (p[1] == 0) type = TTY_VT101; break; case 6: @@ -1064,7 +1064,12 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, case 64: type = TTY_VT420; break; + case 65: + type = TTY_VT520; + break; } + for (i = 2; i < n; i++) + log_debug("%s: DA feature: %d", c->name, p[i]); tty_set_type(tty, type); log_debug("%s: received DA %.*s (%s)", c->name, (int)*size, buf, @@ -642,7 +642,8 @@ tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b) } const char * -tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b, int c) +tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b, + int c) { return (tparm((char *) tty_term_string(term, code), a, b, c, 0, 0, 0, 0, 0, 0)); } @@ -127,29 +127,40 @@ tty_resize(struct tty *tty) { struct client *c = tty->client; struct winsize ws; - u_int sx, sy; + u_int sx, sy, xpixel, ypixel; if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) { sx = ws.ws_col; - if (sx == 0) + if (sx == 0) { sx = 80; + xpixel = 0; + } else + xpixel = ws.ws_xpixel / sx; sy = ws.ws_row; - if (sy == 0) + if (sy == 0) { sy = 24; + ypixel = 0; + } else + ypixel = ws.ws_ypixel / sy; } else { sx = 80; sy = 24; + xpixel = 0; + ypixel = 0; } - log_debug("%s: %s now %ux%u", __func__, c->name, sx, sy); - tty_set_size(tty, sx, sy); + log_debug("%s: %s now %ux%u (%ux%u)", __func__, c->name, sx, sy, + xpixel, ypixel); + tty_set_size(tty, sx, sy, xpixel, ypixel); tty_invalidate(tty); } void -tty_set_size(struct tty *tty, u_int sx, u_int sy) +tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel) { tty->sx = sx; tty->sy = sy; + tty->xpixel = xpixel; + tty->ypixel = ypixel; } static void @@ -2106,7 +2117,9 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy) if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) { tty_putcode1(tty, TTYC_HPA, cx); goto out; - } else if (change > 0 && tty_term_has(term, TTYC_CUB)) { + } else if (change > 0 && + tty_term_has(term, TTYC_CUB) && + !tty_use_margin(tty)) { if (change == 2 && tty_term_has(term, TTYC_CUB1)) { tty_putcode(tty, TTYC_CUB1); tty_putcode(tty, TTYC_CUB1); @@ -2114,7 +2127,9 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy) } tty_putcode1(tty, TTYC_CUB, change); goto out; - } else if (change < 0 && tty_term_has(term, TTYC_CUF)) { + } else if (change < 0 && + tty_term_has(term, TTYC_CUF) && + !tty_use_margin(tty)) { tty_putcode1(tty, TTYC_CUF, -change); goto out; } @@ -2374,10 +2389,7 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) gc->fg &= 7; if (colours >= 16) gc->fg += 90; - else - gc->attr |= GRID_ATTR_BRIGHT; - } else - gc->attr &= ~GRID_ATTR_BRIGHT; + } } return; } @@ -2442,7 +2454,8 @@ 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) +tty_check_us(__unused struct tty *tty, struct window_pane *wp, + struct grid_cell *gc) { int c; @@ -415,7 +415,7 @@ utf8_cstrwidth(const char *s) return (width); } -/* Pad UTF-8 string to width. Caller frees. */ +/* Pad UTF-8 string to width on the left. Caller frees. */ char * utf8_padcstr(const char *s, u_int width) { @@ -436,6 +436,27 @@ utf8_padcstr(const char *s, u_int width) return (out); } +/* Pad UTF-8 string to width on the right. Caller frees. */ +char * +utf8_rpadcstr(const char *s, u_int width) +{ + size_t slen; + char *out; + u_int n, i; + + n = utf8_cstrwidth(s); + if (n >= width) + return (xstrdup(s)); + + slen = strlen(s); + out = xmalloc(slen + 1 + (width - n)); + for (i = 0; i < width - n; i++) + out[i] = ' '; + memcpy(out + i, s, slen); + out[i + slen] = '\0'; + return (out); +} + int utf8_cstrhas(const char *s, const struct utf8_data *ud) { diff --git a/window-buffer.c b/window-buffer.c index 224dfedb..5c897341 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -74,6 +74,7 @@ static const char *window_buffer_sort_list[] = { "name", "size" }; +static struct mode_tree_sort_criteria *window_buffer_sort; struct window_buffer_itemdata { const char *name; @@ -112,43 +113,29 @@ window_buffer_free_item(struct window_buffer_itemdata *item) } static int -window_buffer_cmp_name(const void *a0, const void *b0) +window_buffer_cmp(const void *a0, const void *b0) { - const struct window_buffer_itemdata *const *a = a0; - const struct window_buffer_itemdata *const *b = b0; - - return (strcmp((*a)->name, (*b)->name)); -} - -static int -window_buffer_cmp_time(const void *a0, const void *b0) -{ - const struct window_buffer_itemdata *const *a = a0; - const struct window_buffer_itemdata *const *b = b0; - - if ((*a)->order > (*b)->order) - return (-1); - if ((*a)->order < (*b)->order) - return (1); - return (strcmp((*a)->name, (*b)->name)); -} - -static int -window_buffer_cmp_size(const void *a0, const void *b0) -{ - const struct window_buffer_itemdata *const *a = a0; - const struct window_buffer_itemdata *const *b = b0; - - if ((*a)->size > (*b)->size) - return (-1); - if ((*a)->size < (*b)->size) - return (1); - return (strcmp((*a)->name, (*b)->name)); + const struct window_buffer_itemdata *const *a = a0; + const struct window_buffer_itemdata *const *b = b0; + int result = 0; + + if (window_buffer_sort->field == WINDOW_BUFFER_BY_TIME) + result = (*b)->order - (*a)->order; + else if (window_buffer_sort->field == WINDOW_BUFFER_BY_SIZE) + result = (*b)->size - (*a)->size; + + /* Use WINDOW_BUFFER_BY_NAME as default order and tie breaker. */ + if (result == 0) + result = strcmp((*a)->name, (*b)->name); + + if (window_buffer_sort->reversed) + result = -result; + return (result); } static void -window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag, - const char *filter) +window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, + __unused uint64_t *tag, const char *filter) { struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item; @@ -174,20 +161,9 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag, item->order = paste_buffer_order(pb); } - switch (sort_type) { - case WINDOW_BUFFER_BY_NAME: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_buffer_cmp_name); - break; - case WINDOW_BUFFER_BY_TIME: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_buffer_cmp_time); - break; - case WINDOW_BUFFER_BY_SIZE: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_buffer_cmp_size); - break; - } + window_buffer_sort = sort_crit; + qsort(data->item_list, data->item_size, sizeof *data->item_list, + window_buffer_cmp); if (cmd_find_valid_state(&data->fs)) { s = data->fs.s; diff --git a/window-client.c b/window-client.c index d85414ea..2ca9c012 100644 --- a/window-client.c +++ b/window-client.c @@ -75,6 +75,7 @@ static const char *window_client_sort_list[] = { "creation", "activity" }; +static struct mode_tree_sort_criteria *window_client_sort; struct window_client_itemdata { struct client *c; @@ -110,60 +111,48 @@ window_client_free_item(struct window_client_itemdata *item) } static int -window_client_cmp_name(const void *a0, const void *b0) +window_client_cmp(const void *a0, const void *b0) { - const struct window_client_itemdata *const *a = a0; - const struct window_client_itemdata *const *b = b0; - - return (strcmp((*a)->c->name, (*b)->c->name)); -} - -static int -window_client_cmp_size(const void *a0, const void *b0) -{ - const struct window_client_itemdata *const *a = a0; - const struct window_client_itemdata *const *b = b0; - - if ((*a)->c->tty.sx < (*b)->c->tty.sx) - return (-1); - if ((*a)->c->tty.sx > (*b)->c->tty.sx) - return (1); - if ((*a)->c->tty.sy < (*b)->c->tty.sy) - return (-1); - if ((*a)->c->tty.sy > (*b)->c->tty.sy) - return (1); - return (strcmp((*a)->c->name, (*b)->c->name)); -} + const struct window_client_itemdata *const *a = a0; + const struct window_client_itemdata *const *b = b0; + const struct window_client_itemdata *itema = *a; + const struct window_client_itemdata *itemb = *b; + struct client *ca = itema->c; + struct client *cb = itemb->c; + int result = 0; + + switch (window_client_sort->field) { + case WINDOW_CLIENT_BY_SIZE: + result = ca->tty.sx - cb->tty.sx; + if (result == 0) + result = ca->tty.sy - cb->tty.sy; + break; + case WINDOW_CLIENT_BY_CREATION_TIME: + if (timercmp(&ca->creation_time, &cb->creation_time, >)) + result = -1; + else if (timercmp(&ca->creation_time, &cb->creation_time, <)) + result = 1; + break; + case WINDOW_CLIENT_BY_ACTIVITY_TIME: + if (timercmp(&ca->activity_time, &cb->activity_time, >)) + result = -1; + else if (timercmp(&ca->activity_time, &cb->activity_time, <)) + result = 1; + break; + } -static int -window_client_cmp_creation_time(const void *a0, const void *b0) -{ - const struct window_client_itemdata *const *a = a0; - const struct window_client_itemdata *const *b = b0; - - if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, >)) - return (-1); - if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, <)) - return (1); - return (strcmp((*a)->c->name, (*b)->c->name)); -} + /* Use WINDOW_CLIENT_BY_NAME as default order and tie breaker. */ + if (result == 0) + result = strcmp(ca->name, cb->name); -static int -window_client_cmp_activity_time(const void *a0, const void *b0) -{ - const struct window_client_itemdata *const *a = a0; - const struct window_client_itemdata *const *b = b0; - - if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, >)) - return (-1); - if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, <)) - return (1); - return (strcmp((*a)->c->name, (*b)->c->name)); + if (window_client_sort->reversed) + result = -result; + return (result); } static void -window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag, - const char *filter) +window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, + __unused uint64_t *tag, const char *filter) { struct window_client_modedata *data = modedata; struct window_client_itemdata *item; @@ -187,24 +176,9 @@ window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag, c->references++; } - switch (sort_type) { - case WINDOW_CLIENT_BY_NAME: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_client_cmp_name); - break; - case WINDOW_CLIENT_BY_SIZE: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_client_cmp_size); - break; - case WINDOW_CLIENT_BY_CREATION_TIME: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_client_cmp_creation_time); - break; - case WINDOW_CLIENT_BY_ACTIVITY_TIME: - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_client_cmp_activity_time); - break; - } + window_client_sort = sort_crit; + qsort(data->item_list, data->item_size, sizeof *data->item_list, + window_client_cmp); for (i = 0; i < data->item_size; i++) { item = data->item_list[i]; diff --git a/window-copy.c b/window-copy.c index d868631c..16708d04 100644 --- a/window-copy.c +++ b/window-copy.c @@ -60,8 +60,8 @@ static int window_copy_search_rl(struct grid *, struct grid *, u_int *, static int window_copy_search_marks(struct window_mode_entry *, struct screen *); static void window_copy_clear_marks(struct window_mode_entry *); -static void window_copy_move_left(struct screen *, u_int *, u_int *); -static void window_copy_move_right(struct screen *, u_int *, u_int *); +static void window_copy_move_left(struct screen *, u_int *, u_int *, int); +static void window_copy_move_right(struct screen *, u_int *, u_int *, int); static int window_copy_is_lowercase(const char *); static int window_copy_search_jump(struct window_mode_entry *, struct grid *, struct grid *, u_int, u_int, u_int, int, int, @@ -110,7 +110,7 @@ static void window_copy_cursor_next_word(struct window_mode_entry *, static void window_copy_cursor_next_word_end(struct window_mode_entry *, const char *); static void window_copy_cursor_previous_word(struct window_mode_entry *, - const char *); + const char *, int); static void window_copy_scroll_up(struct window_mode_entry *, u_int); static void window_copy_scroll_down(struct window_mode_entry *, u_int); static void window_copy_rectangle_toggle(struct window_mode_entry *); @@ -564,10 +564,33 @@ static void window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) { struct window_copy_mode_data *data = wme->data; + char *s; - format_add(ft, "selection_present", "%d", data->screen.sel != NULL); format_add(ft, "scroll_position", "%d", data->oy); format_add(ft, "rectangle_toggle", "%d", data->rectflag); + + format_add(ft, "copy_cursor_x", "%d", data->cx); + format_add(ft, "copy_cursor_y", "%d", data->cy); + + format_add(ft, "selection_present", "%d", data->screen.sel != NULL); + if (data->screen.sel != NULL) { + format_add(ft, "selection_start_x", "%d", data->selx); + format_add(ft, "selection_start_y", "%d", data->sely); + format_add(ft, "selection_end_x", "%d", data->endselx); + format_add(ft, "selection_end_y", "%d", data->endsely); + } + + s = format_grid_word(data->screen.grid, data->cx, data->cy); + if (s != NULL) { + format_add(ft, "copy_cursor_word", "%s", s); + free(s); + } + + s = format_grid_line(data->screen.grid, data->cy); + if (s != NULL) { + format_add(ft, "copy_cursor_line", "%s", s); + free(s); + } } static void @@ -820,6 +843,21 @@ window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs) } static enum window_copy_cmd_action +window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix, cy; + + cy = data->cy; + for (; np != 0; np--) + window_copy_cursor_down(wme, 0); + if (cy == data->cy && data->oy == 0) + return (WINDOW_COPY_CMD_CANCEL); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; @@ -1048,7 +1086,7 @@ window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_previous_word(wme, "}]) "); + window_copy_cursor_previous_word(wme, "}]) ", 1); } continue; } @@ -1093,7 +1131,6 @@ window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } - static enum window_copy_cmd_action window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) { @@ -1343,7 +1380,7 @@ window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_previous_word(wme, " "); + window_copy_cursor_previous_word(wme, " ", 1); return (WINDOW_COPY_CMD_NOTHING); } @@ -1357,7 +1394,7 @@ window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_previous_word(wme, ws); + window_copy_cursor_previous_word(wme, ws, 1); return (WINDOW_COPY_CMD_NOTHING); } @@ -1477,7 +1514,7 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) data->rectflag = 0; ws = options_get_string(s->options, "word-separators"); - window_copy_cursor_previous_word(wme, ws); + window_copy_cursor_previous_word(wme, ws, 0); window_copy_start_selection(wme); window_copy_cursor_next_word_end(wme, ws); @@ -1635,12 +1672,29 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument = cs->args->argv[1]; - - if (*argument != '\0') { + const char *argument; + char *expanded; + + if (cs->args->argc == 2) { + argument = cs->args->argv[1]; + if (*argument != '\0') { + if (args_has(cs->args, 'F')) { + expanded = format_single(NULL, argument, NULL, + NULL, NULL, wme->wp); + if (*expanded == '\0') { + free(expanded); + return (WINDOW_COPY_CMD_NOTHING); + } + free(data->searchstr); + data->searchstr = expanded; + } else { + free(data->searchstr); + data->searchstr = xstrdup(argument); + } + } + } + if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; - free(data->searchstr); - data->searchstr = xstrdup(argument); for (; np != 0; np--) window_copy_search_up(wme); } @@ -1653,12 +1707,29 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; - const char *argument = cs->args->argv[1]; - - if (*argument != '\0') { + const char *argument; + char *expanded; + + if (cs->args->argc == 2) { + argument = cs->args->argv[1]; + if (*argument != '\0') { + if (args_has(cs->args, 'F')) { + expanded = format_single(NULL, argument, NULL, + NULL, NULL, wme->wp); + if (*expanded == '\0') { + free(expanded); + return (WINDOW_COPY_CMD_NOTHING); + } + free(data->searchstr); + data->searchstr = expanded; + } else { + free(data->searchstr); + data->searchstr = xstrdup(argument); + } + } + } + if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHDOWN; - free(data->searchstr); - data->searchstr = xstrdup(argument); for (; np != 0; np--) window_copy_search_down(wme); } @@ -1766,131 +1837,134 @@ static const struct { const char *command; int minargs; int maxargs; + int ismotion; enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); } window_copy_cmd_table[] = { - { "append-selection", 0, 0, + { "append-selection", 0, 0, 0, window_copy_cmd_append_selection }, - { "append-selection-and-cancel", 0, 0, + { "append-selection-and-cancel", 0, 0, 0, window_copy_cmd_append_selection_and_cancel }, - { "back-to-indentation", 0, 0, + { "back-to-indentation", 0, 0, 0, window_copy_cmd_back_to_indentation }, - { "begin-selection", 0, 0, + { "begin-selection", 0, 0, 0, window_copy_cmd_begin_selection }, - { "bottom-line", 0, 0, + { "bottom-line", 0, 0, 1, window_copy_cmd_bottom_line }, - { "cancel", 0, 0, + { "cancel", 0, 0, 0, window_copy_cmd_cancel }, - { "clear-selection", 0, 0, + { "clear-selection", 0, 0, 0, window_copy_cmd_clear_selection }, - { "copy-end-of-line", 0, 1, + { "copy-end-of-line", 0, 1, 0, window_copy_cmd_copy_end_of_line }, - { "copy-line", 0, 1, + { "copy-line", 0, 1, 0, window_copy_cmd_copy_line }, - { "copy-pipe-no-clear", 1, 2, + { "copy-pipe-no-clear", 1, 2, 0, window_copy_cmd_copy_pipe_no_clear }, - { "copy-pipe", 1, 2, + { "copy-pipe", 1, 2, 0, window_copy_cmd_copy_pipe }, - { "copy-pipe-and-cancel", 1, 2, + { "copy-pipe-and-cancel", 1, 2, 0, window_copy_cmd_copy_pipe_and_cancel }, - { "copy-selection-no-clear", 0, 1, + { "copy-selection-no-clear", 0, 1, 0, window_copy_cmd_copy_selection_no_clear }, - { "copy-selection", 0, 1, + { "copy-selection", 0, 1, 0, window_copy_cmd_copy_selection }, - { "copy-selection-and-cancel", 0, 1, + { "copy-selection-and-cancel", 0, 1, 0, window_copy_cmd_copy_selection_and_cancel }, - { "cursor-down", 0, 0, + { "cursor-down", 0, 0, 1, window_copy_cmd_cursor_down }, - { "cursor-left", 0, 0, + { "cursor-down-and-cancel", 0, 0, 0, + window_copy_cmd_cursor_down_and_cancel }, + { "cursor-left", 0, 0, 1, window_copy_cmd_cursor_left }, - { "cursor-right", 0, 0, + { "cursor-right", 0, 0, 1, window_copy_cmd_cursor_right }, - { "cursor-up", 0, 0, + { "cursor-up", 0, 0, 1, window_copy_cmd_cursor_up }, - { "end-of-line", 0, 0, + { "end-of-line", 0, 0, 1, window_copy_cmd_end_of_line }, - { "goto-line", 1, 1, + { "goto-line", 1, 1, 1, window_copy_cmd_goto_line }, - { "halfpage-down", 0, 0, + { "halfpage-down", 0, 0, 1, window_copy_cmd_halfpage_down }, - { "halfpage-down-and-cancel", 0, 0, + { "halfpage-down-and-cancel", 0, 0, 0, window_copy_cmd_halfpage_down_and_cancel }, - { "halfpage-up", 0, 0, + { "halfpage-up", 0, 0, 1, window_copy_cmd_halfpage_up }, - { "history-bottom", 0, 0, + { "history-bottom", 0, 0, 1, window_copy_cmd_history_bottom }, - { "history-top", 0, 0, + { "history-top", 0, 0, 1, window_copy_cmd_history_top }, - { "jump-again", 0, 0, + { "jump-again", 0, 0, 1, window_copy_cmd_jump_again }, - { "jump-backward", 1, 1, + { "jump-backward", 1, 1, 1, window_copy_cmd_jump_backward }, - { "jump-forward", 1, 1, + { "jump-forward", 1, 1, 1, window_copy_cmd_jump_forward }, - { "jump-reverse", 0, 0, + { "jump-reverse", 0, 0, 1, window_copy_cmd_jump_reverse }, - { "jump-to-backward", 1, 1, + { "jump-to-backward", 1, 1, 1, window_copy_cmd_jump_to_backward }, - { "jump-to-forward", 1, 1, + { "jump-to-forward", 1, 1, 1, window_copy_cmd_jump_to_forward }, - { "middle-line", 0, 0, + { "middle-line", 0, 0, 1, window_copy_cmd_middle_line }, - { "next-matching-bracket", 0, 0, + { "next-matching-bracket", 0, 0, 0, window_copy_cmd_next_matching_bracket }, - { "next-paragraph", 0, 0, + { "next-paragraph", 0, 0, 1, window_copy_cmd_next_paragraph }, - { "next-space", 0, 0, + { "next-space", 0, 0, 1, window_copy_cmd_next_space }, - { "next-space-end", 0, 0, + { "next-space-end", 0, 0, 1, window_copy_cmd_next_space_end }, - { "next-word", 0, 0, + { "next-word", 0, 0, 1, window_copy_cmd_next_word }, - { "next-word-end", 0, 0, + { "next-word-end", 0, 0, 1, window_copy_cmd_next_word_end }, - { "other-end", 0, 0, + { "other-end", 0, 0, 1, window_copy_cmd_other_end }, - { "page-down", 0, 0, + { "page-down", 0, 0, 1, window_copy_cmd_page_down }, - { "page-down-and-cancel", 0, 0, + { "page-down-and-cancel", 0, 0, 0, window_copy_cmd_page_down_and_cancel }, - { "page-up", 0, 0, + { "page-up", 0, 0, 1, window_copy_cmd_page_up }, - { "previous-matching-bracket", 0, 0, + { "previous-matching-bracket", 0, 0, 0, window_copy_cmd_previous_matching_bracket }, - { "previous-paragraph", 0, 0, + { "previous-paragraph", 0, 0, 1, window_copy_cmd_previous_paragraph }, - { "previous-space", 0, 0, + { "previous-space", 0, 0, 1, window_copy_cmd_previous_space }, - { "previous-word", 0, 0, + { "previous-word", 0, 0, 1, window_copy_cmd_previous_word }, - { "rectangle-toggle", 0, 0, + { "rectangle-toggle", 0, 0, 0, window_copy_cmd_rectangle_toggle }, - { "scroll-down", 0, 0, + { "scroll-down", 0, 0, 1, window_copy_cmd_scroll_down }, - { "scroll-down-and-cancel", 0, 0, + { "scroll-down-and-cancel", 0, 0, 0, window_copy_cmd_scroll_down_and_cancel }, - { "scroll-up", 0, 0, + { "scroll-up", 0, 0, 1, window_copy_cmd_scroll_up }, - { "search-again", 0, 0, + { "search-again", 0, 0, 0, window_copy_cmd_search_again }, - { "search-backward", 1, 1, + { "search-backward", 0, 1, 0, window_copy_cmd_search_backward }, - { "search-backward-incremental", 1, 1, + { "search-backward-incremental", 1, 1, 0, window_copy_cmd_search_backward_incremental }, - { "search-forward", 1, 1, + { "search-forward", 0, 1, 0, window_copy_cmd_search_forward }, - { "search-forward-incremental", 1, 1, + { "search-forward-incremental", 1, 1, 0, window_copy_cmd_search_forward_incremental }, - { "search-reverse", 0, 0, + { "search-reverse", 0, 0, 0, window_copy_cmd_search_reverse }, - { "select-line", 0, 0, + { "select-line", 0, 0, 0, window_copy_cmd_select_line }, - { "select-word", 0, 0, + { "select-word", 0, 0, 0, window_copy_cmd_select_word }, - { "start-of-line", 0, 0, + { "start-of-line", 0, 0, 1, window_copy_cmd_start_of_line }, - { "stop-selection", 0, 0, + { "stop-selection", 0, 0, 0, window_copy_cmd_stop_selection }, - { "top-line", 0, 0, + { "top-line", 0, 0, 1, window_copy_cmd_top_line }, }; @@ -1904,6 +1978,7 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, enum window_copy_cmd_action action; const char *command; u_int i; + int ismotion = 0, keys; if (args->argc == 0) return; @@ -1926,16 +2001,23 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, if (args->argc - 1 < window_copy_cmd_table[i].minargs || args->argc - 1 > window_copy_cmd_table[i].maxargs) break; + ismotion = window_copy_cmd_table[i].ismotion; action = window_copy_cmd_table[i].f (&cs); break; } } if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) { - window_copy_clear_marks(wme); + keys = options_get_number(wme->wp->window->options, "mode-keys"); + if (keys != MODEKEY_VI || !ismotion) { + window_copy_clear_marks(wme); + data->searchx = data->searchy = -1; + } else if (data->searchthis != -1) { + data->searchthis = -1; + action = WINDOW_COPY_CMD_REDRAW; + } if (action == WINDOW_COPY_CMD_NOTHING) action = WINDOW_COPY_CMD_REDRAW; - data->searchx = data->searchy = -1; } wme->prefix = 1; @@ -2047,11 +2129,16 @@ window_copy_search_rl(struct grid *gd, } static void -window_copy_move_left(struct screen *s, u_int *fx, u_int *fy) +window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag) { if (*fx == 0) { /* left */ - if (*fy == 0) /* top */ + if (*fy == 0) { /* top */ + if (wrapflag) { + *fx = screen_size_x(s) - 1; + *fy = screen_hsize(s) + screen_size_y(s); + } return; + } *fx = screen_size_x(s) - 1; *fy = *fy - 1; } else @@ -2059,11 +2146,16 @@ window_copy_move_left(struct screen *s, u_int *fx, u_int *fy) } static void -window_copy_move_right(struct screen *s, u_int *fx, u_int *fy) +window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag) { if (*fx == screen_size_x(s) - 1) { /* right */ - if (*fy == screen_hsize(s) + screen_size_y(s)) /* bottom */ + if (*fy == screen_hsize(s) + screen_size_y(s)) { /* bottom */ + if (wrapflag) { + *fx = 0; + *fy = 0; + } return; + } *fx = 0; *fy = *fy + 1; } else @@ -2155,18 +2247,16 @@ window_copy_search(struct window_mode_entry *wme, int direction) screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); screen_write_stop(&ctx); - if (direction) - window_copy_move_right(s, &fx, &fy); - else - window_copy_move_left(s, &fx, &fy); - wrapflag = options_get_number(wp->window->options, "wrap-search"); cis = window_copy_is_lowercase(data->searchstr); - if (direction) + if (direction) { + window_copy_move_right(s, &fx, &fy, wrapflag); endline = gd->hsize + gd->sy - 1; - else + } else { + window_copy_move_left(s, &fx, &fy, wrapflag); endline = 0; + } found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, wrapflag, direction); @@ -3314,7 +3404,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, /* Move to the previous place where a word begins. */ static void window_copy_cursor_previous_word(struct window_mode_entry *wme, - const char *separators) + const char *separators, int already) { struct window_copy_mode_data *data = wme->data; u_int px, py; @@ -3323,25 +3413,27 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, py = screen_hsize(data->backing) + data->cy - data->oy; /* Move back to the previous word character. */ - for (;;) { - if (px > 0) { - px--; - if (!window_copy_in_set(wme, px, py, separators)) - break; - } else { - if (data->cy == 0 && - (screen_hsize(data->backing) == 0 || - data->oy >= screen_hsize(data->backing) - 1)) - goto out; - window_copy_cursor_up(wme, 0); - - py = screen_hsize(data->backing) + data->cy - data->oy; - px = window_copy_find_length(wme, py); - - /* Stop if separator at EOL. */ - if (px > 0 && - window_copy_in_set(wme, px - 1, py, separators)) - break; + if (already || window_copy_in_set(wme, px, py, separators)) { + for (;;) { + if (px > 0) { + px--; + if (!window_copy_in_set(wme, px, py, separators)) + break; + } else { + if (data->cy == 0 && + (screen_hsize(data->backing) == 0 || + data->oy >= screen_hsize(data->backing) - 1)) + goto out; + window_copy_cursor_up(wme, 0); + + py = screen_hsize(data->backing) + data->cy - data->oy; + px = window_copy_find_length(wme, py); + + /* Stop if separator at EOL. */ + if (px > 0 && + window_copy_in_set(wme, px - 1, py, separators)) + break; + } } } diff --git a/window-tree.c b/window-tree.c index 61b11b1b..4f4cbaab 100644 --- a/window-tree.c +++ b/window-tree.c @@ -89,6 +89,7 @@ static const char *window_tree_sort_list[] = { "name", "time" }; +static struct mode_tree_sort_criteria *window_tree_sort; enum window_tree_type { WINDOW_TREE_NONE, @@ -184,62 +185,92 @@ window_tree_free_item(struct window_tree_itemdata *item) } static int -window_tree_cmp_session_name(const void *a0, const void *b0) +window_tree_cmp_session(const void *a0, const void *b0) { - const struct session *const *a = a0; - const struct session *const *b = b0; + const struct session *const *a = a0; + const struct session *const *b = b0; + const struct session *sa = *a; + const struct session *sb = *b; + int result = 0; - return (strcmp((*a)->name, (*b)->name)); -} - -static int -window_tree_cmp_session_time(const void *a0, const void *b0) -{ - const struct session *const *a = a0; - const struct session *const *b = b0; + switch (window_tree_sort->field) { + case WINDOW_TREE_BY_INDEX: + result = sa->id - sb->id; + break; + case WINDOW_TREE_BY_TIME: + if (timercmp(&sa->activity_time, &sb->activity_time, >)) { + result = -1; + break; + } + if (timercmp(&sa->activity_time, &sb->activity_time, <)) { + result = 1; + break; + } + /* FALLTHROUGH */ + case WINDOW_TREE_BY_NAME: + result = strcmp(sa->name, sb->name); + break; + } - if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >)) - return (-1); - if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <)) - return (1); - return (strcmp((*a)->name, (*b)->name)); + if (window_tree_sort->reversed) + result = -result; + return (result); } static int -window_tree_cmp_window_name(const void *a0, const void *b0) +window_tree_cmp_window(const void *a0, const void *b0) { - const struct winlink *const *a = a0; - const struct winlink *const *b = b0; - - return (strcmp((*a)->window->name, (*b)->window->name)); -} + const struct winlink *const *a = a0; + const struct winlink *const *b = b0; + const struct winlink *wla = *a; + const struct winlink *wlb = *b; + struct window *wa = wla->window; + struct window *wb = wlb->window; + int result = 0; + + switch (window_tree_sort->field) { + case WINDOW_TREE_BY_INDEX: + result = wla->idx - wlb->idx; + break; + case WINDOW_TREE_BY_TIME: + if (timercmp(&wa->activity_time, &wb->activity_time, >)) { + result = -1; + break; + } + if (timercmp(&wa->activity_time, &wb->activity_time, <)) { + result = 1; + break; + } + /* FALLTHROUGH */ + case WINDOW_TREE_BY_NAME: + result = strcmp(wa->name, wb->name); + break; + } -static int -window_tree_cmp_window_time(const void *a0, const void *b0) -{ - const struct winlink *const *a = a0; - const struct winlink *const *b = b0; - - if (timercmp(&(*a)->window->activity_time, - &(*b)->window->activity_time, >)) - return (-1); - if (timercmp(&(*a)->window->activity_time, - &(*b)->window->activity_time, <)) - return (1); - return (strcmp((*a)->window->name, (*b)->window->name)); + if (window_tree_sort->reversed) + result = -result; + return (result); } static int -window_tree_cmp_pane_time(const void *a0, const void *b0) +window_tree_cmp_pane(const void *a0, const void *b0) { - const struct window_pane *const *a = a0; - const struct window_pane *const *b = b0; + const struct window_pane *const *a = a0; + const struct window_pane *const *b = b0; + int result; - if ((*a)->active_point < (*b)->active_point) - return (-1); - if ((*a)->active_point > (*b)->active_point) - return (1); - return (0); + if (window_tree_sort->field == WINDOW_TREE_BY_TIME) + result = (*a)->active_point - (*b)->active_point; + else { + /* + * Panes don't have names, so use number order for any other + * sort field. + */ + result = (*a)->id - (*b)->id; + } + if (window_tree_sort->reversed) + result = -result; + return (result); } static void @@ -285,8 +316,9 @@ window_tree_filter_pane(struct session *s, struct winlink *wl, } static int -window_tree_build_window(struct session *s, struct winlink *wl, void* modedata, - u_int sort_type, struct mode_tree_item *parent, const char *filter) +window_tree_build_window(struct session *s, struct winlink *wl, + void* modedata, struct mode_tree_sort_criteria *sort_crit, + struct mode_tree_item *parent, const char *filter) { struct window_tree_modedata *data = modedata; struct window_tree_itemdata *item; @@ -335,16 +367,8 @@ window_tree_build_window(struct session *s, struct winlink *wl, void* modedata, if (n == 0) goto empty; - switch (sort_type) { - case WINDOW_TREE_BY_INDEX: - break; - case WINDOW_TREE_BY_NAME: - /* Panes don't have names, so leave in number order. */ - break; - case WINDOW_TREE_BY_TIME: - qsort(l, n, sizeof *l, window_tree_cmp_pane_time); - break; - } + window_tree_sort = sort_crit; + qsort(l, n, sizeof *l, window_tree_cmp_pane); for (i = 0; i < n; i++) window_tree_build_pane(s, wl, l[i], modedata, mti); @@ -360,7 +384,7 @@ empty: static void window_tree_build_session(struct session *s, void* modedata, - u_int sort_type, const char *filter) + struct mode_tree_sort_criteria *sort_crit, const char *filter) { struct window_tree_modedata *data = modedata; struct window_tree_itemdata *item; @@ -392,20 +416,12 @@ window_tree_build_session(struct session *s, void* modedata, l = xreallocarray(l, n + 1, sizeof *l); l[n++] = wl; } - switch (sort_type) { - case WINDOW_TREE_BY_INDEX: - break; - case WINDOW_TREE_BY_NAME: - qsort(l, n, sizeof *l, window_tree_cmp_window_name); - break; - case WINDOW_TREE_BY_TIME: - qsort(l, n, sizeof *l, window_tree_cmp_window_time); - break; - } + window_tree_sort = sort_crit; + qsort(l, n, sizeof *l, window_tree_cmp_window); empty = 0; for (i = 0; i < n; i++) { - if (!window_tree_build_window(s, l[i], modedata, sort_type, mti, + if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti, filter)) empty++; } @@ -418,8 +434,8 @@ window_tree_build_session(struct session *s, void* modedata, } static void -window_tree_build(void *modedata, u_int sort_type, uint64_t *tag, - const char *filter) +window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, + uint64_t *tag, const char *filter) { struct window_tree_modedata *data = modedata; struct session *s, **l; @@ -446,19 +462,11 @@ window_tree_build(void *modedata, u_int sort_type, uint64_t *tag, l = xreallocarray(l, n + 1, sizeof *l); l[n++] = s; } - switch (sort_type) { - case WINDOW_TREE_BY_INDEX: - break; - case WINDOW_TREE_BY_NAME: - qsort(l, n, sizeof *l, window_tree_cmp_session_name); - break; - case WINDOW_TREE_BY_TIME: - qsort(l, n, sizeof *l, window_tree_cmp_session_time); - break; - } + window_tree_sort = sort_crit; + qsort(l, n, sizeof *l, window_tree_cmp_session); for (i = 0; i < n; i++) - window_tree_build_session(l[i], modedata, sort_type, filter); + window_tree_build_session(l[i], modedata, sort_crit, filter); free(l); switch (data->type) { @@ -306,12 +306,17 @@ window_update_activity(struct window *w) } struct window * -window_create(u_int sx, u_int sy) +window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) { struct window *w; + if (xpixel == 0) + xpixel = DEFAULT_XPIXEL; + if (ypixel == 0) + ypixel = DEFAULT_YPIXEL; + w = xcalloc(1, sizeof *w); - w->name = NULL; + w->name = xstrdup(""); w->flags = 0; TAILQ_INIT(&w->panes); @@ -322,6 +327,8 @@ window_create(u_int sx, u_int sy) w->sx = sx; w->sy = sy; + w->xpixel = xpixel; + w->ypixel = ypixel; w->options = options_create(global_w_options); @@ -408,11 +415,49 @@ window_set_name(struct window *w, const char *new_name) } void -window_resize(struct window *w, u_int sx, u_int sy) +window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) { - log_debug("%s: @%u resize %ux%u", __func__, w->id, sx, sy); + if (xpixel == 0) + xpixel = DEFAULT_XPIXEL; + if (ypixel == 0) + ypixel = DEFAULT_YPIXEL; + + log_debug("%s: @%u resize %ux%u (%ux%u)", __func__, w->id, sx, sy, + xpixel == -1 ? w->xpixel : xpixel, + ypixel == -1 ? w->ypixel : ypixel); w->sx = sx; w->sy = sy; + if (xpixel != -1) + w->xpixel = xpixel; + if (ypixel != -1) + w->ypixel = ypixel; +} + +void +window_pane_send_resize(struct window_pane *wp, int yadjust) +{ + struct window *w = wp->window; + struct winsize ws; + + if (wp->fd == -1) + return; + + memset(&ws, 0, sizeof ws); + ws.ws_col = wp->sx; + ws.ws_row = wp->sy + yadjust; + ws.ws_xpixel = w->xpixel * ws.ws_col; + ws.ws_ypixel = w->ypixel * ws.ws_row; + if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) +#ifdef __sun + /* + * Some versions of Solaris apparently can return an error when + * resizing; don't know why this happens, can't reproduce on + * other platforms and ignoring it doesn't seem to cause any + * issues. + */ + if (errno != EINVAL && errno != ENXIO) +#endif + fatal("ioctl failed"); } int @@ -585,6 +630,28 @@ window_unzoom(struct window *w) return (0); } +int +window_push_zoom(struct window *w, int flag) +{ + log_debug("%s: @%u %d", __func__, w->id, + flag && (w->flags & WINDOW_ZOOMED)); + if (flag && (w->flags & WINDOW_ZOOMED)) + w->flags |= WINDOW_WASZOOMED; + else + w->flags &= ~WINDOW_WASZOOMED; + return (window_unzoom(w) == 0); +} + +int +window_pop_zoom(struct window *w) +{ + log_debug("%s: @%u %d", __func__, w->id, + !!(w->flags & WINDOW_WASZOOMED)); + if (w->flags & WINDOW_WASZOOMED) + return (window_zoom(w->active) == 0); + return (0); +} + struct window_pane * window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, int flags) @@ -932,7 +999,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); - wp->flags |= PANE_RESIZE; + wp->flags |= (PANE_RESIZE|PANE_RESIZED); } /* @@ -71,6 +71,20 @@ xreallocarray(void *ptr, size_t nmemb, size_t size) return new_ptr; } +void * +xrecallocarray(void *ptr, size_t oldnmemb, size_t nmemb, size_t size) +{ + void *new_ptr; + + if (nmemb == 0 || size == 0) + fatalx("xrecallocarray: zero size"); + new_ptr = recallocarray(ptr, oldnmemb, nmemb, size); + if (new_ptr == NULL) + fatalx("xrecallocarray: allocating %zu * %zu bytes: %s", + nmemb, size, strerror(errno)); + return new_ptr; +} + char * xstrdup(const char *str) { @@ -27,6 +27,7 @@ void *xmalloc(size_t); void *xcalloc(size_t, size_t); void *xrealloc(void *, size_t); void *xreallocarray(void *, size_t, size_t); +void *xrecallocarray(void *, size_t, size_t, size_t); char *xstrdup(const char *); char *xstrndup(const char *, size_t); int xasprintf(char **, const char *, ...) |