From 8f28f31b788eb75832d03b777578a641a32f90f9 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 13 May 2026 08:25:05 +0000 Subject: [PATCH 01/12] Do not crash if set progress bar with no pane, from Dane Jensen. --- server-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 8279f109..6729c851 100644 --- a/server-client.c +++ b/server-client.c @@ -2137,7 +2137,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 && From dbc0710bc905f8e89d2fe00d80ee14071a49fe39 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 13 May 2026 10:24:57 +0000 Subject: [PATCH 02/12] Update supported features for Foot, from Meriel Luna Mittelbach in GutHub issue 5079. --- tty-features.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tty-features.c b/tty-features.c index 3560bbb0..95812e8e 100644 --- a/tty-features.c +++ b/tty-features.c @@ -89,7 +89,7 @@ static const struct tty_feature tty_feature_clipboard = { /* Terminal supports OSC 8 hyperlinks. */ static const char *const tty_feature_hyperlinks_capabilities[] = { - "*:Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\", + "Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\", NULL }; static const struct tty_feature tty_feature_hyperlinks = { @@ -517,8 +517,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 "," From 281e8ff76630b9f6f5e36463e9a3a209093235e3 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 13 May 2026 13:12:23 +0000 Subject: [PATCH 03/12] Fix a couple of bugs in tty_draw_line: do not loop forever if orphan padding appears, or if a wide character is trimmed at the right of the region. Much help with testing from qingliu at alauda dot io in GitHub issue 5024. --- tty-draw.c | 57 ++++++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) 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; From 4cea1c7189982cc7e3862b5aa9612f74c897175b Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 17 May 2026 10:44:53 +0000 Subject: [PATCH 04/12] Add new-pane command, currently this is equivalent to split-window but it will have minor differences for floating panes in future. Also add -R/-s/-S/-k/-m flags to control border and style and behaviour, like popups. GitHub issue 5027 from Dane Jensen. --- cmd-split-window.c | 150 ++++++++++++++++++++++++++++---------------- cmd.c | 2 + tmux.1 | 153 ++++++++++++++++++++++++++++++--------------- tmux.h | 3 + window.c | 50 +++++++++++++++ 5 files changed, 254 insertions(+), 104 deletions(-) diff --git a/cmd-split-window.c b/cmd-split-window.c index bbec0cc8..ed3f47fc 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -28,22 +28,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 }, @@ -51,6 +52,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) { @@ -63,51 +104,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; @@ -118,11 +122,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; @@ -157,6 +159,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 cd64a3ea..d20b8736 100644 --- a/cmd.c +++ b/cmd.c @@ -72,6 +72,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; @@ -164,6 +165,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/tmux.1 b/tmux.1 index a0138a64..9bc8d8a4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3268,6 +3268,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 @@ -3592,65 +3684,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 fec919e2..c94c6e07 100644 --- a/tmux.h +++ b/tmux.h @@ -3412,6 +3412,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 *); diff --git a/window.c b/window.c index 57f3c972..f554ee67 100644 --- a/window.c +++ b/window.c @@ -2021,3 +2021,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); +} From 742efe989f38a02b096d4f3d9ff4e8b593d3b05e Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 17 May 2026 10:54:01 +0000 Subject: [PATCH 05/12] Report file open errors more sensibly, reported by Meriel Luna Mittelbach in GitHub issue 5081. --- file.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/file.c b/file.c index f13eb862..38a692e0 100644 --- a/file.c +++ b/file.c @@ -402,6 +402,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; @@ -674,7 +678,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; @@ -683,7 +687,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); From 818352c3cdc1ef137fee97534c8eda1e26ca09d0 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 17 May 2026 11:12:07 +0000 Subject: [PATCH 06/12] Add some more explanatory text about targets, GitHub issue 4850. --- tmux.1 | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tmux.1 b/tmux.1 index 9bc8d8a4..56bf6386 100644 --- a/tmux.1 +++ b/tmux.1 @@ -934,6 +934,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 From 35bd1a4c021118535699b23e5e73cf83d98d1edc Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 17 May 2026 11:17:16 +0000 Subject: [PATCH 07/12] Do not include unattached clients on sorted list, fixes crash reported by Marcel Partap. --- sort.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) 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; From b24b8424b1579fb22ebd2f40e0d4a71b272223ae Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 17 May 2026 12:38:04 +0000 Subject: [PATCH 08/12] Switch to use backing grid not copy mode's grid for copy line, GitHub issue 4995. --- window-copy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/window-copy.c b/window-copy.c index af80b8b4..72ac1306 100644 --- a/window-copy.c +++ b/window-copy.c @@ -898,9 +898,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 * From bbea6e63758ac00a9f731a52a189c9d3829e8be6 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 17 May 2026 13:01:04 +0000 Subject: [PATCH 09/12] Add a five second limit on pasting for terminals which mysteriously lose the end sequence if the paste is too big (that is, Terminal.app). Reported by Garri Djavadyan in GitHub issue 4527. --- server-client.c | 9 +++++++++ tmux.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/server-client.c b/server-client.c index 6729c851..c27704a7 100644 --- a/server-client.c +++ b/server-client.c @@ -1037,6 +1037,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); } @@ -1070,6 +1071,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); } @@ -2504,6 +2506,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/tmux.h b/tmux.h index c94c6e07..d79beabb 100644 --- a/tmux.h +++ b/tmux.h @@ -1945,6 +1945,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 *); @@ -2084,6 +2087,7 @@ struct client { struct key_table *keytable; key_code last_key; + time_t paste_time; uint64_t redraw_panes; uint64_t redraw_scrollbars; From f12d7b4e67b6b62a14a584137db5e7a9d5614f77 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 17 May 2026 13:12:21 +0000 Subject: [PATCH 10/12] When mode-keys is set to vi, do not allow the cursor to go into the invisible extra cell to the right of the visible text. This is closer to what vi(1) does. From Max Vim in GitHub issue 5070. --- grid-reader.c | 9 ++++++-- tmux.h | 2 +- window-copy.c | 61 ++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 61 insertions(+), 11 deletions(-) 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/tmux.h b/tmux.h index d79beabb..2c034b8e 100644 --- a/tmux.h +++ b/tmux.h @@ -3163,7 +3163,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 *); diff --git a/window-copy.c b/window-copy.c index 72ac1306..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 *); @@ -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); From a8520ba59e32133414c710b26ec23bf3c19f8b23 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 17 May 2026 16:01:42 +0000 Subject: [PATCH 11/12] Bring over some of layout and positioning code for floating panes, by Michael Grant. --- cmd-break-pane.c | 2 +- cmd-join-pane.c | 2 +- cmd-select-pane.c | 2 +- format.c | 10 +-- layout-custom.c | 176 +++++++++++++++++++++++++++++++++------------- layout-set.c | 12 ++-- layout.c | 95 +++++++++++++++++++------ log.c | 2 + server-fn.c | 2 +- tmux.h | 22 +++--- window-tree.c | 4 +- window.c | 86 +++++++++++----------- 12 files changed, 282 insertions(+), 133 deletions(-) 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 6eb0ea65..f8975a67 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -171,7 +171,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/format.c b/format.c index 8a0accf7..472316ec 100644 --- a/format.c +++ b/format.c @@ -2046,7 +2046,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); } @@ -2205,7 +2205,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); } @@ -2329,7 +2329,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); } @@ -2371,7 +2371,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); } @@ -2918,7 +2918,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/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/log.c b/log.c index ede6e257..98a03763 100644 --- a/log.c +++ b/log.c @@ -150,6 +150,7 @@ fatal(const char *msg, ...) log_vwrite(msg, ap, tmp); va_end(ap); + abort ();//XXX exit(1); } @@ -163,5 +164,6 @@ fatalx(const char *msg, ...) log_vwrite(msg, ap, "fatal: "); va_end(ap); + abort ();//XXX exit(1); } diff --git a/server-fn.c b/server-fn.c index 6c4c9c61..585a01f9 100644 --- a/server-fn.c +++ b/server-fn.c @@ -185,7 +185,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/tmux.h b/tmux.h index 2c034b8e..2209732e 100644 --- a/tmux.h +++ b/tmux.h @@ -1302,9 +1302,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. */ @@ -1324,6 +1326,7 @@ struct window { struct window_pane *active; struct window_panes last_panes; + struct window_panes z_index; struct window_panes panes; int lastlayout; @@ -1417,6 +1420,7 @@ TAILQ_HEAD(winlink_stack, winlink); enum layout_type { LAYOUT_LEFTRIGHT, LAYOUT_TOPBOTTOM, + LAYOUT_FLOATING, LAYOUT_WINDOWPANE }; @@ -1432,8 +1436,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; @@ -1710,10 +1714,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; @@ -2284,6 +2288,7 @@ struct spawn_context { #define SPAWN_FULLSIZE 0x20 #define SPAWN_EMPTY 0x40 #define SPAWN_ZOOM 0x80 +#define SPAWN_FLOATING 0x100 }; /* Paste buffer. */ @@ -3362,7 +3367,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); @@ -3430,8 +3435,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/window-tree.c b/window-tree.c index b58d5ed5..9b5269b7 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 f554ee67..72ccb7cc 100644 --- a/window.c +++ b/window.c @@ -73,7 +73,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); @@ -308,6 +308,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; @@ -523,6 +524,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); @@ -665,7 +668,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) @@ -852,14 +855,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); } @@ -1377,7 +1381,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; @@ -1411,9 +1415,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); @@ -1428,25 +1432,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) @@ -1472,9 +1476,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); @@ -1486,20 +1490,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); @@ -1507,7 +1511,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) @@ -1533,9 +1537,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); @@ -1548,18 +1552,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) @@ -1585,9 +1589,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); @@ -1598,12 +1602,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); @@ -1611,7 +1615,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) @@ -1631,6 +1635,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) { @@ -1641,6 +1646,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) { From 962d647993ed5aea028128050945003ba01ce634 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 17 May 2026 16:02:33 +0000 Subject: [PATCH 12/12] Remove some leftover debug code. --- log.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/log.c b/log.c index 98a03763..ede6e257 100644 --- a/log.c +++ b/log.c @@ -150,7 +150,6 @@ fatal(const char *msg, ...) log_vwrite(msg, ap, tmp); va_end(ap); - abort ();//XXX exit(1); } @@ -164,6 +163,5 @@ fatalx(const char *msg, ...) log_vwrite(msg, ap, "fatal: "); va_end(ap); - abort ();//XXX exit(1); }