diff --git a/cmd-break-pane.c b/cmd-break-pane.c index add3743b..790b8df2 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -73,7 +73,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) } server_unzoom_window(w); - if (window_count_panes(w) == 1) { + if (window_count_panes(w, 1) == 1) { if (server_link_window(src_s, wl, dst_s, idx, 0, !args_has(args, 'd'), &cause) != 0) { cmdq_error(item, "%s", cause); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 2fa2561f..5b6abc51 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -170,7 +170,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) } else server_status_session(dst_s); - if (window_count_panes(src_w) == 0) + if (window_count_panes(src_w, 1) == 0) server_kill_window(src_w, 1); else notify_window("window-layout-changed", src_w); diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 3cabe07e..51c29ec2 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -103,7 +103,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) * spawned without being visited (for example split-window -d). */ lastwp = TAILQ_FIRST(&w->last_panes); - if (lastwp == NULL && window_count_panes(w) == 2) { + if (lastwp == NULL && window_count_panes(w, 1) == 2) { lastwp = TAILQ_PREV(w->active, window_panes, entry); if (lastwp == NULL) lastwp = TAILQ_NEXT(w->active, entry); diff --git a/cmd-split-window.c b/cmd-split-window.c index 3d366a00..2a372ead 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -27,22 +27,23 @@ #include "tmux.h" /* - * Split a window (add a new pane). + * Create a new pane. */ #define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" -static enum cmd_retval cmd_split_window_exec(struct cmd *, - struct cmdq_item *); +static enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmdq_item *); -const struct cmd_entry cmd_split_window_entry = { - .name = "split-window", - .alias = "splitw", +const struct cmd_entry cmd_new_pane_entry = { + .name = "new-pane", + .alias = "newp", - .args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL }, - .usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] " - "[-F format] [-l size] " CMD_TARGET_PANE_USAGE - " [shell-command [argument ...]]", + .args = { "bc:de:fF:hIkl:m:p:PR:s:S:t:vZ", 0, -1, NULL }, + .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " + "[-F format] [-l size] [-m message] [-p percentage] " + "[-s style] [-S active-border-style] " + "[-R inactive-border-style] " CMD_TARGET_PANE_USAGE " " + "[shell-command [argument ...]]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -50,6 +51,46 @@ const struct cmd_entry cmd_split_window_entry = { .exec = cmd_split_window_exec }; +const struct cmd_entry cmd_split_window_entry = { + .name = "split-window", + .alias = "splitw", + + .args = { "bc:de:fF:hIkl:m:p:PR:s:S:t:vZ", 0, -1, NULL }, + .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " + "[-F format] [-l size] [-m message] [-p percentage] " + "[-s style] [-S active-border-style] " + "[-R inactive-border-style] " CMD_TARGET_PANE_USAGE " " + "[shell-command [argument ...]]", + + .target = { 't', CMD_FIND_PANE, 0 }, + + .flags = 0, + .exec = cmd_split_window_exec +}; + +static struct layout_cell * +cmd_split_window_get_tiled_layout_cell(struct cmdq_item *item, + struct args *args, struct window *w, struct window_pane *wp, int flags) +{ + enum layout_type type; + struct layout_cell *lc = NULL; + char *cause = NULL; + int size; + + if (window_pane_tile_geometry(w, wp, &size, &flags, &type, item, args, + &cause) != 0) { + cmdq_error(item, "invalid tiled geometry %s", cause); + free(cause); + return (NULL); + } + + window_push_zoom(wp->window, 1, args_has(args, 'Z')); + lc = layout_split_pane(wp, type, size, flags); + if (lc == NULL) + cmdq_error(item, "no space for new pane"); + return (lc); +} + static enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) { @@ -62,51 +103,14 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct winlink *wl = target->wl; struct window *w = wl->window; struct window_pane *wp = target->wp, *new_wp; - enum layout_type type; - struct layout_cell *lc; + struct layout_cell *lc = NULL; struct cmd_find_state fs; - int size, flags, input; - const char *template; + int flags, input; + const char *template, *style; char *cause = NULL, *cp; struct args_value *av; - u_int count = args_count(args), curval = 0; + u_int count = args_count(args); - type = LAYOUT_TOPBOTTOM; - if (args_has(args, 'h')) - type = LAYOUT_LEFTRIGHT; - - /* If the 'p' flag is dropped then this bit can be moved into 'l'. */ - if (args_has(args, 'l') || args_has(args, 'p')) { - if (args_has(args, 'f')) { - if (type == LAYOUT_TOPBOTTOM) - curval = w->sy; - else - curval = w->sx; - } else { - if (type == LAYOUT_TOPBOTTOM) - curval = wp->sy; - else - curval = wp->sx; - } - } - - size = -1; - if (args_has(args, 'l')) { - size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval, - item, &cause); - } else if (args_has(args, 'p')) { - size = args_strtonum_and_expand(args, 'p', 0, 100, item, - &cause); - if (cause == NULL) - size = curval * size / 100; - } - if (cause != NULL) { - cmdq_error(item, "size %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - window_push_zoom(wp->window, 1, args_has(args, 'Z')); input = (args_has(args, 'I') && count == 0); flags = 0; @@ -117,11 +121,9 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (input || (count == 1 && *args_string(args, 0) == '\0')) flags |= SPAWN_EMPTY; - lc = layout_split_pane(wp, type, size, flags); - if (lc == NULL) { - cmdq_error(item, "no space for new pane"); + lc = cmd_split_window_get_tiled_layout_cell(item, args, w, wp, flags); + if (lc == NULL) return (CMD_RETURN_ERROR); - } sc.item = item; sc.s = s; @@ -156,6 +158,44 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) environ_free(sc.environ); return (CMD_RETURN_ERROR); } + + style = args_get(args, 's'); + if (style != NULL) { + if (options_set_string(new_wp->options, "window-style", 0, + "%s", style) == NULL) { + cmdq_error(item, "bad style: %s", style); + return (CMD_RETURN_ERROR); + } + options_set_string(new_wp->options, "window-active-style", 0, + "%s", style); + new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED + |PANE_THEMECHANGED); + } + style = args_get(args, 'S'); + if (style != NULL) { + if (options_set_string(new_wp->options, + "pane-active-border-style", 0, "%s", style) == NULL) { + cmdq_error(item, "bad active border style: %s", style); + return (CMD_RETURN_ERROR); + } + } + style = args_get(args, 'R'); + if (style != NULL) { + if (options_set_string(new_wp->options, "pane-border-style", 0, + "%s", style) == NULL) { + cmdq_error(item, "bad inactive border style: %s", + style); + return (CMD_RETURN_ERROR); + } + } + if (args_has(args, 'k') || args_has(args, 'm')) { + options_set_number(new_wp->options, "remain-on-exit", 3); + if (args_has(args, 'm')) + options_set_string(new_wp->options, + "remain-on-exit-format", + 0, "%s", args_get(args, 'm')); + } + if (input) { switch (window_pane_start_input(new_wp, item, &cause)) { case -1: diff --git a/cmd.c b/cmd.c index d72d0c5f..6916da70 100644 --- a/cmd.c +++ b/cmd.c @@ -71,6 +71,7 @@ extern const struct cmd_entry cmd_lock_server_entry; extern const struct cmd_entry cmd_lock_session_entry; extern const struct cmd_entry cmd_move_pane_entry; extern const struct cmd_entry cmd_move_window_entry; +extern const struct cmd_entry cmd_new_pane_entry; extern const struct cmd_entry cmd_new_session_entry; extern const struct cmd_entry cmd_new_window_entry; extern const struct cmd_entry cmd_next_layout_entry; @@ -163,6 +164,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_lock_session_entry, &cmd_move_pane_entry, &cmd_move_window_entry, + &cmd_new_pane_entry, &cmd_new_session_entry, &cmd_new_window_entry, &cmd_next_layout_entry, diff --git a/file.c b/file.c index 2bf7a570..0e10b37a 100644 --- a/file.c +++ b/file.c @@ -399,6 +399,10 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) } for (;;) { size = fread(buffer, 1, sizeof buffer, f); + if (ferror(f)) { + cf->error = errno; + goto done; + } if (evbuffer_add(cf->buffer, buffer, size) != 0) { cf->error = ENOMEM; goto done; @@ -671,7 +675,7 @@ file_write_close(struct client_files *files, struct imsg *imsg) /* Client file read error callback. */ static void -file_read_error_callback(__unused struct bufferevent *bev, __unused short what, +file_read_error_callback(__unused struct bufferevent *bev, short what, void *arg) { struct client_file *cf = arg; @@ -680,7 +684,7 @@ file_read_error_callback(__unused struct bufferevent *bev, __unused short what, log_debug("read error file %d", cf->stream); msg.stream = cf->stream; - msg.error = 0; + msg.error = (what & EVBUFFER_ERROR) ? EIO : 0; proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg); bufferevent_free(cf->event); diff --git a/format.c b/format.c index 7cbd131f..9735be0e 100644 --- a/format.c +++ b/format.c @@ -2060,7 +2060,7 @@ static void * format_cb_pane_bottom(struct format_tree *ft) { if (ft->wp != NULL) - return (format_printf("%u", ft->wp->yoff + ft->wp->sy - 1)); + return (format_printf("%d", ft->wp->yoff + ft->wp->sy - 1)); return (NULL); } @@ -2219,7 +2219,7 @@ static void * format_cb_pane_left(struct format_tree *ft) { if (ft->wp != NULL) - return (format_printf("%u", ft->wp->xoff)); + return (format_printf("%d", ft->wp->xoff)); return (NULL); } @@ -2343,7 +2343,7 @@ static void * format_cb_pane_right(struct format_tree *ft) { if (ft->wp != NULL) - return (format_printf("%u", ft->wp->xoff + ft->wp->sx - 1)); + return (format_printf("%d", ft->wp->xoff + ft->wp->sx - 1)); return (NULL); } @@ -2385,7 +2385,7 @@ static void * format_cb_pane_top(struct format_tree *ft) { if (ft->wp != NULL) - return (format_printf("%u", ft->wp->yoff)); + return (format_printf("%d", ft->wp->yoff)); return (NULL); } @@ -2936,7 +2936,7 @@ static void * format_cb_window_panes(struct format_tree *ft) { if (ft->w != NULL) - return (format_printf("%u", window_count_panes(ft->w))); + return (format_printf("%u", window_count_panes(ft->w, 1))); return (NULL); } diff --git a/grid-reader.c b/grid-reader.c index be374cd7..421c2518 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -45,15 +45,20 @@ grid_reader_line_length(struct grid_reader *gr) /* Move cursor forward one position. */ void -grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all) +grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all, int onemore) { u_int px; struct grid_cell gc; if (all) px = gr->gd->sx; - else + else if (onemore) px = grid_reader_line_length(gr); + else { + px = grid_reader_line_length(gr); + if (px != 0) + px--; + } if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) { grid_reader_cursor_start_of_line(gr, 0); diff --git a/layout-custom.c b/layout-custom.c index 27d5bf82..aa36d2c4 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -27,10 +27,11 @@ static struct layout_cell *layout_find_bottomright(struct layout_cell *); static u_short layout_checksum(const char *); static int layout_append(struct layout_cell *, char *, size_t); -static struct layout_cell *layout_construct(struct layout_cell *, - const char **); +static int layout_construct(struct layout_cell *, + const char **, struct layout_cell **, + struct layout_cell **); static void layout_assign(struct window_pane **, - struct layout_cell *); + struct layout_cell *, int); /* Find the bottom-right cell. */ static struct layout_cell * @@ -58,14 +59,30 @@ layout_checksum(const char *layout) /* Dump layout as a string. */ char * -layout_dump(__unused struct window *w, struct layout_cell *root) +layout_dump(struct window *w, struct layout_cell *root) { - char layout[8192], *out; + char layout[8192], *out; + int bracket = 0; + struct window_pane *wp; *layout = '\0'; if (layout_append(root, layout, sizeof layout) != 0) return (NULL); + TAILQ_FOREACH(wp, &w->z_index, zentry) { + if (~wp->flags & PANE_FLOATING) + break; + if (!bracket) { + strlcat(layout, "<", sizeof layout); + bracket = 1; + } + if (layout_append(wp->layout_cell, layout, sizeof layout) != 0) + return (NULL); + strlcat(layout, ",", sizeof layout); + } + if (bracket) + layout[strlen(layout) - 1] = '>'; + xasprintf(&out, "%04hx,%s", layout_checksum(layout), layout); return (out); } @@ -81,12 +98,13 @@ layout_append(struct layout_cell *lc, char *buf, size_t len) if (len == 0) return (-1); - + if (lc == NULL) + return (0); if (lc->wp != NULL) { - tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u", + tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%d,%d,%u", lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id); } else { - tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u", + tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%d,%d", lc->sx, lc->sy, lc->xoff, lc->yoff); } if (tmplen > (sizeof tmp) - 1) @@ -109,6 +127,7 @@ layout_append(struct layout_cell *lc, char *buf, size_t len) } buf[strlen(buf) - 1] = brackets[0]; break; + case LAYOUT_FLOATING: case LAYOUT_WINDOWPANE: break; } @@ -124,6 +143,7 @@ layout_check(struct layout_cell *lc) u_int n = 0; switch (lc->type) { + case LAYOUT_FLOATING: case LAYOUT_WINDOWPANE: break; case LAYOUT_LEFTRIGHT: @@ -156,7 +176,7 @@ layout_check(struct layout_cell *lc) int layout_parse(struct window *w, const char *layout, char **cause) { - struct layout_cell *lc, *lcchild; + struct layout_cell *lcchild, *tiled_lc = NULL, *floating_lc = NULL; struct window_pane *wp; u_int npanes, ncells, sx = 0, sy = 0; u_short csum; @@ -174,20 +194,27 @@ layout_parse(struct window *w, const char *layout, char **cause) } /* Build the layout. */ - lc = layout_construct(NULL, &layout); - if (lc == NULL) { + if (layout_construct(NULL, &layout, &tiled_lc, &floating_lc) != 0) { *cause = xstrdup("invalid layout"); return (-1); } + if (tiled_lc == NULL) { + /* A stub layout cell for an empty window. */ + tiled_lc = layout_create_cell(NULL); + tiled_lc->type = LAYOUT_LEFTRIGHT; + layout_set_size(tiled_lc, w->sx, w->sy, 0, 0); + } if (*layout != '\0') { *cause = xstrdup("invalid layout"); goto fail; } /* Check this window will fit into the layout. */ + npanes = window_count_panes(w, 1); for (;;) { - npanes = window_count_panes(w); - ncells = layout_count_cells(lc); + ncells = layout_count_cells(tiled_lc); + if (floating_lc != NULL) + ncells += layout_count_cells(floating_lc); if (npanes > ncells) { xasprintf(cause, "have %u panes but need %u", npanes, ncells); @@ -196,9 +223,17 @@ layout_parse(struct window *w, const char *layout, char **cause) if (npanes == ncells) break; - /* Fewer panes than cells - close the bottom right. */ - lcchild = layout_find_bottomright(lc); - layout_destroy_cell(w, lcchild, &lc); + /* + * Fewer panes than cells - close floating panes first + * then close the bottom right until none remain. + */ + if (floating_lc != NULL && !TAILQ_EMPTY(&floating_lc->cells)) { + lcchild = TAILQ_FIRST(&floating_lc->cells); + layout_destroy_cell(w, lcchild, &floating_lc); + } else { + lcchild = layout_find_bottomright(tiled_lc); + layout_destroy_cell(w, lcchild, &tiled_lc); + } } /* @@ -206,91 +241,108 @@ layout_parse(struct window *w, const char *layout, char **cause) * an incorrect top cell size - if it is larger than the top child then * correct that (if this is still wrong the check code will catch it). */ - switch (lc->type) { + + switch (tiled_lc->type) { case LAYOUT_WINDOWPANE: break; case LAYOUT_LEFTRIGHT: - TAILQ_FOREACH(lcchild, &lc->cells, entry) { + TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) { sy = lcchild->sy + 1; sx += lcchild->sx + 1; } break; case LAYOUT_TOPBOTTOM: - TAILQ_FOREACH(lcchild, &lc->cells, entry) { + TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) { sx = lcchild->sx + 1; sy += lcchild->sy + 1; } break; + case LAYOUT_FLOATING: + *cause = xstrdup("invalid layout"); + goto fail; } - if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) { - log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy); - layout_print_cell(lc, __func__, 0); - lc->sx = sx - 1; lc->sy = sy - 1; + if (tiled_lc->type != LAYOUT_WINDOWPANE && + (tiled_lc->sx != sx || tiled_lc->sy != sy)) { + layout_print_cell(tiled_lc, __func__, 0); + tiled_lc->sx = sx - 1; tiled_lc->sy = sy - 1; } /* Check the new layout. */ - if (!layout_check(lc)) { + if (!layout_check(tiled_lc)) { *cause = xstrdup("size mismatch after applying layout"); goto fail; } - /* Resize to the layout size. */ - window_resize(w, lc->sx, lc->sy, -1, -1); + /* Resize window to the layout size. */ + if (sx != 0 && sy != 0) + window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1); /* Destroy the old layout and swap to the new. */ layout_free_cell(w->layout_root); - w->layout_root = lc; + w->layout_root = tiled_lc; /* Assign the panes into the cells. */ wp = TAILQ_FIRST(&w->panes); - layout_assign(&wp, lc); + if (tiled_lc != NULL) + layout_assign(&wp, tiled_lc, 0); + if (floating_lc != NULL) + layout_assign(&wp, floating_lc, PANE_FLOATING); /* Update pane offsets and sizes. */ layout_fix_offsets(w); layout_fix_panes(w, NULL); recalculate_sizes(); + layout_print_cell(tiled_lc, __func__, 0); - layout_print_cell(lc, __func__, 0); + /* Free the floating layout cell, no longer needed. */ + if (floating_lc != NULL) + layout_free_cell(floating_lc); notify_window("window-layout-changed", w); return (0); fail: - layout_free_cell(lc); + layout_free_cell(tiled_lc); + layout_free_cell(floating_lc); return (-1); } /* Assign panes into cells. */ static void -layout_assign(struct window_pane **wp, struct layout_cell *lc) +layout_assign(struct window_pane **wp, struct layout_cell *lc, int flags) { struct layout_cell *lcchild; + if (lc == NULL) + return; + switch (lc->type) { case LAYOUT_WINDOWPANE: layout_make_leaf(lc, *wp); + (*wp)->flags |= flags; *wp = TAILQ_NEXT(*wp, entry); return; case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: + case LAYOUT_FLOATING: TAILQ_FOREACH(lcchild, &lc->cells, entry) - layout_assign(wp, lcchild); + layout_assign(wp, lcchild, 1); return; } } -/* Construct a cell from all or part of a layout tree. */ static struct layout_cell * -layout_construct(struct layout_cell *lcparent, const char **layout) +layout_construct_cell(struct layout_cell *lcparent, const char **layout) { - struct layout_cell *lc, *lcchild; - u_int sx, sy, xoff, yoff; + struct layout_cell *lc; + u_int sx, sy; + int xoff, yoff; const char *saved; if (!isdigit((u_char) **layout)) return (NULL); - if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4) + if (sscanf(*layout, "%ux%u,%d,%d", &sx, &sy, &xoff, &yoff) != 4) return (NULL); while (isdigit((u_char) **layout)) @@ -325,17 +377,41 @@ layout_construct(struct layout_cell *lcparent, const char **layout) lc->xoff = xoff; lc->yoff = yoff; + return (lc); +} + +/* + * Given a character string layout, recursively construct cells. + * Possible return values: + * lc LAYOUT_WINDOWPANE, no children + * lc LAYOUT_LEFTRIGHT or LAYOUT_TOPBOTTOM, with children + * floating_lc LAYOUT_FLOATING, with children + */ +static int +layout_construct(struct layout_cell *lcparent, const char **layout, + struct layout_cell **lc, struct layout_cell **floating_lc) +{ + struct layout_cell *lcchild, *saved_lc; + + *lc = layout_construct_cell(lcparent, layout); + switch (**layout) { case ',': case '}': case ']': + case '>': case '\0': - return (lc); + return (0); case '{': - lc->type = LAYOUT_LEFTRIGHT; + (*lc)->type = LAYOUT_LEFTRIGHT; break; case '[': - lc->type = LAYOUT_TOPBOTTOM; + (*lc)->type = LAYOUT_TOPBOTTOM; + break; + case '<': + saved_lc = *lc; + *lc = layout_create_cell(lcparent); + (*lc)->type = LAYOUT_FLOATING; break; default: goto fail; @@ -343,13 +419,12 @@ layout_construct(struct layout_cell *lcparent, const char **layout) do { (*layout)++; - lcchild = layout_construct(lc, layout); - if (lcchild == NULL) + if (layout_construct(*lc, layout, &lcchild, floating_lc) != 0) goto fail; - TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry); + TAILQ_INSERT_TAIL(&(*lc)->cells, lcchild, entry); } while (**layout == ','); - switch (lc->type) { + switch ((*lc)->type) { case LAYOUT_LEFTRIGHT: if (**layout != '}') goto fail; @@ -358,14 +433,21 @@ layout_construct(struct layout_cell *lcparent, const char **layout) if (**layout != ']') goto fail; break; + case LAYOUT_FLOATING: + if (**layout != '>') + goto fail; + *floating_lc = *lc; + *lc = saved_lc; + break; default: goto fail; } (*layout)++; - return (lc); + return (0); fail: - layout_free_cell(lc); - return (NULL); + layout_free_cell(*lc); + layout_free_cell(*floating_lc); + return (-1); } diff --git a/layout-set.c b/layout-set.c index bd68f663..f9944ed9 100644 --- a/layout-set.c +++ b/layout-set.c @@ -133,7 +133,7 @@ layout_set_even(struct window *w, enum layout_type type) layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ - n = window_count_panes(w); + n = window_count_panes(w, 0); if (n <= 1) return; @@ -201,7 +201,7 @@ layout_set_main_h(struct window *w) layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ - n = window_count_panes(w); + n = window_count_panes(w, 0); if (n <= 1) return; n--; /* take off main pane */ @@ -299,7 +299,7 @@ layout_set_main_h_mirrored(struct window *w) layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ - n = window_count_panes(w); + n = window_count_panes(w, 0); if (n <= 1) return; n--; /* take off main pane */ @@ -397,7 +397,7 @@ layout_set_main_v(struct window *w) layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ - n = window_count_panes(w); + n = window_count_panes(w, 0); if (n <= 1) return; n--; /* take off main pane */ @@ -495,7 +495,7 @@ layout_set_main_v_mirrored(struct window *w) layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ - n = window_count_panes(w); + n = window_count_panes(w, 0); if (n <= 1) return; n--; /* take off main pane */ @@ -593,7 +593,7 @@ layout_set_tiled(struct window *w) layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ - n = window_count_panes(w); + n = window_count_panes(w, 0); if (n <= 1) return; diff --git a/layout.c b/layout.c index d3df5e7f..5afd697b 100644 --- a/layout.c +++ b/layout.c @@ -46,7 +46,10 @@ static int layout_set_size_check(struct window *, struct layout_cell *, enum layout_type, int); static void layout_resize_child_cells(struct window *, struct layout_cell *); +void layout_redistribute_cells(struct window *, struct layout_cell *, + enum layout_type); +/* Create a new layout cell. */ struct layout_cell * layout_create_cell(struct layout_cell *lcparent) { @@ -61,14 +64,15 @@ layout_create_cell(struct layout_cell *lcparent) lc->sx = UINT_MAX; lc->sy = UINT_MAX; - lc->xoff = UINT_MAX; - lc->yoff = UINT_MAX; + lc->xoff = INT_MAX; + lc->yoff = INT_MAX; lc->wp = NULL; return (lc); } +/* Free a layout cell. */ void layout_free_cell(struct layout_cell *lc) { @@ -83,21 +87,40 @@ layout_free_cell(struct layout_cell *lc) layout_free_cell(lcchild); } break; + case LAYOUT_FLOATING: + /* + * A floating layout cell is only used temporarily while + * select-layout constructs a layout. Remove the children from + * the temporary layout, then free temporary floating layout + * cell. Each floating pane has stub layout. + */ + while (!TAILQ_EMPTY(&lc->cells)) { + lcchild = TAILQ_FIRST(&lc->cells); + TAILQ_REMOVE(&lc->cells, lcchild, entry); + lcchild->parent = NULL; + } + break; case LAYOUT_WINDOWPANE: - if (lc->wp != NULL) + if (lc->wp != NULL) { + lc->wp->layout_cell->parent = NULL; lc->wp->layout_cell = NULL; + } break; } free(lc); } +/* Log a cell. */ void layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) { struct layout_cell *lcchild; const char *type; + if (lc == NULL) + return; + switch (lc->type) { case LAYOUT_LEFTRIGHT: type = "LEFTRIGHT"; @@ -105,6 +128,9 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) case LAYOUT_TOPBOTTOM: type = "TOPBOTTOM"; break; + case LAYOUT_FLOATING: + type = "FLOATING"; + break; case LAYOUT_WINDOWPANE: type = "WINDOWPANE"; break; @@ -112,12 +138,13 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) type = "UNKNOWN"; break; } - log_debug("%s:%*s%p type %s [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, + log_debug("%s:%*s%p type %s [parent %p] wp=%p [%d,%d %ux%u]", hdr, n, " ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, lc->sy); switch (lc->type) { case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: + case LAYOUT_FLOATING: TAILQ_FOREACH(lcchild, &lc->cells, entry) layout_print_cell(lcchild, hdr, n + 1); break; @@ -126,14 +153,17 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) } } +/* Search for a cell by the border position. */ struct layout_cell * layout_search_by_border(struct layout_cell *lc, u_int x, u_int y) { struct layout_cell *lcchild, *last = NULL; TAILQ_FOREACH(lcchild, &lc->cells, entry) { - if (x >= lcchild->xoff && x < lcchild->xoff + lcchild->sx && - y >= lcchild->yoff && y < lcchild->yoff + lcchild->sy) { + if ((int)x >= lcchild->xoff && + (int)x < lcchild->xoff + (int)lcchild->sx && + (int)y >= lcchild->yoff && + (int)y < lcchild->yoff + (int)lcchild->sy) { /* Inside the cell - recurse. */ return (layout_search_by_border(lcchild, x, y)); } @@ -145,14 +175,17 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y) switch (lc->type) { case LAYOUT_LEFTRIGHT: - if (x < lcchild->xoff && x >= last->xoff + last->sx) + if ((int)x < lcchild->xoff && + (int)x >= last->xoff + (int)last->sx) return (last); break; case LAYOUT_TOPBOTTOM: - if (y < lcchild->yoff && y >= last->yoff + last->sy) + if ((int)y < lcchild->yoff && + (int)y >= last->yoff + (int)last->sy) return (last); break; case LAYOUT_WINDOWPANE: + case LAYOUT_FLOATING: break; } @@ -162,9 +195,9 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y) return (NULL); } +/* Set cell size. */ void -layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, - u_int yoff) +layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, int xoff, int yoff) { lc->sx = sx; lc->sy = sy; @@ -173,6 +206,7 @@ layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, lc->yoff = yoff; } +/* Make a cell a leaf cell. */ void layout_make_leaf(struct layout_cell *lc, struct window_pane *wp) { @@ -184,6 +218,7 @@ layout_make_leaf(struct layout_cell *lc, struct window_pane *wp) lc->wp = wp; } +/* Make a cell a node cell. */ void layout_make_node(struct layout_cell *lc, enum layout_type type) { @@ -203,7 +238,7 @@ static void layout_fix_offsets1(struct layout_cell *lc) { struct layout_cell *lcchild; - u_int xoff, yoff; + int xoff, yoff; if (lc->type == LAYOUT_LEFTRIGHT) { xoff = lc->xoff; @@ -246,6 +281,8 @@ layout_cell_is_top(struct window *w, struct layout_cell *lc) while (lc != w->layout_root) { next = lc->parent; + if (next == NULL) + return (0); if (next->type == LAYOUT_TOPBOTTOM && lc != TAILQ_FIRST(&next->cells)) return (0); @@ -262,6 +299,8 @@ layout_cell_is_bottom(struct window *w, struct layout_cell *lc) while (lc != w->layout_root) { next = lc->parent; + if (next == NULL) + return (0); if (next->type == LAYOUT_TOPBOTTOM && lc != TAILQ_LAST(&next->cells, layout_cells)) return (0); @@ -307,7 +346,8 @@ layout_fix_panes(struct window *w, struct window_pane *skip) sx = lc->sx; sy = lc->sy; - if (layout_add_horizontal_border(w, lc, status)) { + if ((~wp->flags & PANE_FLOATING) && + layout_add_horizontal_border(w, lc, status)) { if (status == PANE_STATUS_TOP) wp->yoff++; sy--; @@ -346,14 +386,14 @@ u_int layout_count_cells(struct layout_cell *lc) { struct layout_cell *lcchild; - u_int count; + u_int count = 0; switch (lc->type) { case LAYOUT_WINDOWPANE: return (1); case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: - count = 0; + case LAYOUT_FLOATING: TAILQ_FOREACH(lcchild, &lc->cells, entry) count += layout_count_cells(lcchild); return (count); @@ -470,13 +510,22 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc, struct layout_cell *lcother, *lcparent; /* - * If no parent, this is the last pane so window close is imminent and - * there is no need to resize anything. + * If no parent, this is either a floating pane or the last + * pane so window close is imminent and there is no need to + * resize anything. */ lcparent = lc->parent; if (lcparent == NULL) { + if (lc->wp != NULL && ~lc->wp->flags & PANE_FLOATING) + *lcroot = NULL; + layout_free_cell(lc); + return; + } + + /* A floating cell need only be removed from the parent. */ + if (lcparent->type == LAYOUT_FLOATING) { + TAILQ_REMOVE(&lcparent->cells, lc, entry); layout_free_cell(lc); - *lcroot = NULL; return; } @@ -499,12 +548,13 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc, * replace it by that cell. */ lc = TAILQ_FIRST(&lcparent->cells); - if (TAILQ_NEXT(lc, entry) == NULL) { + if (lc != NULL && TAILQ_NEXT(lc, entry) == NULL) { TAILQ_REMOVE(&lcparent->cells, lc, entry); lc->parent = lcparent->parent; if (lc->parent == NULL) { - lc->xoff = 0; lc->yoff = 0; + lc->xoff = 0; + lc->yoff = 0; *lcroot = lc; } else TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry); @@ -513,6 +563,7 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc, } } +/* Initialize layout for pane. */ void layout_init(struct window *w, struct window_pane *wp) { @@ -524,6 +575,7 @@ layout_init(struct window *w, struct window_pane *wp) layout_fix_panes(w, NULL); } +/* Free layout for pane. */ void layout_free(struct window *w) { @@ -741,7 +793,7 @@ layout_resize_pane_shrink(struct window *w, struct layout_cell *lc, return (size); } -/* Assign window pane to newly split cell. */ +/* Assign window pane to new cell. */ void layout_assign_pane(struct layout_cell *lc, struct window_pane *wp, int do_not_resize) @@ -1085,6 +1137,9 @@ layout_close_pane(struct window_pane *wp) { struct window *w = wp->window; + if (wp->layout_cell == NULL) + return; + /* Remove the cell. */ layout_destroy_cell(w, wp->layout_cell, &w->layout_root); diff --git a/server-client.c b/server-client.c index f7bc2037..a8151c2a 100644 --- a/server-client.c +++ b/server-client.c @@ -1033,6 +1033,7 @@ server_client_is_bracket_paste(struct client *c, key_code key) { if ((key & KEYC_MASK_KEY) == KEYC_PASTE_START) { c->flags |= CLIENT_BRACKETPASTING; + c->paste_time = current_time; log_debug("%s: bracket paste on", c->name); return (0); } @@ -1066,6 +1067,7 @@ server_client_is_assume_paste(struct client *c) if (c->flags & CLIENT_ASSUMEPASTING) return (1); c->flags |= CLIENT_ASSUMEPASTING; + c->paste_time = current_time; log_debug("%s: assume paste on", c->name); return (0); } @@ -2133,7 +2135,7 @@ server_client_set_progress_bar(struct client *c) struct session *s = c->session; struct progress_bar *pane_pb; - if (s->curw == NULL) + if (s->curw == NULL || s->curw->window->active == NULL) return; pane_pb = &s->curw->window->active->base.progress_bar; if (pane_pb->state == c->progress_bar.state && @@ -2505,6 +2507,13 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->out_fd = -1; } + /* If pasting has taken too long, turn it off. */ + if (c->flags & (CLIENT_BRACKETPASTING|CLIENT_ASSUMEPASTING) && + current_time - c->paste_time > CLIENT_PASTE_TIME_LIMIT) { + log_debug("%s: paste time limit exceeded", c->name); + c->flags &= ~(CLIENT_BRACKETPASTING|CLIENT_ASSUMEPASTING); + } + /* * If this is the first client, load configuration files. Any later * clients are allowed to continue with their command even if the diff --git a/server-fn.c b/server-fn.c index 21cd23e1..16cda756 100644 --- a/server-fn.c +++ b/server-fn.c @@ -184,7 +184,7 @@ server_kill_pane(struct window_pane *wp) { struct window *w = wp->window; - if (window_count_panes(w) == 1) { + if (window_count_panes(w, 1) == 1) { server_kill_window(w, 1); recalculate_sizes(); } else { diff --git a/sort.c b/sort.c index 8a8f7028..dcacfb1d 100644 --- a/sort.c +++ b/sort.c @@ -56,8 +56,8 @@ sort_buffer_cmp(const void *a0, const void *b0) struct sort_criteria *sort_crit = sort_criteria; const struct paste_buffer *const *a = a0; const struct paste_buffer *const *b = b0; - const struct paste_buffer *pa = *a; - const struct paste_buffer *pb = *b; + const struct paste_buffer *pa = *a; + const struct paste_buffer *pb = *b; int result = 0; switch (sort_crit->order) { @@ -92,8 +92,8 @@ sort_client_cmp(const void *a0, const void *b0) struct sort_criteria *sort_crit = sort_criteria; const struct client *const *a = a0; const struct client *const *b = b0; - const struct client *ca = *a; - const struct client *cb = *b; + const struct client *ca = *a; + const struct client *cb = *b; int result = 0; switch (sort_crit->order) { @@ -425,7 +425,11 @@ sort_get_clients(u_int *n, struct sort_criteria *sort_crit) i = 0; TAILQ_FOREACH(c, &clients, entry) { - if (lsz <= i) { + if (c->flags & CLIENT_UNATTACHEDFLAGS) + continue; + if (~c->flags & CLIENT_ATTACHED) + continue; + if (lsz <= i) { lsz += 100; l = xreallocarray(l, lsz, sizeof *l); } @@ -468,7 +472,7 @@ sort_get_panes(u_int *n, struct sort_criteria *sort_crit) struct winlink *wl; struct window *w; struct window_pane *wp; - u_int i; + u_int i; static struct window_pane **l = NULL; static u_int lsz = 0; @@ -499,7 +503,7 @@ sort_get_panes_session(struct session *s, u_int *n, struct winlink *wl = NULL; struct window *w = NULL; struct window_pane *wp = NULL; - u_int i; + u_int i; static struct window_pane **l = NULL; static u_int lsz = 0; @@ -526,7 +530,7 @@ sort_get_panes_window(struct window *w, u_int *n, struct sort_criteria *sort_crit) { struct window_pane *wp; - u_int i; + u_int i; static struct window_pane **l = NULL; static u_int lsz = 0; diff --git a/tmux.1 b/tmux.1 index fe360159..73517bad 100644 --- a/tmux.1 +++ b/tmux.1 @@ -936,6 +936,37 @@ or .Ic list\-panes commands. .Pp +.Em target\-session , +.Em target\-window +or +.Em target\-pane +each denote the +.Ql type +of target the command needs to work on. +If a target is not explicitly qualified using +.Ql \&: +and +.Ql \&. , +.Nm +will pick what seems to be the best choice available. +For example, if a +.Em target-pane +of +.Ql 1 +is used with a current window that has only one pane, +.Nm +knows that the +.Ql 1 +cannot mean a pane, so it will look for the active pane in window 1, or the +active pane in the current window in session 1. +Only if none of these are present will it report an error. +If it is important that a specific pane, window or session always be used, +such as from a script, the target should be fully qualified (for example a +.Em target-pane +of +.Ql -t:.1 ) , +or pane, window or session IDs should be used. +.Pp .Ar shell\-command arguments are .Xr sh 1 @@ -3270,6 +3301,98 @@ By default, it uses the format .Ql #{session_name}:#{window_index} but a different format may be specified with .Fl F . +.Tg newp +.It Xo Ic new\-pane +.Op Fl bdefhIkPvZ +.Op Fl c Ar start\-directory +.Op Fl e Ar environment +.Op Fl F Ar format +.Op Fl l Ar size +.Op Fl m Ar message +.Op Fl p Ar percentage +.Op Fl R Ar inactive\-border\-style +.Op Fl s Ar style +.Op Fl S Ar active\-border\-style +.Op Fl t Ar target\-pane +.Op Ar shell\-command Op Ar argument ... +.Xc +.D1 Pq alias: Ic newp +Create a new pane. +The new pane is created by splitting +.Ar target\-pane . +If +.Fl d +is given, the session does not make the new pane the current pane. +.Fl Z +zooms if the window is not zoomed, or keeps it zoomed if already zoomed. +.Fl s +sets the style for the pane content. +.Fl S +sets the border style when the pane is active and +.Fl R +sets the border style when the pane is inactive (see +.Sx STYLES ) . +.Pp +.Fl h +does a horizontal split and +.Fl v +a vertical split; if neither is specified, +.Fl v +is assumed. +The +.Fl l +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. +.Fl p +is a shorthand option for this. +The +.Fl b +option causes the new pane to be created to the left of or above +.Ar target\-pane . +The +.Fl f +option creates a new pane spanning the full window height (with +.Fl h ) +or full window width (with +.Fl v ) , +instead of splitting the active pane. +.Pp +.Fl k +keeps the pane open after the optional +.Ar shell\-command +exits and waits for a key to be pressed before closing it. +The message shown is controlled by the +.Ic remain\-on\-exit\-format +option. +.Fl m Ar message +is equivalent to +.Fl k +but also sets the +.Ic remain\-on\-exit\-format +option for this pane to +.Ar message . +.Pp +An empty +.Ar shell\-command +(\[aq]\[aq]) will create a pane with no command running in it. +The +.Fl I +flag (if +.Ar shell\-command +is not specified or empty) +will create an empty pane and forward any output from stdin to it. +For example: +.Bd -literal -offset indent +$ make 2>&1|tmux splitw \-dI & +.Ed +.Pp +All other options have the same meaning as for the +.Ic new\-window +command. .Tg nextl .It Ic next\-layout Op Fl t Ar target\-window .D1 Pq alias: Ic nextl @@ -3594,65 +3717,28 @@ the command behaves like .Ic last\-window . .Tg splitw .It Xo Ic split\-window -.Op Fl bdfhIvPZ +.Op Fl bdefhIkPvZ .Op Fl c Ar start\-directory .Op Fl e Ar environment .Op Fl F Ar format .Op Fl l Ar size +.Op Fl m Ar message +.Op Fl p Ar percentage +.Op Fl R Ar inactive\-border\-style +.Op Fl s Ar style +.Op Fl S Ar active\-border\-style .Op Fl t Ar target\-pane .Op Ar shell\-command Op Ar argument ... .Xc .D1 Pq alias: Ic splitw -Create a new pane by splitting -.Ar target\-pane : -.Fl h -does a horizontal split and -.Fl v -a vertical split; if neither is specified, -.Fl v -is assumed. -The -.Fl l -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 +Creates a new pane by splitting .Ar target\-pane . -The -.Fl f -option creates a new pane spanning the full window height (with -.Fl h ) -or full window width (with -.Fl v ) , -instead of splitting the active pane. -.Fl Z -zooms if the window is not zoomed, or keeps it zoomed if already zoomed. +Shares behavior with +.Ic new\-pane . .Pp -An empty -.Ar shell\-command -(\[aq]\[aq]) will create a pane with no command running in it. -Output can be sent to such a pane with the -.Ic display\-message -command. -The -.Fl I -flag (if -.Ar shell\-command -is not specified or empty) -will create an empty pane and forward any output from stdin to it. -For example: -.Bd -literal -offset indent -$ make 2>&1|tmux splitw \-dI & -.Ed -.Pp -All other options have the same meaning as for the -.Ic new\-window -command. +See +.Ic new\-pane +for more details. .Tg swapp .It Xo Ic swap\-pane .Op Fl dDUZ diff --git a/tmux.h b/tmux.h index c1fff4ae..2c978046 100644 --- a/tmux.h +++ b/tmux.h @@ -1336,9 +1336,11 @@ struct window_pane { TAILQ_ENTRY(window_pane) entry; /* link in list of all panes */ TAILQ_ENTRY(window_pane) sentry; /* link in list of last visited */ + TAILQ_ENTRY(window_pane) zentry; /* z-index link in list of all panes */ RB_ENTRY(window_pane) tree_entry; }; TAILQ_HEAD(window_panes, window_pane); +TAILQ_HEAD(window_panes_zindex, window_pane); RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ @@ -1358,6 +1360,7 @@ struct window { struct window_pane *active; struct window_panes last_panes; + struct window_panes z_index; struct window_panes panes; int lastlayout; @@ -1451,6 +1454,7 @@ TAILQ_HEAD(winlink_stack, winlink); enum layout_type { LAYOUT_LEFTRIGHT, LAYOUT_TOPBOTTOM, + LAYOUT_FLOATING, LAYOUT_WINDOWPANE }; @@ -1466,8 +1470,8 @@ struct layout_cell { u_int sx; u_int sy; - u_int xoff; - u_int yoff; + int xoff; + int yoff; struct window_pane *wp; struct layout_cells cells; @@ -1747,10 +1751,10 @@ struct tty_ctx { u_int orlower; /* Target region (usually pane) offset and size. */ - u_int xoff; - u_int yoff; - u_int rxoff; - u_int ryoff; + int xoff; + int yoff; + int rxoff; + int ryoff; u_int sx; u_int sy; @@ -1982,6 +1986,9 @@ struct client_window { }; RB_HEAD(client_windows, client_window); +/* Maximum time to be pasting. */ +#define CLIENT_PASTE_TIME_LIMIT 5 + /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); @@ -2121,6 +2128,7 @@ struct client { struct key_table *keytable; key_code last_key; + time_t paste_time; uint64_t redraw_panes; uint64_t redraw_scrollbars; @@ -2317,6 +2325,7 @@ struct spawn_context { #define SPAWN_FULLSIZE 0x20 #define SPAWN_EMPTY 0x40 #define SPAWN_ZOOM 0x80 +#define SPAWN_FLOATING 0x100 }; /* Paste buffer. */ @@ -3206,7 +3215,7 @@ void grid_reader_start(struct grid_reader *, struct grid *, u_int, u_int); void grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *); u_int grid_reader_line_length(struct grid_reader *); int grid_reader_in_set(struct grid_reader *, const char *); -void grid_reader_cursor_right(struct grid_reader *, int, int); +void grid_reader_cursor_right(struct grid_reader *, int, int, int); void grid_reader_cursor_left(struct grid_reader *, int); void grid_reader_cursor_down(struct grid_reader *); void grid_reader_cursor_up(struct grid_reader *); @@ -3409,7 +3418,7 @@ struct window_pane *window_pane_next_by_number(struct window *, struct window_pane *window_pane_previous_by_number(struct window *, struct window_pane *, u_int); int window_pane_index(struct window_pane *, u_int *); -u_int window_count_panes(struct window *); +u_int window_count_panes(struct window *, int); void window_destroy_panes(struct window *); struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); @@ -3463,6 +3472,9 @@ enum client_theme window_pane_get_theme(struct window_pane *); void window_pane_send_theme_update(struct window_pane *); struct style_range *window_pane_border_status_get_range(struct window_pane *, u_int, u_int); +int window_pane_tile_geometry(struct window *, + struct window_pane *, int *, int *, enum layout_type *, + struct cmdq_item *, struct args *, char **); /* layout.c */ u_int layout_count_cells(struct layout_cell *); @@ -3474,8 +3486,7 @@ void layout_destroy_cell(struct window *, struct layout_cell *, void layout_resize_layout(struct window *, struct layout_cell *, enum layout_type, int, int); struct layout_cell *layout_search_by_border(struct layout_cell *, u_int, u_int); -void layout_set_size(struct layout_cell *, u_int, u_int, u_int, - u_int); +void layout_set_size(struct layout_cell *, u_int, u_int, int, int); void layout_make_leaf(struct layout_cell *, struct window_pane *); void layout_make_node(struct layout_cell *, enum layout_type); void layout_fix_offsets(struct window *); diff --git a/tty-draw.c b/tty-draw.c index b5ecbe50..865fff60 100644 --- a/tty-draw.c +++ b/tty-draw.c @@ -29,7 +29,6 @@ enum tty_draw_line_state { TTY_DRAW_LINE_NEW2, TTY_DRAW_LINE_EMPTY, TTY_DRAW_LINE_SAME, - TTY_DRAW_LINE_PAD, TTY_DRAW_LINE_DONE }; static const char* tty_draw_line_states[] = { @@ -39,7 +38,6 @@ static const char* tty_draw_line_states[] = { "NEW2", "EMPTY", "SAME", - "PAD", "DONE" }; @@ -100,25 +98,6 @@ tty_draw_line_clear(struct tty *tty, u_int px, u_int py, u_int nx, } } -/* Is this cell empty? */ -static u_int -tty_draw_line_get_empty(const struct grid_cell *gc, u_int nx) -{ - u_int empty = 0; - - if (gc->data.width != 1 && gc->data.width > nx) - empty = nx; - else if (gc->attr == 0 && gc->link == 0) { - if (gc->flags & GRID_FLAG_CLEARED) - empty = 1; - else if (gc->flags & GRID_FLAG_TAB) - empty = gc->data.width; - else if (gc->data.size == 1 && *gc->data.data == ' ') - empty = 1; - } - return (empty); -} - /* Draw a line from screen to tty. */ void tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, @@ -239,6 +218,9 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, next_state = TTY_DRAW_LINE_DONE; gcp = &grid_default_cell; } else { + if (i > nx) + fatalx("position %u > width %u", i, nx); + /* Get the current cell. */ grid_view_get_cell(gd, px + i, py, &gc); @@ -253,20 +235,36 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, } /* Work out the the empty width. */ - if (px >= ex || i >= ex - px) + empty = 0; + if (px >= ex || i >= ex - px) { + /* Outside the area being drawn. */ empty = 1; - else if (gcp->bg != last.bg) - empty = 0; - else - empty = tty_draw_line_get_empty(gcp, nx - i); + } else if (gcp->data.width > nx - i) { + /* Wide character that has been truncated. */ + empty = nx - i; + } else if (gcp->flags & GRID_FLAG_PADDING) { + /* Orphan padding cell. */ + empty = 1; + } else if (gcp->bg == last.bg && gcp->attr == 0 && + gcp->link == 0) { + /* + * No attributes - empty if cleared, tab or + * space. + */ + if (gcp->flags & GRID_FLAG_CLEARED) + empty = 1; + else if (gcp->flags & GRID_FLAG_TAB) + empty = gcp->data.width; + else if (gcp->data.size == 1 && + *gcp->data.data == ' ') + empty = 1; + } /* Work out the next state. */ if (empty != 0) next_state = TTY_DRAW_LINE_EMPTY; else if (current_state == TTY_DRAW_LINE_FIRST) next_state = TTY_DRAW_LINE_SAME; - else if (gcp->flags & GRID_FLAG_PADDING) - next_state = TTY_DRAW_LINE_PAD; else if (grid_cells_look_equal(gcp, &last)) { if (gcp->data.size > (sizeof buf) - len) next_state = TTY_DRAW_LINE_FLUSH; @@ -312,8 +310,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, } /* Append the cell if it is not empty and not padding. */ - if (next_state != TTY_DRAW_LINE_EMPTY && - next_state != TTY_DRAW_LINE_PAD) { + if (next_state != TTY_DRAW_LINE_EMPTY) { memcpy(buf + len, gcp->data.data, gcp->data.size); len += gcp->data.size; width += gcp->data.width; diff --git a/tty-features.c b/tty-features.c index 74e7922f..b4340e2e 100644 --- a/tty-features.c +++ b/tty-features.c @@ -98,7 +98,7 @@ static const char *tty_feature_hyperlinks_capabilities[] = { #if defined (__OpenBSD__) || (defined(NCURSES_VERSION_MAJOR) && \ (NCURSES_VERSION_MAJOR > 5 || \ (NCURSES_VERSION_MAJOR == 5 && NCURSES_VERSION_MINOR > 8))) - "*:Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\", + "Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\", #endif NULL }; @@ -527,8 +527,13 @@ tty_default_features(int *feat, const char *name, u_int version) }, { .name = "foot", .features = TTY_FEATURES_BASE_MODERN_XTERM "," + "ccolour," "cstyle," - "extkeys" + "extkeys," + "usstyle," + "sync," + "osc7," + "hyperlinks" }, { .name = "WezTerm", .features = TTY_FEATURES_BASE_MODERN_XTERM "," diff --git a/window-copy.c b/window-copy.c index af80b8b4..a02c99c1 100644 --- a/window-copy.c +++ b/window-copy.c @@ -119,6 +119,8 @@ static void window_copy_copy_line(struct window_mode_entry *, char **, static int window_copy_in_set(struct window_mode_entry *, u_int, u_int, const char *); static u_int window_copy_find_length(struct window_mode_entry *, u_int); +static u_int window_copy_cursor_limit(struct window_mode_entry *, u_int, + int); static void window_copy_cursor_start_of_line(struct window_mode_entry *); static void window_copy_cursor_back_to_indentation( struct window_mode_entry *); @@ -898,9 +900,9 @@ window_copy_get_line(struct window_pane *wp, u_int y) { struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); struct window_copy_mode_data *data = wme->data; - struct grid *gd = data->screen.grid; + struct grid *gd = data->backing->grid; - return (format_grid_line(gd, gd->hsize + y)); + return (format_grid_line(gd, gd->hsize + y - data->oy)); } char * @@ -1662,7 +1664,7 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) window_copy_other_end(wme); data->cy = screen_size_y(&data->screen) - 1; - data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy); + data->cx = window_copy_cursor_limit(wme, screen_hsize(s) + data->cy, 0); data->oy = 0; if (data->searchmark != NULL && !data->timeout) @@ -2683,6 +2685,8 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->cx = data->searchx; data->cy = data->searchy; data->oy = data->searcho; + data->cx = window_copy_cursor_limit(wme, + screen_hsize(data->backing) + data->cy - data->oy, 0); action = WINDOW_COPY_CMD_REDRAW; } if (*arg0 == '\0') { @@ -2738,6 +2742,8 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->cx = data->searchx; data->cy = data->searchy; data->oy = data->searcho; + data->cx = window_copy_cursor_limit(wme, + screen_hsize(data->backing) + data->cy - data->oy, 0); action = WINDOW_COPY_CMD_REDRAW; } if (*arg0 == '\0') { @@ -5151,7 +5157,17 @@ window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct screen_write_ctx ctx; - u_int old_cx, old_cy, width, content_sx; + u_int old_cx, old_cy, py, width, content_sx; + u_int maxx; + int allow_onemore; + + allow_onemore = (data->screen.sel != NULL && data->rectflag); + if (cy < screen_size_y(s)) { + py = screen_hsize(data->backing) + cy - data->oy; + maxx = window_copy_cursor_limit(wme, py, allow_onemore); + if (cx > maxx) + cx = maxx; + } old_cx = data->cx; old_cy = data->cy; data->cx = cx; data->cy = cy; @@ -5644,7 +5660,7 @@ window_copy_clear_selection(struct window_mode_entry *wme) data->selflag = SEL_CHAR; py = screen_hsize(data->backing) + data->cy - data->oy; - px = window_copy_find_length(wme, py); + px = window_copy_cursor_limit(wme, py, data->rectflag); if (data->cx > px) window_copy_update_cursor(wme, px, data->cy); } @@ -5666,6 +5682,22 @@ window_copy_find_length(struct window_mode_entry *wme, u_int py) return (grid_line_length(data->backing->grid, py)); } +static u_int +window_copy_cursor_limit(struct window_mode_entry *wme, u_int py, + int allow_onemore) +{ + struct options *oo = wme->wp->window->options; + u_int len; + + len = window_copy_find_length(wme, py); + if (allow_onemore || + options_get_number(oo, "mode-keys") != MODEKEY_VI) + return (len); + if (len == 0) + return (0); + return (len - 1); +} + static void window_copy_cursor_start_of_line(struct window_mode_entry *wme) { @@ -5723,6 +5755,8 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme) else grid_reader_cursor_end_of_line(&gr, 1, 0); grid_reader_get_cursor(&gr, &px, &py); + if (data->screen.sel == NULL || !data->rectflag) + px = window_copy_cursor_limit(wme, py, 0); window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), data->oy, oldy, px, py, 0); } @@ -5773,6 +5807,10 @@ window_copy_other_end(struct window_mode_entry *wme) data->cy = screen_size_y(s) - 1; } else data->cy = cy + sely - yy; + yy = screen_hsize(data->backing) + data->cy - data->oy; + hsize = window_copy_cursor_limit(wme, yy, data->rectflag); + if (data->cx > hsize) + data->cx = hsize; window_copy_update_selection(wme, 1, 1); window_copy_redraw_screen(wme); @@ -5800,18 +5838,22 @@ window_copy_cursor_left(struct window_mode_entry *wme) static void window_copy_cursor_right(struct window_mode_entry *wme, int all) { + struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; + struct options *oo = wp->window->options; struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, oldy, hsize; + int onemore; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; oldy = data->cy; + onemore = (options_get_number(oo, "mode-keys") != MODEKEY_VI); grid_reader_start(&gr, back_s->grid, px, py); - grid_reader_cursor_right(&gr, 1, all); + grid_reader_cursor_right(&gr, 1, all, onemore); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), data->oy, oldy, px, py, 0); @@ -6026,20 +6068,23 @@ static void window_copy_cursor_jump_to_back(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; + struct options *oo = wme->wp->window->options; struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, oldy, hsize; + int onemore; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; oldy = data->cy; + onemore = (options_get_number(oo, "mode-keys") != MODEKEY_VI); grid_reader_start(&gr, back_s->grid, px, py); grid_reader_cursor_left(&gr, 0); grid_reader_cursor_left(&gr, 0); if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { - grid_reader_cursor_right(&gr, 1, 0); + grid_reader_cursor_right(&gr, 1, 0, onemore); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); @@ -6086,7 +6131,7 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, grid_reader_start(&gr, back_s->grid, px, py); if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { if (!grid_reader_in_set(&gr, WHITESPACE)) - grid_reader_cursor_right(&gr, 0, 0); + grid_reader_cursor_right(&gr, 0, 0, 0); grid_reader_cursor_next_word_end(&gr, separators); grid_reader_cursor_left(&gr, 1); } else @@ -6116,7 +6161,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, grid_reader_start(&gr, back_s->grid, px, py); if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { if (!grid_reader_in_set(&gr, WHITESPACE)) - grid_reader_cursor_right(&gr, 0, 0); + grid_reader_cursor_right(&gr, 0, 0, 0); grid_reader_cursor_next_word_end(&gr, separators); grid_reader_cursor_left(&gr, 1); } else @@ -6350,7 +6395,7 @@ window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag) data->rectflag = rectflag; py = screen_hsize(data->backing) + data->cy - data->oy; - px = window_copy_find_length(wme, py); + px = window_copy_cursor_limit(wme, py, data->rectflag); if (data->cx > px) window_copy_update_cursor(wme, px, data->cy); diff --git a/window-tree.c b/window-tree.c index 531d8e20..21029c00 100644 --- a/window-tree.c +++ b/window-tree.c @@ -387,7 +387,7 @@ window_tree_build(void *modedata, struct sort_criteria *sort_crit, *tag = (uint64_t)data->fs.wl; break; case WINDOW_TREE_PANE: - if (window_count_panes(data->fs.wl->window) == 1) + if (window_count_panes(data->fs.wl->window, 1) == 1) *tag = (uint64_t)data->fs.wl; else *tag = (uint64_t)data->fs.wp; @@ -578,7 +578,7 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s, struct format_tree *ft; struct options *oo; - total = window_count_panes(w); + total = window_count_panes(w, 1); if (sx / total < 24) { visible = sx / 24; diff --git a/window.c b/window.c index 59d1659e..9b631471 100644 --- a/window.c +++ b/window.c @@ -71,7 +71,7 @@ static struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); static void window_pane_destroy(struct window_pane *); static void window_pane_full_size_offset(struct window_pane *wp, - u_int *xoff, u_int *yoff, u_int *sx, u_int *sy); + int *xoff, int *yoff, u_int *sx, u_int *sy); RB_GENERATE(windows, window, entry, window_cmp); RB_GENERATE(winlinks, winlink, entry, winlink_cmp); @@ -306,6 +306,7 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) w->flags = 0; TAILQ_INIT(&w->panes); + TAILQ_INIT(&w->z_index); TAILQ_INIT(&w->last_panes); w->active = NULL; @@ -530,6 +531,8 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) if (wp == w->active) return (0); + if (w->flags & WINDOW_ZOOMED) + window_unzoom(w, 1); lastwp = w->active; window_pane_stack_remove(&w->last_panes, wp); @@ -672,7 +675,7 @@ window_zoom(struct window_pane *wp) if (w->flags & WINDOW_ZOOMED) return (-1); - if (window_count_panes(w) == 1) + if (window_count_panes(w, 1) == 1) return (-1); if (w->active != wp) @@ -859,14 +862,15 @@ window_pane_index(struct window_pane *wp, u_int *i) } u_int -window_count_panes(struct window *w) +window_count_panes(struct window *w, int with_floating) { struct window_pane *wp; - u_int n; + u_int n = 0; - n = 0; - TAILQ_FOREACH(wp, &w->panes, entry) - n++; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (with_floating || ~wp->flags & PANE_FLOATING) + n++; + } return (n); } @@ -1388,7 +1392,7 @@ window_pane_choose_best(struct window_pane **list, u_int size) * scrollbars if they were visible but not including the border(s). */ static void -window_pane_full_size_offset(struct window_pane *wp, u_int *xoff, u_int *yoff, +window_pane_full_size_offset(struct window_pane *wp, int *xoff, int *yoff, u_int *sx, u_int *sy) { struct window *w = wp->window; @@ -1422,9 +1426,9 @@ window_pane_find_up(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; - u_int edge, left, right, end, size; - int status, found; - u_int xoff, yoff, sx, sy; + int edge, left, right, end, status, found; + int xoff, yoff; + u_int size, sx, sy; if (wp == NULL) return (NULL); @@ -1439,25 +1443,25 @@ window_pane_find_up(struct window_pane *wp) edge = yoff; if (status == PANE_STATUS_TOP) { if (edge == 1) - edge = w->sy + 1; + edge = (int)w->sy + 1; } else if (status == PANE_STATUS_BOTTOM) { if (edge == 0) - edge = w->sy; + edge = (int)w->sy; } else { if (edge == 0) - edge = w->sy + 1; + edge = (int)w->sy + 1; } left = xoff; - right = xoff + sx; + right = xoff + (int)sx; TAILQ_FOREACH(next, &w->panes, entry) { window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); if (next == wp) continue; - if (yoff + sy + 1 != edge) + if (yoff + (int)sy + 1 != edge) continue; - end = xoff + sx - 1; + end = xoff + (int)sx - 1; found = 0; if (xoff < left && end > right) @@ -1483,9 +1487,9 @@ window_pane_find_down(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; - u_int edge, left, right, end, size; - int status, found; - u_int xoff, yoff, sx, sy; + int edge, left, right, end, status, found; + int xoff, yoff; + u_int size, sx, sy; if (wp == NULL) return (NULL); @@ -1497,20 +1501,20 @@ window_pane_find_down(struct window_pane *wp) window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); - edge = yoff + sy + 1; + edge = yoff + (int)sy + 1; if (status == PANE_STATUS_TOP) { - if (edge >= w->sy) + if (edge >= (int)w->sy) edge = 1; } else if (status == PANE_STATUS_BOTTOM) { - if (edge >= w->sy - 1) + if (edge >= (int)w->sy - 1) edge = 0; } else { - if (edge >= w->sy) + if (edge >= (int)w->sy) edge = 0; } left = wp->xoff; - right = wp->xoff + wp->sx; + right = wp->xoff + (int)wp->sx; TAILQ_FOREACH(next, &w->panes, entry) { window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); @@ -1518,7 +1522,7 @@ window_pane_find_down(struct window_pane *wp) continue; if (yoff != edge) continue; - end = xoff + sx - 1; + end = xoff + (int)sx - 1; found = 0; if (xoff < left && end > right) @@ -1544,9 +1548,9 @@ window_pane_find_left(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; - u_int edge, top, bottom, end, size; - int found; - u_int xoff, yoff, sx, sy; + int edge, top, bottom, end, found; + int xoff, yoff; + u_int size, sx, sy; if (wp == NULL) return (NULL); @@ -1559,18 +1563,18 @@ window_pane_find_left(struct window_pane *wp) edge = xoff; if (edge == 0) - edge = w->sx + 1; + edge = (int)w->sx + 1; top = yoff; - bottom = yoff + sy; + bottom = yoff + (int)sy; TAILQ_FOREACH(next, &w->panes, entry) { window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); if (next == wp) continue; - if (xoff + sx + 1 != edge) + if (xoff + (int)sx + 1 != edge) continue; - end = yoff + sy - 1; + end = yoff + (int)sy - 1; found = 0; if (yoff < top && end > bottom) @@ -1596,9 +1600,9 @@ window_pane_find_right(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; - u_int edge, top, bottom, end, size; - int found; - u_int xoff, yoff, sx, sy; + int edge, top, bottom, end, found; + int xoff, yoff; + u_int size, sx, sy; if (wp == NULL) return (NULL); @@ -1609,12 +1613,12 @@ window_pane_find_right(struct window_pane *wp) window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); - edge = xoff + sx + 1; - if (edge >= w->sx) + edge = xoff + (int)sx + 1; + if (edge >= (int)w->sx) edge = 0; top = wp->yoff; - bottom = wp->yoff + wp->sy; + bottom = wp->yoff + (int)wp->sy; TAILQ_FOREACH(next, &w->panes, entry) { window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); @@ -1622,7 +1626,7 @@ window_pane_find_right(struct window_pane *wp) continue; if (xoff != edge) continue; - end = yoff + sy - 1; + end = yoff + (int)sy - 1; found = 0; if (yoff < top && end > bottom) @@ -1642,6 +1646,7 @@ window_pane_find_right(struct window_pane *wp) return (best); } +/* Add window to stack. */ void window_pane_stack_push(struct window_panes *stack, struct window_pane *wp) { @@ -1652,6 +1657,7 @@ window_pane_stack_push(struct window_panes *stack, struct window_pane *wp) } } +/* Remove window from stack. */ void window_pane_stack_remove(struct window_panes *stack, struct window_pane *wp) { @@ -2032,3 +2038,53 @@ window_pane_border_status_get_range(struct window_pane *wp, u_int x, u_int y) */ return (style_ranges_get_range(srs, x - wp->xoff - 2)); } + +int +window_pane_tile_geometry(struct window *w, struct window_pane *wp, + int *out_size, int *out_flags, enum layout_type *out_type, + struct cmdq_item *item, struct args *args, char **cause) +{ + int size = -1, flags = *out_flags; + enum layout_type type; + u_int curval = 0; + + type = LAYOUT_TOPBOTTOM; + if (args_has(args, 'h')) + type = LAYOUT_LEFTRIGHT; + + if (args_has(args, 'l') || args_has(args, 'p')) { + if (args_has(args, 'f')) { + if (type == LAYOUT_TOPBOTTOM) + curval = w->sy; + else + curval = w->sx; + } else { + if (type == LAYOUT_TOPBOTTOM) + curval = wp->sy; + else + curval = wp->sx; + } + } + + if (args_has(args, 'l')) { + size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval, + item, cause); + } else if (args_has(args, 'p')) { + size = args_strtonum_and_expand(args, 'p', 0, 100, item, + cause); + if (cause == NULL) + size = curval * size / 100; + } + if (*cause != NULL) + return (-1); + + if (args_has(args, 'b')) + flags |= SPAWN_BEFORE; + if (args_has(args, 'f')) + flags |= SPAWN_FULLSIZE; + + *out_size = size; + *out_flags = flags; + *out_type = type; + return (0); +}