Merge branch 'master' into floating_panes

This commit is contained in:
Nicholas Marriott
2026-05-18 11:10:09 +01:00
14 changed files with 347 additions and 258 deletions

View File

@@ -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,44 +157,15 @@ 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);
}
@@ -209,7 +174,6 @@ cmd_split_window_get_tiled_layout_cell(struct cmdq_item *item,
lc = layout_split_pane(wp, type, size, flags);
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);
}
}

8
file.c
View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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,9 +210,10 @@ layout_parse(struct window *w, const char *layout, char **cause)
}
/* Check this window will fit into the layout. */
for (;;) {
npanes = window_count_panes(w, 1);
for (;;) {
ncells = layout_count_cells(tiled_lc);
if (floating_lc != NULL)
ncells += layout_count_cells(floating_lc);
if (npanes > ncells) {
/* Modify this to open a new pane */
@@ -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,14 +284,17 @@ layout_parse(struct window *w, const char *layout, char **cause)
/* Assign the panes into the cells. */
wp = TAILQ_FIRST(&w->panes);
if (tiled_lc != NULL)
layout_assign(&wp, tiled_lc, 0);
layout_assign(&wp, floating_lc, 1);
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);
}
if (floating_lc != NULL)
layout_fix_zindexes(w, floating_lc);
layout_fix_zindexes(w, tiled_lc);
@@ -303,11 +302,10 @@ layout_parse(struct window *w, const char *layout, char **cause)
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. */
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:

View File

@@ -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,7 +680,9 @@ 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
@@ -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)
{

View File

@@ -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

4
sort.c
View File

@@ -425,6 +425,10 @@ sort_get_clients(u_int *n, struct sort_criteria *sort_crit)
i = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (c->flags & CLIENT_UNATTACHEDFLAGS)
continue;
if (~c->flags & CLIENT_ATTACHED)
continue;
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);

163
tmux.1
View File

@@ -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

9
tmux.h
View File

@@ -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 *);

View File

@@ -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 = 1;
else if (gcp->bg != last.bg)
empty = 0;
else
empty = tty_draw_line_get_empty(gcp, nx - i);
if (px >= ex || i >= ex - px) {
/* Outside the area being drawn. */
empty = 1;
} 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;

View File

@@ -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 ","

View File

@@ -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);

View File

@@ -935,15 +935,13 @@ 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;
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);
}