diff --git a/cmd-split-window.c b/cmd-split-window.c index 0cd6d8da..2286d41d 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -70,12 +70,6 @@ const struct cmd_entry cmd_split_window_entry = { .exec = cmd_split_window_exec }; -enum new_pane_mode { - FLOATING, - TILED, - NONE, -}; - static struct layout_cell * cmd_split_window_get_floating_layout_cell(struct cmdq_item *item, struct args *args, struct window *w) @@ -163,53 +157,23 @@ cmd_split_window_get_tiled_layout_cell(struct cmdq_item *item, struct layout_cell *lc = NULL; char *cause = NULL; int size; - u_int curval = 0; if (wp->flags & PANE_FLOATING) { cmdq_error(item, "can't split a floating pane"); return (NULL); } - 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); + 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) + if (lc == NULL) cmdq_error(item, "no space for new pane"); - return (lc); } @@ -232,7 +196,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) char *cause = NULL, *cp; struct args_value *av; u_int count = args_count(args); - enum new_pane_mode pane_mode = NONE; + enum { FLOATING, TILED, NONE } pane_mode; if (args_has(args, 'M')) { if (strcasecmp(args_get(args, 'M'), "f") == 0) @@ -250,7 +214,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) input = (args_has(args, 'I') && count == 0); - flags = pane_mode == FLOATING ? SPAWN_FLOATING : 0; + flags = (pane_mode == FLOATING) ? SPAWN_FLOATING : 0; if (args_has(args, 'b')) flags |= SPAWN_BEFORE; if (args_has(args, 'f')) @@ -258,13 +222,12 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (input || (count == 1 && *args_string(args, 0) == '\0')) flags |= SPAWN_EMPTY; - if (pane_mode == FLOATING) lc = cmd_split_window_get_floating_layout_cell(item, args, w); - else if (pane_mode == TILED) + else if (pane_mode == TILED) { lc = cmd_split_window_get_tiled_layout_cell(item, args, w, wp, flags); - else { + } else { cmdq_error(item, "unrecognized pane mode '%s'", args_get(args, 'M')); return (CMD_RETURN_ERROR); @@ -315,7 +278,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) } options_set_string(new_wp->options, "window-active-style", 0, "%s", style); - new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED); + new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED| + PANE_THEMECHANGED); } style = args_get(args, 'S'); if (style != NULL) { @@ -329,7 +293,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) 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); + cmdq_error(item, "bad inactive border style: %s", + style); return (CMD_RETURN_ERROR); } } 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 0486d52e..b42b117b 100644 --- a/format.c +++ b/format.c @@ -2059,8 +2059,10 @@ format_cb_pane_at_right(struct format_tree *ft) static void * format_cb_pane_bottom(struct format_tree *ft) { - if (ft->wp != NULL) - return (format_printf("%d", ft->wp->yoff + (int)ft->wp->sy - 1)); + struct window_pane *wp = ft->wp; + + if (wp != NULL) + return (format_printf("%d", wp->yoff + (int)wp->sy - 1)); return (NULL); } @@ -2357,8 +2359,10 @@ format_cb_pane_pb_state(struct format_tree *ft) static void * format_cb_pane_right(struct format_tree *ft) { - if (ft->wp != NULL) - return (format_printf("%d", ft->wp->xoff + (int)ft->wp->sx - 1)); + struct window_pane *wp = ft->wp; + + if (wp != NULL) + return (format_printf("%d", wp->xoff + (int)wp->sx - 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 050edb4b..33027738 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -62,29 +62,26 @@ char * layout_dump(struct window *w, struct layout_cell *root) { char layout[8192], *out; - int braket; + int bracket = 0; struct window_pane *wp; *layout = '\0'; if (layout_append(root, layout, sizeof layout) != 0) return (NULL); - braket = 0; TAILQ_FOREACH(wp, &w->z_index, zentry) { if (~wp->flags & PANE_FLOATING) break; - if (!braket) { - strcat(layout, "<"); - braket = 1; + if (!bracket) { + strlcat(layout, "<", sizeof layout); + bracket = 1; } if (layout_append(wp->layout_cell, layout, sizeof layout) != 0) return (NULL); - strcat(layout, ","); + strlcat(layout, ",", sizeof layout); } - if (braket) { - /* Overwrite the trailing ','. */ + if (bracket) layout[strlen(layout) - 1] = '>'; - } xasprintf(&out, "%04hx,%s", layout_checksum(layout), layout); return (out); @@ -146,8 +143,8 @@ layout_check(struct layout_cell *lc) u_int n = 0; switch (lc->type) { - case LAYOUT_WINDOWPANE: case LAYOUT_FLOATING: + case LAYOUT_WINDOWPANE: break; case LAYOUT_LEFTRIGHT: TAILQ_FOREACH(lcchild, &lc->cells, entry) { @@ -213,10 +210,11 @@ layout_parse(struct window *w, const char *layout, char **cause) } /* Check this window will fit into the layout. */ + npanes = window_count_panes(w, 1); for (;;) { - npanes = window_count_panes(w, 1); ncells = layout_count_cells(tiled_lc); - ncells += layout_count_cells(floating_lc); + if (floating_lc != NULL) + ncells += layout_count_cells(floating_lc); if (npanes > ncells) { /* Modify this to open a new pane */ xasprintf(cause, "have %u panes but need %u", npanes, @@ -228,9 +226,9 @@ layout_parse(struct window *w, const char *layout, char **cause) /* * Fewer panes than cells - close floating panes first - * then close the bottom right until. + * then close the bottom right until none remain. */ - if (floating_lc && ! TAILQ_EMPTY(&floating_lc->cells)) { + if (floating_lc != NULL && !TAILQ_EMPTY(&floating_lc->cells)) { lcchild = TAILQ_FIRST(&floating_lc->cells); layout_destroy_cell(w, lcchild, &floating_lc); } else { @@ -266,8 +264,6 @@ layout_parse(struct window *w, const char *layout, char **cause) } if (tiled_lc->type != LAYOUT_WINDOWPANE && (tiled_lc->sx != sx || tiled_lc->sy != sy)) { - log_debug("fix layout %u,%u to %u,%u", tiled_lc->sx, - tiled_lc->sy, sx,sy); layout_print_cell(tiled_lc, __func__, 0); tiled_lc->sx = sx - 1; tiled_lc->sy = sy - 1; } @@ -288,27 +284,29 @@ layout_parse(struct window *w, const char *layout, char **cause) /* Assign the panes into the cells. */ wp = TAILQ_FIRST(&w->panes); - layout_assign(&wp, tiled_lc, 0); - layout_assign(&wp, floating_lc, 1); + if (tiled_lc != NULL) + layout_assign(&wp, tiled_lc, 0); + if (floating_lc != NULL) + layout_assign(&wp, floating_lc, PANE_FLOATING); - /* Fix z_indexes. */ + /* Fix z indexes. */ while (!TAILQ_EMPTY(&w->z_index)) { wp = TAILQ_FIRST(&w->z_index); TAILQ_REMOVE(&w->z_index, wp, zentry); } - layout_fix_zindexes(w, floating_lc); + if (floating_lc != NULL) + layout_fix_zindexes(w, floating_lc); layout_fix_zindexes(w, tiled_lc); /* 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(floating_lc, __func__, 0); /* Free the floating layout cell, no longer needed. */ - layout_free_cell(floating_lc); + if (floating_lc != NULL) + layout_free_cell(floating_lc); notify_window("window-layout-changed", w); @@ -322,7 +320,7 @@ fail: /* Assign panes into cells. */ static void -layout_assign(struct window_pane **wp, struct layout_cell *lc, int floating) +layout_assign(struct window_pane **wp, struct layout_cell *lc, int flags) { struct layout_cell *lcchild; @@ -332,16 +330,14 @@ layout_assign(struct window_pane **wp, struct layout_cell *lc, int floating) switch (lc->type) { case LAYOUT_WINDOWPANE: layout_make_leaf(lc, *wp); - if (floating) { - (*wp)->flags |= PANE_FLOATING; - } + (*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, 1); + layout_assign(wp, lcchild, PANE_FLOATING); return; } } @@ -394,7 +390,6 @@ layout_construct_cell(struct layout_cell *lcparent, const char **layout) return (lc); } - /* * Given a character string layout, recursively construct cells. * Possible return values: diff --git a/layout.c b/layout.c index 0d2bc7f1..dfdce707 100644 --- a/layout.c +++ b/layout.c @@ -50,6 +50,7 @@ static struct layout_cell *layout_active_neighbour(struct layout_cell *, int); 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) { @@ -72,6 +73,7 @@ layout_create_cell(struct layout_cell *lcparent) return (lc); } +/* Free a layout cell. */ void layout_free_cell(struct layout_cell *lc) { @@ -87,11 +89,11 @@ layout_free_cell(struct layout_cell *lc) } break; case LAYOUT_FLOATING: - /* A Floating layout cell is only used temporarily - * while select-layout constructs a layout. - * Cleave the children from the temp layout, then - * free temp floating layout cell. Each floating - * pane has stub layout. + /* + * 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); @@ -110,6 +112,7 @@ layout_free_cell(struct layout_cell *lc) free(lc); } +/* Log a cell. */ void layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) { @@ -151,6 +154,7 @@ 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) { @@ -192,9 +196,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, int xoff, - 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; @@ -203,6 +207,7 @@ layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, int xoff, lc->yoff = yoff; } +/* Make a cell a leaf cell. */ void layout_make_leaf(struct layout_cell *lc, struct window_pane *wp) { @@ -214,6 +219,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) { @@ -372,7 +378,7 @@ layout_fix_panes(struct window *w, struct window_pane *skip) sx = lc->sx; sy = lc->sy; - if (~wp->flags & PANE_FLOATING && + if ((~wp->flags & PANE_FLOATING) && layout_add_horizontal_border(w, lc, status)) { if (status == PANE_STATUS_TOP) wp->yoff++; @@ -412,7 +418,7 @@ 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: @@ -420,7 +426,6 @@ layout_count_cells(struct layout_cell *lc) case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: case LAYOUT_FLOATING: - count = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) count += layout_count_cells(lcchild); return (count); @@ -637,6 +642,13 @@ layout_destroy_cell(struct window *w, struct layout_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); + return; + } + /* In tiled layouts, merge the space into the previous or next cell. */ if (lcparent->type != LAYOUT_FLOATING) { is_minimised = (lc->wp != NULL && (lc->wp->flags & PANE_MINIMISED)); @@ -668,12 +680,14 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc, lc->parent = lcparent->parent; if (lc->parent == NULL) { - lc->xoff = 0; lc->yoff = 0; + lc->xoff = 0; + lc->yoff = 0; + /* * If the sole remaining child is a minimised * WINDOWPANE, its stored size may be stale (it never * received the space that was given to the removed - * cell). Restore the full window size so that + * cell). Restore the full window size so that * unminimise can reclaim the correct amount. */ if (lc->type == LAYOUT_WINDOWPANE && @@ -754,6 +768,7 @@ layout_unminimise_cell(struct window *w, struct layout_cell *lc) layout_redistribute_cells(w, lcparent, lcparent->type); } +/* Initialize layout for pane. */ void layout_init(struct window *w, struct window_pane *wp) { @@ -765,6 +780,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) { diff --git a/server-client.c b/server-client.c index e4e66236..7a812f95 100644 --- a/server-client.c +++ b/server-client.c @@ -1066,6 +1066,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); } @@ -1099,6 +1100,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); } @@ -2591,6 +2593,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/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 1eee1e8f..b22a305c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -940,6 +940,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 @@ -3329,46 +3360,22 @@ but a different format may be specified with .Tg newp .It Xo Ic new\-pane .Op Fl bdefhIkPvZ -.Op Fl c Ar start-directory +.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 M Ar mode .Op Fl p Ar percentage -.Op Fl R Ar inactive-border-style +.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 Fl x Ar width -.Op Fl X Ar x-position -.Op Fl y Ar height -.Op Fl Y Ar y-position -.Op Ar shell-command Op Ar argument ... +.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. -A -.Ar mode -may be specified with the -.Fl M -option and must be followed by one of the following special values: -.Bl -column "XXXXX" -offset indent -.It Sy "Value" Ta Sy "Meaning" -.It Li "f" Ta "floating pane above the current layout" -.It Li "t" Ta "tiled into the layout by splitting a pane" -.El -.Pp -If no -.Ar mode -is specified, -.Ic f -is assumed. -When creating a tiled pane, a target pane may be specified with -.Fl t . -Note that some options will only affect one -.Ar mode . -.Pp +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. @@ -3381,48 +3388,7 @@ sets the border style when the pane is active and .Fl R sets the border style when the pane is inactive (see .Sx STYLES ) . -.Fl k -keeps the pane open after the optional -.Ar shell-command -exits and waits for a keypress (any non-mouse key) 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 . -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 -For floating panes, the following options are availible: -The -.Fl x , -.Fl y , -.Fl X , -and -.Fl Y -options set the width, height, and position of the pane. -If not given, the pane is sized to half the window dimensions and offset from -the previous floating pane. -These four options may be followed by '%' to specify a percentage of the -current window dimensions. -.Pp -For tiled panes, the following options are availible: .Fl h does a horizontal split and .Fl v @@ -3437,6 +3403,8 @@ columns (for horizontal split); 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 @@ -3449,9 +3417,37 @@ 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. +.Ic new\-window .Tg nextl .It Ic next\-layout Op Fl t Ar target\-window .D1 Pq alias: Ic nextl @@ -3777,26 +3773,21 @@ the command behaves like .Tg splitw .It Xo Ic split\-window .Op Fl bdefhIkPvZ -.Op Fl c Ar start-directory +.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 M Ar mode .Op Fl p Ar percentage -.Op Fl R Ar inactive-border-style +.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 Fl x Ar width -.Op Fl X Ar x-position -.Op Fl y Ar height -.Op Fl Y Ar y-position -.Op Ar shell-command Op Ar argument ... +.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 -Creates a new pane. -Default behavior is to split the pane in a tiled layout. +Creates a new pane by splitting +.Ar target\-pane . Shares behavior with .Ic new\-pane . .Pp diff --git a/tmux.h b/tmux.h index b3b841fc..0150e277 100644 --- a/tmux.h +++ b/tmux.h @@ -1347,7 +1347,7 @@ 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 */ + 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); @@ -1371,7 +1371,7 @@ struct window { struct window_pane *active; struct window_panes last_panes; - struct window_panes z_index; + struct window_panes z_index; struct window_panes panes; int lastlayout; @@ -2000,6 +2000,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 *); @@ -2139,6 +2142,7 @@ struct client { struct key_table *keytable; key_code last_key; + time_t paste_time; uint64_t redraw_panes; uint64_t redraw_scrollbars; @@ -3225,7 +3229,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 *); @@ -3487,6 +3491,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 *); @@ -3502,7 +3509,7 @@ void layout_redistribute_cells(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, int, 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_zindexes(struct window *, struct layout_cell *); 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 65c14aae..96e3bd11 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 *); @@ -906,9 +908,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 * @@ -1673,7 +1675,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) @@ -2694,6 +2696,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') { @@ -2749,6 +2753,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') { @@ -5162,7 +5168,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; @@ -5655,7 +5671,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); } @@ -5677,6 +5693,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) { @@ -5734,6 +5766,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); } @@ -5784,6 +5818,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); @@ -5811,18 +5849,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); @@ -6037,20 +6079,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); @@ -6097,7 +6142,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 @@ -6127,7 +6172,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 @@ -6361,7 +6406,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.c b/window.c index fb7dec8a..c183fd37 100644 --- a/window.c +++ b/window.c @@ -935,16 +935,14 @@ window_pane_index(struct window_pane *wp, u_int *i) } u_int -window_count_panes(struct window *w, int inc_floating) +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) { - if ((inc_floating == 0) && (wp->flags & PANE_FLOATING)) - continue; - n++; + if (with_floating || ~wp->flags & PANE_FLOATING) + n++; } return (n); } @@ -1516,11 +1514,9 @@ window_pane_find_up(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; - int edge, left, right, end; - u_int size; - int status, found; + int edge, left, right, end, status, found; int xoff, yoff; - u_int sx, sy; + u_int size, sx, sy; if (wp == NULL) return (NULL); @@ -1579,11 +1575,9 @@ window_pane_find_down(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; - int edge, left, right, end; - u_int size; - int status, found; + int edge, left, right, end, status, found; int xoff, yoff; - u_int sx, sy; + u_int size, sx, sy; if (wp == NULL) return (NULL); @@ -1642,11 +1636,9 @@ window_pane_find_left(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; - int edge, top, bottom, end; - u_int size; - int found; + int edge, top, bottom, end, found; int xoff, yoff; - u_int sx, sy; + u_int size, sx, sy; if (wp == NULL) return (NULL); @@ -1696,11 +1688,9 @@ window_pane_find_right(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; - int edge, top, bottom, end; - u_int size; - int found; + int edge, top, bottom, end, found; int xoff, yoff; - u_int sx, sy; + u_int size, sx, sy; if (wp == NULL) return (NULL); @@ -1744,6 +1734,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) { @@ -1754,6 +1745,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) { @@ -2134,3 +2126,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); +}