diff --git a/Makefile b/Makefile index d73312013..d7673eab2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.120 2026/07/10 13:38:45 nicm Exp $ +# $OpenBSD: Makefile,v 1.121 2026/07/14 17:17:17 nicm Exp $ PROG= tmux SRCS= alerts.c \ @@ -17,7 +17,6 @@ SRCS= alerts.c \ cmd-detach-client.c \ cmd-display-menu.c \ cmd-display-message.c \ - cmd-display-panes.c \ cmd-find-window.c \ cmd-find.c \ cmd-if-shell.c \ @@ -137,9 +136,10 @@ SRCS= alerts.c \ window-clock.c \ window-copy.c \ window-customize.c \ + window-panes.c \ window-switch.c \ - window-visible.c \ window-tree.c \ + window-visible.c \ window.c \ xmalloc.c diff --git a/cfg.c b/cfg.c index 7a79a6025..8e67105a0 100644 --- a/cfg.c +++ b/cfg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cfg.c,v 1.89 2026/06/25 11:39:11 nicm Exp $ */ +/* $OpenBSD: cfg.c,v 1.90 2026/07/14 17:17:17 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -269,7 +269,8 @@ cfg_show_causes(struct session *s) wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) - window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); + window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL, + NULL); for (i = 0; i < cfg_ncauses; i++) { window_copy_add(wp, 0, "%s", cfg_causes[i]); free(cfg_causes[i]); diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index f9ce7d3b7..2dc304ff2 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-choose-tree.c,v 1.57 2026/06/26 14:40:30 nicm Exp $ */ +/* $OpenBSD: cmd-choose-tree.c,v 1.58 2026/07/14 17:17:17 nicm Exp $ */ /* * Copyright (c) 2012 Thomas Adam @@ -97,6 +97,21 @@ const struct cmd_entry cmd_switch_mode_entry = { .exec = cmd_choose_tree_exec }; +const struct cmd_entry cmd_display_panes_entry = { + .name = "display-panes", + .alias = "displayp", + + .args = { "d:kNs:t:Z", 0, 1, cmd_choose_tree_args_parse }, + .usage = "[-kNZ] [-d duration] [-s source-window] " + CMD_TARGET_PANE_USAGE " [template]", + + .source = { 's', CMD_FIND_WINDOW, 0 }, + .target = { 't', CMD_FIND_PANE, 0 }, + + .flags = CMD_AFTERHOOK, + .exec = cmd_choose_tree_exec +}; + static enum args_parse_type cmd_choose_tree_args_parse(__unused struct args *args, __unused u_int idx, __unused char **cause) @@ -131,9 +146,11 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) mode = &window_customize_mode; else if (cmd_get_entry(self) == &cmd_switch_mode_entry) mode = &window_switch_mode; + else if (cmd_get_entry(self) == &cmd_display_panes_entry) + mode = &window_panes_mode; else mode = &window_tree_mode; - window_pane_set_mode(wp, NULL, mode, target, args); + window_pane_set_mode(wp, NULL, mode, item, target, args); return (CMD_RETURN_NORMAL); } diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 051cb58a2..e6888ca7e 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-copy-mode.c,v 1.53 2026/07/08 08:07:42 nicm Exp $ */ +/* $OpenBSD: cmd-copy-mode.c,v 1.54 2026/07/14 17:17:17 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -79,7 +79,8 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) } if (cmd_get_entry(self) == &cmd_clock_mode_entry) { - window_pane_set_mode(wp, NULL, &window_clock_mode, NULL, NULL); + window_pane_set_mode(wp, NULL, &window_clock_mode, item, NULL, + NULL); return (CMD_RETURN_NORMAL); } @@ -90,7 +91,8 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) line_numbers = 1; if (event != NULL && KEYC_IS_MOUSE(event->key)) line_numbers = 0; - if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) { + if (!window_pane_set_mode(wp, swp, &window_copy_mode, item, NULL, + args)) { window_copy_set_line_numbers(wp, line_numbers); if (args_has(args, 'M')) window_copy_start_drag(c, &event->m); diff --git a/cmd-display-panes.c b/cmd-display-panes.c deleted file mode 100644 index 176238a1f..000000000 --- a/cmd-display-panes.c +++ /dev/null @@ -1,393 +0,0 @@ -/* $OpenBSD: cmd-display-panes.c,v 1.56 2026/06/25 16:32:42 nicm Exp $ */ - -/* - * Copyright (c) 2009 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include -#include - -#include "tmux.h" - -/* - * Display panes on a client. - */ - -static enum args_parse_type cmd_display_panes_args_parse(struct args *, - u_int, char **); -static enum cmd_retval cmd_display_panes_exec(struct cmd *, - struct cmdq_item *); - -const struct cmd_entry cmd_display_panes_entry = { - .name = "display-panes", - .alias = "displayp", - - .args = { "bd:Nt:", 0, 1, cmd_display_panes_args_parse }, - .usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", - - .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, - .exec = cmd_display_panes_exec -}; - -struct cmd_display_panes_data { - struct cmdq_item *item; - struct args_command_state *state; -}; - -struct cmd_display_panes_ctx { - struct client *c; - int ox; - int oy; - u_int sx; - u_int sy; - u_int statuslines; - int statustop; -}; - -static enum args_parse_type -cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx, - __unused char **cause) -{ - return (ARGS_PARSE_COMMANDS_OR_STRING); -} - -static void -cmd_display_panes_put(struct cmd_display_panes_ctx *ctx, - struct window_pane *wp, u_int cx, u_int cy, const char *buf, size_t len) -{ - struct client *c = ctx->c; - struct tty *tty = &c->tty; - struct visible_ranges *r; - struct visible_range *ri; - u_int i, j; - - r = window_visible_ranges(wp, ctx->ox + cx, ctx->oy + cy, len, NULL); - for (i = 0; i < r->used; i++) { - ri = &r->ranges[i]; - for (j = ri->px; j < ri->px + ri->nx; j++) { - tty_cursor(tty, j - ctx->ox, cy); - tty_putn(tty, &buf[j - ri->px], 1, 1); - } - } -} - -static void -cmd_display_panes_draw_format(struct cmd_display_panes_ctx *ctx, - struct window_pane *wp, u_int xoff, u_int yoff, u_int sx, - const struct grid_cell *gc) -{ - struct client *c = ctx->c; - struct tty *tty = &c->tty; - struct session *s = c->session; - struct screen *sc = wp->screen, screen; - struct screen_write_ctx sctx; - struct visible_ranges *r; - struct visible_range *ri; - const char *format; - char *expanded; - u_int i, px = ctx->ox + xoff; - - format = options_get_string(s->options, "display-panes-format"); - expanded = format_single(NULL, format, c, s, s->curw, wp); - - screen_init(&screen, sx, 1, 0); - screen_write_start(&sctx, &screen); - screen_write_fast_copy(&sctx, sc, 0, sc->grid->hsize, sx, 1); - screen_write_cursormove(&sctx, 0, 0, 0); - format_draw(&sctx, gc, sx, expanded, NULL, 0); - screen_write_stop(&sctx); - free(expanded); - - r = window_visible_ranges(wp, px, wp->yoff, sx, NULL); - for (i = 0; i < r->used; i++) { - ri = &r->ranges[i]; - tty_draw_line(tty, &screen, ri->px - px, 0, ri->nx, - ri->px - ctx->ox, yoff, NULL); - } - screen_free(&screen); -} - -static void -cmd_display_panes_draw_pane(struct cmd_display_panes_ctx *ctx, - struct window_pane *wp) -{ - struct client *c = ctx->c; - struct tty *tty = &c->tty; - struct session *s = c->session; - struct options *oo = s->options; - struct window *w = wp->window; - struct grid_cell fgc, bgc; - u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy; - u_int cx, cy; - const char *name; - struct format_tree *ft; - char buf[16], lbuf[16], *ptr; - size_t len, llen; - - if (wp->xoff + (int)wp->sx <= ctx->ox || - wp->xoff >= ctx->ox + (int)ctx->sx || - wp->yoff + (int)wp->sy <= ctx->oy || - wp->yoff >= ctx->oy + (int)ctx->sy) - return; - - if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) { - /* All visible. */ - xoff = wp->xoff - ctx->ox; - sx = wp->sx; - } else if (wp->xoff < ctx->ox && - wp->xoff + wp->sx > ctx->ox + ctx->sx) { - /* Both left and right not visible. */ - xoff = 0; - sx = ctx->sx; - } else if (wp->xoff < ctx->ox) { - /* Left not visible. */ - xoff = 0; - sx = wp->sx - (ctx->ox - wp->xoff); - } else { - /* Right not visible. */ - xoff = wp->xoff - ctx->ox; - sx = wp->sx - xoff; - } - if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) { - /* All visible. */ - yoff = wp->yoff - ctx->oy; - sy = wp->sy; - } else if (wp->yoff < ctx->oy && - wp->yoff + wp->sy > ctx->oy + ctx->sy) { - /* Both top and bottom not visible. */ - yoff = 0; - sy = ctx->sy; - } else if (wp->yoff < ctx->oy) { - /* Top not visible. */ - yoff = 0; - sy = wp->sy - (ctx->oy - wp->yoff); - } else { - /* Bottom not visible. */ - yoff = wp->yoff - ctx->oy; - sy = wp->sy - yoff; - } - - if (ctx->statustop) - yoff += ctx->statuslines; - px = sx / 2; - py = sy / 2; - - if (window_pane_index(wp, &pane) != 0) - fatalx("index not found"); - len = xsnprintf(buf, sizeof buf, "%u", pane); - - if (sx < len) - return; - if (w->active == wp) - name = "display-panes-active-colour"; - else - name = "display-panes-colour"; - ft = format_create_defaults(NULL, c, s, NULL, wp); - style_apply(&fgc, oo, name, ft); - format_free(ft); - - memcpy(&bgc, &grid_default_cell, sizeof bgc); - bgc.bg = fgc.fg; - - if (pane > 9 && pane < 35) - llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10)); - else - llen = 0; - - if (sx < len * 6 || sy < 5) { - tty_attributes(tty, &fgc, NULL); - if (sx >= len + llen + 1) { - len += llen + 1; - cx = xoff + px - len / 2; - cy = yoff + py; - cmd_display_panes_put(ctx, wp, cx, cy, buf, len); - cx += len; - cmd_display_panes_put(ctx, wp, cx, cy, " ", 1); - cx++; - cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen); - } else { - cx = xoff + px - len / 2; - cy = yoff + py; - cmd_display_panes_put(ctx, wp, cx, cy, buf, len); - } - goto out; - } - - px -= len * 3; - py -= 2; - - tty_attributes(tty, &bgc, NULL); - for (ptr = buf; *ptr != '\0'; ptr++) { - if (*ptr < '0' || *ptr > '9') - continue; - idx = *ptr - '0'; - - for (j = 0; j < 5; j++) { - for (i = px; i < px + 5; i++) { - if (!window_clock_table[idx][j][i - px]) - continue; - cx = xoff + i; - cy = yoff + py + j; - cmd_display_panes_put(ctx, wp, cx, cy, " ", 1); - } - } - px += 6; - } - - if (sy <= 6) - goto out; - cmd_display_panes_draw_format(ctx, wp, xoff, yoff, sx, &fgc); - if (llen != 0) { - tty_attributes(tty, &fgc, NULL); - cx = xoff + sx / 2 + len * 3 - llen - 1; - cy = yoff + py + 5; - cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen); - } - -out: - tty_cursor(tty, 0, 0); -} - -static void -cmd_display_panes_draw(struct client *c, __unused void *data) -{ - struct session *s = c->session; - struct window *w = s->curw->window; - struct window_pane *wp; - struct cmd_display_panes_ctx ctx; - u_int lines; - - log_debug("%s: %s @%u", __func__, c->name, w->id); - - memset(&ctx, 0, sizeof ctx); - ctx.c = c; - tty_window_offset(&c->tty, &ctx.ox, &ctx.oy, &ctx.sx, &ctx.sy); - if (options_get_number(s->options, "status-position") == 0) { - lines = status_line_size(c); - if (c->message_string != NULL || c->prompt != NULL) - lines = (lines == 0 ? 1 : lines); - ctx.statuslines = lines; - ctx.statustop = 1; - } - - TAILQ_FOREACH(wp, &w->panes, entry) { - if (window_pane_is_visible(wp)) - cmd_display_panes_draw_pane(&ctx, wp); - } -} - -static void -cmd_display_panes_free(__unused struct client *c, void *data) -{ - struct cmd_display_panes_data *cdata = data; - - if (cdata->item != NULL) - cmdq_continue(cdata->item); - args_make_commands_free(cdata->state); - free(cdata); -} - -static int -cmd_display_panes_key(struct client *c, void *data, struct key_event *event) -{ - struct cmd_display_panes_data *cdata = data; - char *expanded, *error; - struct cmdq_item *item = cdata->item, *new_item; - struct cmd_list *cmdlist; - struct window *w = c->session->curw->window; - struct window_pane *wp; - u_int index; - key_code key; - - if (event->key >= '0' && event->key <= '9') - index = event->key - '0'; - else if ((event->key & KEYC_MASK_MODIFIERS) == 0) { - key = (event->key & KEYC_MASK_KEY); - if (key >= 'a' && key <= 'z') - index = 10 + (key - 'a'); - else - return (-1); - } else - return (-1); - - wp = window_pane_at_index(w, index); - if (wp == NULL) - return (1); - window_unzoom(w, 1); - - xasprintf(&expanded, "%%%u", wp->id); - - cmdlist = args_make_commands(cdata->state, 1, &expanded, &error); - if (cmdlist == NULL) { - cmdq_append(c, cmdq_get_error(error)); - free(error); - } else if (item == NULL) { - new_item = cmdq_get_command(cmdlist, NULL); - cmdq_append(c, new_item); - } else { - new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); - cmdq_insert_after(item, new_item); - } - - free(expanded); - return (1); -} - -static enum cmd_retval -cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) -{ - struct args *args = cmd_get_args(self); - struct client *tc = cmdq_get_target_client(item); - struct session *s = tc->session; - u_int delay; - char *cause; - struct cmd_display_panes_data *cdata; - int wait = !args_has(args, 'b'); - - if (tc->overlay_draw != NULL) - return (CMD_RETURN_NORMAL); - - if (args_has(args, 'd')) { - delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause); - if (cause != NULL) { - cmdq_error(item, "delay %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - } else - delay = options_get_number(s->options, "display-panes-time"); - - cdata = xcalloc(1, sizeof *cdata); - if (wait) - cdata->item = item; - cdata->state = args_make_commands_prepare(self, item, 0, - "select-pane -t \"%%%\"", wait, 0); - - if (args_has(args, 'N')) { - server_client_set_overlay(tc, delay, NULL, NULL, - cmd_display_panes_draw, NULL, cmd_display_panes_free, NULL, - cdata); - } else { - server_client_set_overlay(tc, delay, NULL, NULL, - cmd_display_panes_draw, cmd_display_panes_key, - cmd_display_panes_free, NULL, cdata); - } - - if (!wait) - return (CMD_RETURN_NORMAL); - return (CMD_RETURN_WAIT); -} diff --git a/cmd-find-window.c b/cmd-find-window.c index 28a8949cd..0091ae907 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-find-window.c,v 1.56 2023/12/27 20:42:01 nicm Exp $ */ +/* $OpenBSD: cmd-find-window.c,v 1.57 2026/07/14 17:17:17 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -109,7 +109,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) args_set(new_args, 'Z', NULL, 0); args_set(new_args, 'f', filter, 0); - window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args); + window_pane_set_mode(wp, NULL, &window_tree_mode, item, target, + new_args); args_free(new_args); return (CMD_RETURN_NORMAL); diff --git a/cmd-queue.c b/cmd-queue.c index 463d82b3f..973b28e37 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-queue.c,v 1.120 2026/07/10 13:38:45 nicm Exp $ */ +/* $OpenBSD: cmd-queue.c,v 1.121 2026/07/14 17:17:17 nicm Exp $ */ /* * Copyright (c) 2013 Nicholas Marriott @@ -148,6 +148,13 @@ cmdq_get_name(struct cmdq_item *item) return (item->name); } +/* Get item command. */ +struct cmd * +cmdq_get_cmd(struct cmdq_item *item) +{ + return (item->cmd); +} + /* Get item client. */ struct client * cmdq_get_client(struct cmdq_item *item) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index b440828fb..7f5514475 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-run-shell.c,v 1.91 2026/06/01 08:27:37 nicm Exp $ */ +/* $OpenBSD: cmd-run-shell.c,v 1.92 2026/07/14 17:17:17 nicm Exp $ */ /* * Copyright (c) 2009 Tiago Cunha @@ -100,7 +100,8 @@ cmd_run_shell_print(struct job *job, const char *msg) wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) - window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); + window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL, + NULL); window_copy_add(wp, 1, "%s", msg); } diff --git a/layout.c b/layout.c index bddad3ab8..c3554718c 100644 --- a/layout.c +++ b/layout.c @@ -1,4 +1,4 @@ -/* $OpenBSD: layout.c,v 1.94 2026/07/13 10:03:27 nicm Exp $ */ +/* $OpenBSD: layout.c,v 1.95 2026/07/14 17:17:17 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -370,13 +370,30 @@ layout_fix_offsets(struct window *w) layout_fix_offsets1(lc); } +static int +layout_cell_is_last_tiled(struct layout_cell *lc) +{ + struct layout_cell *lcchild, *lcparent = lc->parent; + + if (lcparent == NULL) + return (layout_cell_is_tiled(lc)); + + TAILQ_FOREACH_REVERSE(lcchild, &lcparent->cells, layout_cells, entry) { + if (layout_cell_is_tiled(lcchild) || + layout_cell_has_tiled_child(lcchild)) + break; + } + + return (lcchild == lc); +} + /* Is this a top cell? */ static int -layout_cell_is_top(struct window *w, struct layout_cell *lc) +layout_cell_is_top(struct layout_cell *root, struct layout_cell *lc) { struct layout_cell *next; - while (lc != w->layout_root) { + while (lc != root) { next = lc->parent; if (next == NULL) return (0); @@ -390,24 +407,17 @@ layout_cell_is_top(struct window *w, struct layout_cell *lc) /* Is this a bottom cell? */ static int -layout_cell_is_bottom(struct window *w, struct layout_cell *lc) +layout_cell_is_bottom(struct layout_cell *root, struct layout_cell *lc) { - struct layout_cell *next, *edge; + struct layout_cell *next; - while (lc != w->layout_root) { + while (lc != root) { next = lc->parent; if (next == NULL) return (0); - if (next->type == LAYOUT_TOPBOTTOM) { - edge = TAILQ_LAST(&next->cells, layout_cells); - while (edge != NULL) { - if (~edge->flags & LAYOUT_CELL_FLOATING) - break; - edge = TAILQ_PREV(edge, layout_cells, entry); - } - if (lc != edge) - return (0); - } + if (next->type == LAYOUT_TOPBOTTOM && + !layout_cell_is_last_tiled(lc)) + return (0); lc = next; } return (1); @@ -417,14 +427,14 @@ layout_cell_is_bottom(struct window *w, struct layout_cell *lc) * Returns 1 if we need to add an extra line for the pane status line. This is * the case for the most upper or lower panes only. */ -static int -layout_add_horizontal_border(struct window *w, struct layout_cell *lc, +int +layout_add_horizontal_border(struct layout_cell *root, struct layout_cell *lc, int status) { if (status == PANE_STATUS_TOP) - return (layout_cell_is_top(w, lc)); + return (layout_cell_is_top(root, lc)); if (status == PANE_STATUS_BOTTOM) - return (layout_cell_is_bottom(w, lc)); + return (layout_cell_is_bottom(root, lc)); return (0); } @@ -433,13 +443,11 @@ void layout_fix_panes(struct window *w, struct window_pane *skip) { struct window_pane *wp; - struct layout_cell *lc; + struct layout_cell *lc, *root = w->layout_root; int status, sb_w, sb_pad; int old_xoff, old_yoff, changed = 0; u_int sx, sy, old_sx, old_sy; - status = window_get_pane_status(w); - TAILQ_FOREACH(wp, &w->panes, entry) { if ((lc = wp->layout_cell) == NULL || wp == skip) continue; @@ -454,8 +462,9 @@ layout_fix_panes(struct window *w, struct window_pane *skip) sx = lc->g.sx; sy = lc->g.sy; + status = window_pane_get_pane_status(wp); if (!window_pane_is_floating(wp) && - layout_add_horizontal_border(w, lc, status)) { + layout_add_horizontal_border(root, lc, status)) { if (status == PANE_STATUS_TOP) wp->yoff++; if (sy > 1) @@ -523,7 +532,7 @@ static u_int layout_resize_check(struct window *w, struct layout_cell *lc, enum layout_type type) { - struct layout_cell *lcchild; + struct layout_cell *lcchild, *root = w->layout_root; struct style *sb_style = &w->active->scrollbar_style; u_int available, minimum; int status; @@ -541,7 +550,7 @@ layout_resize_check(struct window *w, struct layout_cell *lc, minimum = PANE_MINIMUM; } else { available = lc->g.sy; - if (layout_add_horizontal_border(w, lc, status)) + if (layout_add_horizontal_border(root, lc, status)) minimum = PANE_MINIMUM + 1; else minimum = PANE_MINIMUM; @@ -1256,9 +1265,10 @@ int layout_split_check_space(struct window_pane *wp, struct layout_cell *lc, enum layout_type type) { - struct style *sb_style = &wp->scrollbar_style; - u_int minimum, sx = lc->g.sx, sy = lc->g.sy; - int status; + struct layout_cell *root = wp->window->layout_root; + struct style *sb_style = &wp->scrollbar_style; + u_int minimum, sx = lc->g.sx, sy = lc->g.sy; + int status; if (lc->flags & LAYOUT_CELL_FLOATING) fatalx("floating cells cannot be split"); @@ -1276,7 +1286,7 @@ layout_split_check_space(struct window_pane *wp, struct layout_cell *lc, return (0); break; case LAYOUT_TOPBOTTOM: - if (layout_add_horizontal_border(wp->window, lc, status)) + if (layout_add_horizontal_border(root, lc, status)) minimum = PANE_MINIMUM * 2 + 2; else minimum = PANE_MINIMUM * 2 + 1; @@ -1509,7 +1519,7 @@ layout_close_pane(struct window_pane *wp) int layout_spread_cell(struct window *w, struct layout_cell *parent) { - struct layout_cell *lc; + struct layout_cell *lc, *root = w->layout_root; u_int number, each, size, this, remainder; int change, changed, status; @@ -1524,7 +1534,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) if (parent->type == LAYOUT_LEFTRIGHT) size = parent->g.sx; else if (parent->type == LAYOUT_TOPBOTTOM) { - if (layout_add_horizontal_border(w, parent, status)) + if (layout_add_horizontal_border(root, parent, status)) size = parent->g.sy - 1; else size = parent->g.sy; @@ -1555,7 +1565,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) } layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change); } else if (parent->type == LAYOUT_TOPBOTTOM) { - if (layout_add_horizontal_border(w, lc, status)) + if (layout_add_horizontal_border(root, lc, status)) this = each + 1; else this = each; diff --git a/options-table.c b/options-table.c index ddbb9404d..6a33b1151 100644 --- a/options-table.c +++ b/options-table.c @@ -1,4 +1,4 @@ -/* $OpenBSD: options-table.c,v 1.236 2026/07/13 19:32:28 nicm Exp $ */ +/* $OpenBSD: options-table.c,v 1.237 2026/07/14 17:17:17 nicm Exp $ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -815,40 +815,6 @@ const struct options_table_entry options_table[] = { "the client to another session if any exist." }, - { .name = "display-panes-active-colour", - .type = OPTIONS_TABLE_STRING, - .scope = OPTIONS_TABLE_SESSION, - .flags = OPTIONS_TABLE_IS_COLOUR, - .default_str = "themered", - .text = "Colour of the active pane for 'display-panes'." - }, - - { .name = "display-panes-colour", - .type = OPTIONS_TABLE_STRING, - .scope = OPTIONS_TABLE_SESSION, - .flags = OPTIONS_TABLE_IS_COLOUR, - .default_str = "themeblue", - .text = "Colour of not active panes for 'display-panes'." - }, - - { .name = "display-panes-format", - .type = OPTIONS_TABLE_STRING, - .scope = OPTIONS_TABLE_SESSION, - .default_str = "#[align=right]#{pane_width}x#{pane_height}", - .text = "Format of text shown by 'display-panes', expanded for each " - "pane." - }, - - { .name = "display-panes-time", - .type = OPTIONS_TABLE_NUMBER, - .scope = OPTIONS_TABLE_SESSION, - .minimum = 1, - .maximum = INT_MAX, - .default_num = 1000, - .unit = "milliseconds", - .text = "Time for which 'display-panes' should show pane numbers." - }, - { .name = "display-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, @@ -1355,6 +1321,49 @@ const struct options_table_entry options_table[] = { .text = "Time format of the clock in clock mode." }, + { .name = "display-panes-active-colour", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .flags = OPTIONS_TABLE_IS_COLOUR, + .default_str = "themered", + .text = "Colour of the active pane for 'display-panes'." + }, + + { .name = "display-panes-border-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "bg=themedarkgrey,fg=themelightgrey", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = ",", + .text = "Style of the pane borders in 'display-panes'." + }, + + { .name = "display-panes-colour", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .flags = OPTIONS_TABLE_IS_COLOUR, + .default_str = "themeblue", + .text = "Colour of not active panes for 'display-panes'." + }, + + { .name = "display-panes-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "#[align=right]#{pane_width}x#{pane_height}", + .text = "Format of text shown by 'display-panes', expanded for each " + "pane." + }, + + { .name = "display-panes-time", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 1000, + .unit = "milliseconds", + .text = "Time for which 'display-panes' should show pane numbers." + }, + { .name = "copy-mode-match-style", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, diff --git a/server-client.c b/server-client.c index 1e85f0688..c78015f2c 100644 --- a/server-client.c +++ b/server-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server-client.c,v 1.489 2026/07/13 16:07:47 nicm Exp $ */ +/* $OpenBSD: server-client.c,v 1.490 2026/07/14 17:17:17 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -3125,7 +3125,8 @@ server_client_print(struct client *c, int parse, struct evbuffer *evb) wp = server_client_get_pane(c); wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) - window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); + window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL, + NULL); if (parse) { do { line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF); diff --git a/tmux.1 b/tmux.1 index ca0ba018b..a393c5954 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.1139 2026/07/14 15:06:54 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.1140 2026/07/14 17:17:18 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -3169,19 +3169,31 @@ option. .It Xo .Tg displayp .Ic display\-panes -.Op Fl bN +.Op Fl kNZ .Op Fl d Ar duration -.Op Fl t Ar target\-client +.Op Fl s Ar source\-window +.Op Fl t Ar target\-pane .Op Ar template .Xc .D1 Pq alias: Ic displayp -Display a visible indicator of each pane shown by -.Ar target\-client . +Put +.Ar target\-pane +into panes mode, which displays a preview of the window and a visible +indicator of each pane. +If +.Fl s +is given, the panes in +.Ar source\-window +are displayed instead of those in the window containing +.Ar target\-pane . See the -.Ic display\-panes\-colour +.Ic display\-panes\-colour , +.Ic display\-panes\-active\-colour , +.Ic display\-panes\-border\-style , +.Ic display\-panes\-format and -.Ic display\-panes\-active\-colour -session options. +.Ic display\-panes\-time +window options. The indicator is closed when a key is pressed (unless .Fl N is given) or @@ -3193,11 +3205,16 @@ is not given, .Ic display\-panes\-time is used. A duration of zero means the indicator stays until a key is pressed. +The mode is zoomed by default; +.Fl Z +starts it unzoomed. +.Fl k +kills the pane when the mode is exited. While the indicator is on screen, a pane may be chosen with the .Ql 0 to .Ql 9 -keys, which will cause +keys or by clicking it with the mouse, which will cause .Ar template to be executed as a command with .Ql %% @@ -3205,9 +3222,6 @@ substituted by the pane ID. The default .Ar template is "select\-pane \-t \[aq]%%\[aq]". -With -.Fl b , -other commands are not blocked from running until the indicator is closed. .Tg findw .It Xo Ic find\-window .Op Fl iCNrTZ @@ -5231,24 +5245,6 @@ If or .Ic next , the client is switched to the previous or next session in alphabetical order. -.It Ic display\-panes\-active\-colour Ar colour -Set the colour used by the -.Ic display\-panes -command to show the indicator for the active pane. -.It Ic display\-panes\-colour Ar colour -Set the colour used by the -.Ic display\-panes -command to show the indicators for inactive panes. -.It Ic display\-panes\-format Ar format -Set the -.Ar format -of the text shown by the -.Ic display\-panes -command, expanded for each pane. -.It Ic display\-panes\-time Ar time -Set the time in milliseconds for which the indicators shown by the -.Ic display\-panes -command appear. .It Ic display\-time Ar time Set the amount of time for which status line messages and other on-screen indicators are displayed. @@ -5680,6 +5676,38 @@ Set clock colour. .Xc Set clock hour format. .Pp +.It Ic display\-panes\-active\-colour Ar colour +Set the colour used by the +.Ic display\-panes +command to show the indicator for the active pane. +.Pp +.It Ic display\-panes\-border\-style Ar style +Set the style of borders in panes mode used by the +.Ic display\-panes +command. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic display\-panes\-colour Ar colour +Set the colour used by the +.Ic display\-panes +command to show the indicators for inactive panes. +.Pp +.It Ic display\-panes\-format Ar format +Set the +.Ar format +of the text shown by the +.Ic display\-panes +command, expanded for each pane. +.Pp +.It Ic display\-panes\-time Ar time +Set the time in milliseconds for which the indicators shown by the +.Ic display\-panes +command appear. +.Pp .It Ic fill\-character Ar character Set the character used to fill areas of the terminal unused by a window. .Pp diff --git a/tmux.h b/tmux.h index fc9153e19..5ee3b26f6 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1402 2026/07/13 22:03:08 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1403 2026/07/14 17:17:18 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1133,8 +1133,13 @@ struct window_mode { const char *name; const char *default_format; + int flags; +#define WINDOW_MODE_HIDE_PANE_STATUS 0x1 +#define WINDOW_MODE_NO_STACK 0x2 + struct screen *(*init)(struct window_mode_entry *, - struct cmd_find_state *, struct args *); + struct cmdq_item *, struct cmd_find_state *, + struct args *); void (*free)(struct window_mode_entry *); void (*resize)(struct window_mode_entry *, u_int, u_int); void (*update)(struct window_mode_entry *); @@ -3109,6 +3114,7 @@ void cmdq_merge_formats(struct cmdq_item *, struct format_tree *); struct cmdq_list *cmdq_new(void); void cmdq_free(struct cmdq_list *); const char *cmdq_get_name(struct cmdq_item *); +struct cmd *cmdq_get_cmd(struct cmdq_item *); struct client *cmdq_get_client(struct cmdq_item *); struct client *cmdq_get_target_client(struct cmdq_item *); struct cmdq_state *cmdq_get_state(struct cmdq_item *); @@ -3661,7 +3667,7 @@ void window_pane_clear_resizes(struct window_pane *, struct window_pane_resize *); int window_pane_set_mode(struct window_pane *, struct window_pane *, const struct window_mode *, - struct cmd_find_state *, struct args *); + struct cmdq_item *, struct cmd_find_state *, struct args *); void window_pane_reset_mode(struct window_pane *); void window_pane_reset_mode_all(struct window_pane *); int window_pane_key(struct window_pane *, struct client *, @@ -3763,6 +3769,8 @@ void layout_make_leaf(struct layout_cell *, struct window_pane *); void layout_make_node(struct layout_cell *, enum layout_type); void layout_fix_zindexes(struct window *, struct layout_cell *); int layout_cell_is_tiled(struct layout_cell *); +int layout_add_horizontal_border(struct layout_cell *, + struct layout_cell *, int); void layout_fix_offsets(struct window *); void layout_fix_panes(struct window *, struct window_pane *); void layout_resize_adjust(struct window *, struct layout_cell *, @@ -3886,6 +3894,9 @@ extern const struct window_mode window_switch_mode; extern const struct window_mode window_clock_mode; extern const char window_clock_table[14][5][5]; +/* window-panes.c */ +extern const struct window_mode window_panes_mode; + /* window-client.c */ extern const struct window_mode window_client_mode; diff --git a/window-buffer.c b/window-buffer.c index f3116f8da..17eae8f3c 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-buffer.c,v 1.49 2026/06/26 08:10:49 nicm Exp $ */ +/* $OpenBSD: window-buffer.c,v 1.50 2026/07/14 17:17:18 nicm Exp $ */ /* * Copyright (c) 2017 Nicholas Marriott @@ -28,7 +28,8 @@ #include "tmux.h" static struct screen *window_buffer_init(struct window_mode_entry *, - struct cmd_find_state *, struct args *); + struct cmdq_item *, struct cmd_find_state *, + struct args *); static void window_buffer_free(struct window_mode_entry *); static void window_buffer_resize(struct window_mode_entry *, u_int, u_int); @@ -362,7 +363,8 @@ window_buffer_help(u_int *width, const char **item) } static struct screen * -window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, +window_buffer_init(struct window_mode_entry *wme, + __unused struct cmdq_item *item, struct cmd_find_state *fs, struct args *args) { struct window_pane *wp = wme->wp; diff --git a/window-client.c b/window-client.c index 64e38cb81..b94c22119 100644 --- a/window-client.c +++ b/window-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-client.c,v 1.46 2026/07/04 22:09:06 nicm Exp $ */ +/* $OpenBSD: window-client.c,v 1.47 2026/07/14 17:17:18 nicm Exp $ */ /* * Copyright (c) 2017 Nicholas Marriott @@ -26,7 +26,8 @@ #include "tmux.h" static struct screen *window_client_init(struct window_mode_entry *, - struct cmd_find_state *, struct args *); + struct cmdq_item *, struct cmd_find_state *, + struct args *); static void window_client_free(struct window_mode_entry *); static void window_client_resize(struct window_mode_entry *, u_int, u_int); @@ -421,7 +422,8 @@ window_client_help(u_int *width, const char **item) static struct screen * window_client_init(struct window_mode_entry *wme, - __unused struct cmd_find_state *fs, struct args *args) + __unused struct cmdq_item *item, __unused struct cmd_find_state *fs, + struct args *args) { struct window_pane *wp = wme->wp; struct window_client_modedata *data; diff --git a/window-clock.c b/window-clock.c index acd4382b3..2dc4ef332 100644 --- a/window-clock.c +++ b/window-clock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-clock.c,v 1.34 2026/06/25 16:32:42 nicm Exp $ */ +/* $OpenBSD: window-clock.c,v 1.35 2026/07/14 17:17:18 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -25,7 +25,8 @@ #include "tmux.h" static struct screen *window_clock_init(struct window_mode_entry *, - struct cmd_find_state *, struct args *); + struct cmdq_item *, struct cmd_find_state *, + struct args *); static void window_clock_free(struct window_mode_entry *); static void window_clock_resize(struct window_mode_entry *, u_int, u_int); static void window_clock_key(struct window_mode_entry *, struct client *, @@ -169,7 +170,8 @@ window_clock_timer_callback(__unused int fd, __unused short events, void *arg) static struct screen * window_clock_init(struct window_mode_entry *wme, - __unused struct cmd_find_state *fs, __unused struct args *args) + __unused struct cmdq_item *item, __unused struct cmd_find_state *fs, + __unused struct args *args) { struct window_pane *wp = wme->wp; struct window_clock_mode_data *data; diff --git a/window-copy.c b/window-copy.c index 0991a387c..a6909a8d4 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-copy.c,v 1.420 2026/07/14 15:06:54 nicm Exp $ */ +/* $OpenBSD: window-copy.c,v 1.421 2026/07/14 17:17:18 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -33,9 +33,9 @@ static void window_copy_command(struct window_mode_entry *, struct client *, struct session *, struct winlink *, struct args *, struct mouse_event *); static struct screen *window_copy_init(struct window_mode_entry *, - struct cmd_find_state *, struct args *); + struct cmdq_item *, struct cmd_find_state *, struct args *); static struct screen *window_copy_view_init(struct window_mode_entry *, - struct cmd_find_state *, struct args *); + struct cmdq_item *, struct cmd_find_state *, struct args *); static void window_copy_free(struct window_mode_entry *); static void window_copy_resize(struct window_mode_entry *, u_int, u_int); static void window_copy_formats(struct window_mode_entry *, @@ -597,7 +597,8 @@ window_copy_common_init(struct window_mode_entry *wme) static struct screen * window_copy_init(struct window_mode_entry *wme, - __unused struct cmd_find_state *fs, struct args *args) + __unused struct cmdq_item *item, __unused struct cmd_find_state *fs, + struct args *args) { struct window_pane *wp = wme->swp; struct window_copy_mode_data *data; @@ -648,7 +649,8 @@ window_copy_init(struct window_mode_entry *wme, static struct screen * window_copy_view_init(struct window_mode_entry *wme, - __unused struct cmd_find_state *fs, __unused struct args *args) + __unused struct cmdq_item *item, __unused struct cmd_find_state *fs, + __unused struct args *args) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data; diff --git a/window-customize.c b/window-customize.c index d4b7e7d86..4f5ff8a9f 100644 --- a/window-customize.c +++ b/window-customize.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-customize.c,v 1.33 2026/07/13 22:03:08 nicm Exp $ */ +/* $OpenBSD: window-customize.c,v 1.34 2026/07/14 17:17:18 nicm Exp $ */ /* * Copyright (c) 2020 Nicholas Marriott @@ -25,7 +25,8 @@ #include "tmux.h" static struct screen *window_customize_init(struct window_mode_entry *, - struct cmd_find_state *, struct args *); + struct cmdq_item *, struct cmd_find_state *, + struct args *); static void window_customize_free(struct window_mode_entry *); static void window_customize_resize(struct window_mode_entry *, u_int, u_int); @@ -1544,7 +1545,8 @@ window_customize_help(u_int *width, const char **item) } static struct screen * -window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs, +window_customize_init(struct window_mode_entry *wme, + __unused struct cmdq_item *item, struct cmd_find_state *fs, struct args *args) { struct window_pane *wp = wme->wp; diff --git a/window-panes.c b/window-panes.c new file mode 100644 index 000000000..c262c82c3 --- /dev/null +++ b/window-panes.c @@ -0,0 +1,1067 @@ +/* $OpenBSD: window-panes.c,v 1.1 2026/07/14 17:17:18 nicm Exp $ */ + +/* + * Copyright (c) 2026 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +static struct screen *window_panes_init(struct window_mode_entry *, + struct cmdq_item *, struct cmd_find_state *, + struct args *); +static void window_panes_free(struct window_mode_entry *); +static void window_panes_resize(struct window_mode_entry *, u_int, + u_int); +static void window_panes_key(struct window_mode_entry *, + struct client *, struct session *, + struct winlink *, key_code, struct mouse_event *); + +const struct window_mode window_panes_mode = { + .name = "panes-mode", + .flags = WINDOW_MODE_HIDE_PANE_STATUS|WINDOW_MODE_NO_STACK, + + .init = window_panes_init, + .free = window_panes_free, + .resize = window_panes_resize, + .key = window_panes_key, +}; + +struct window_panes_area { + u_int id; + u_int x; + u_int y; + u_int sx; + u_int sy; +}; + +struct window_panes_modedata { + struct window_pane *wp; + struct session *session; + u_int source_session; + u_int source_window; + struct screen screen; + + struct event timer; + + struct args_command_state *state; + u_int delay; + int ignore_keys; + int zoomed; + + struct window_panes_area *areas; + u_int areas_size; +}; + +#define WINDOW_PANES_BORDER_L 0x1 +#define WINDOW_PANES_BORDER_R 0x2 +#define WINDOW_PANES_BORDER_U 0x4 +#define WINDOW_PANES_BORDER_D 0x8 + +static int +window_panes_get_source(struct window_panes_modedata *data, struct session **sp, + struct winlink **wlp, struct window **wp) +{ + struct session *s; + struct window *w; + struct winlink *wl = NULL; + + w = window_find_by_id(data->source_window); + if (w == NULL) + return (0); + + s = session_find_by_id(data->source_session); + if (s != NULL) + wl = winlink_find_by_window(&s->windows, w); + if (wl == NULL) + s = data->session; + if (s != NULL) + wl = winlink_find_by_window(&s->windows, w); + + if (sp != NULL) + *sp = s; + if (wlp != NULL) + *wlp = wl; + if (wp != NULL) + *wp = w; + return (1); +} + +static void +window_panes_free_areas(struct window_panes_modedata *data) +{ + free(data->areas); + data->areas = NULL; + data->areas_size = 0; +} + +static void +window_panes_add_area(struct window_panes_modedata *data, + struct window_pane *wp, u_int x, u_int y, u_int sx, u_int sy) +{ + struct window_panes_area *area; + + data->areas = xreallocarray(data->areas, data->areas_size + 1, + sizeof *data->areas); + area = &data->areas[data->areas_size++]; + area->id = wp->id; + area->x = x; + area->y = y; + area->sx = sx; + area->sy = sy; +} + +static int +window_panes_pane_floating(struct window_pane *wp) +{ + struct layout_cell *lc = wp->saved_layout_cell; + + if (lc == NULL) + lc = wp->layout_cell; + if (lc == NULL || (~lc->flags & LAYOUT_CELL_FLOATING)) + return (0); + return (1); +} + +static int +window_panes_pane_visible(struct window_pane *wp) +{ + if (wp->saved_layout_cell != NULL) + return (1); + return (window_pane_is_visible(wp)); +} + +static int +window_panes_get_geometry(struct window_pane *wp, struct layout_cell *root, + u_int osx, u_int osy, u_int dsx, u_int dsy, u_int *xp, u_int *yp, + u_int *sxp, u_int *syp) +{ + struct layout_cell *lc = wp->saved_layout_cell; + int status; + u_int x, y, sx, sy, x2, y2; + + if (lc == NULL) + lc = wp->layout_cell; + if (lc == NULL || osx == 0 || osy == 0 || dsx == 0 || dsy == 0) + return (0); + + if (osx <= dsx && osy <= dsy) { + x = lc->g.xoff; + y = lc->g.yoff; + x2 = x + lc->g.sx; + y2 = y + lc->g.sy; + } else { + x = (u_int)lc->g.xoff * dsx / osx; + y = (u_int)lc->g.yoff * dsy / osy; + x2 = ((u_int)lc->g.xoff + lc->g.sx) * dsx / osx; + y2 = ((u_int)lc->g.yoff + lc->g.sy) * dsy / osy; + } + + if (x >= dsx || y >= dsy) + return (0); + if (x2 <= x) + x2 = x + 1; + if (y2 <= y) + y2 = y + 1; + if (x2 > dsx) + x2 = dsx; + if (y2 > dsy) + y2 = dsy; + + sx = x2 - x; + sy = y2 - y; + if (sx == 0 || sy == 0) + return (0); + + status = window_get_pane_status(wp->window); + if (layout_add_horizontal_border(root, lc, status) && sy > 1) { + if (status == PANE_STATUS_TOP) + y++; + sy--; + } + + *xp = x; + *yp = y; + *sxp = sx; + *syp = sy; + return (1); +} + +static void +window_panes_get_border_cell(struct window_panes_modedata *data, + struct grid_cell *gc) +{ + struct format_tree *ft; + struct window_pane *wp = data->wp; + struct session *s = data->session; + + memcpy(gc, &grid_default_cell, sizeof *gc); + ft = format_create_defaults(NULL, NULL, s, s->curw, wp); + style_apply(gc, wp->window->options, "display-panes-border-style", ft); + format_free(ft); +} + +static int +window_panes_map_x(u_int x, u_int osx, u_int dsx) +{ + if (osx <= dsx) + return (x); + return (x * dsx / osx); +} + +static int +window_panes_map_y(u_int y, u_int osy, u_int dsy) +{ + if (osy <= dsy) + return (y); + return (y * dsy / osy); +} + +static struct layout_cell * +window_panes_next_tiled_cell(struct layout_cell *lc) +{ + struct layout_cell *next; + + next = TAILQ_NEXT(lc, entry); + while (next != NULL) { + if (~next->flags & LAYOUT_CELL_FLOATING) + return (next); + next = TAILQ_NEXT(next, entry); + } + return (NULL); +} + +static void +window_panes_mark_border(u_char *map, u_int dsx, u_int dsy, u_int x, u_int y, + u_char mask) +{ + if (x < dsx && y < dsy) + map[y * dsx + x] |= mask; +} + +static void +window_panes_mark_vline(u_char *map, u_int dsx, u_int dsy, int x, int y, int y2) +{ + u_char mask; + int yy; + + if (x < 0 || (u_int)x >= dsx || y2 <= y) + return; + if (y < 0) + y = 0; + if ((u_int)y2 > dsy) + y2 = dsy; + + for (yy = y; yy < y2; yy++) { + mask = 0; + if (yy > y) + mask |= WINDOW_PANES_BORDER_U; + if (yy + 1 < y2) + mask |= WINDOW_PANES_BORDER_D; + if (mask == 0) + mask = WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D; + window_panes_mark_border(map, dsx, dsy, x, yy, mask); + } +} + +static void +window_panes_mark_hline(u_char *map, u_int dsx, u_int dsy, int x, int x2, int y) +{ + u_char mask; + int xx; + + if (y < 0 || (u_int)y >= dsy || x2 <= x) + return; + if (x < 0) + x = 0; + if ((u_int)x2 > dsx) + x2 = dsx; + + for (xx = x; xx < x2; xx++) { + mask = 0; + if (xx > x) + mask |= WINDOW_PANES_BORDER_L; + if (xx + 1 < x2) + mask |= WINDOW_PANES_BORDER_R; + if (mask == 0) + mask = WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R; + window_panes_mark_border(map, dsx, dsy, xx, y, mask); + } +} + +static void +window_panes_mark_borders_cell(u_char *map, struct layout_cell *lc, u_int osx, + u_int osy, u_int dsx, u_int dsy) +{ + struct layout_cell *lcchild, *lcnext; + int x, y, x2, y2; + + if (lc->type == LAYOUT_WINDOWPANE) + return; + + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + window_panes_mark_borders_cell(map, lcchild, osx, osy, dsx, + dsy); + if (lcchild->flags & LAYOUT_CELL_FLOATING) + continue; + lcnext = window_panes_next_tiled_cell(lcchild); + if (lcnext == NULL) + continue; + + if (lc->type == LAYOUT_LEFTRIGHT) { + x = window_panes_map_x(lcchild->g.xoff + lcchild->g.sx, + osx, dsx); + y = window_panes_map_y(lc->g.yoff, osy, dsy); + y2 = window_panes_map_y(lc->g.yoff + lc->g.sy, osy, + dsy); + window_panes_mark_vline(map, dsx, dsy, x, y, y2); + } else { + x = window_panes_map_x(lc->g.xoff, osx, dsx); + x2 = window_panes_map_x(lc->g.xoff + lc->g.sx, osx, + dsx); + y = window_panes_map_y(lcchild->g.yoff + lcchild->g.sy, + osy, dsy); + window_panes_mark_hline(map, dsx, dsy, x, x2, y); + } + } +} + +static void +window_panes_mark_pane_status_borders(u_char *map, struct window *w, + struct layout_cell *root, u_int osx, u_int osy, u_int dsx, u_int dsy) +{ + struct window_pane *wp; + struct layout_cell *lc; + int status, x, y, x2, y2; + + status = window_get_pane_status(w); + if (status != PANE_STATUS_TOP && status != PANE_STATUS_BOTTOM) + return; + + TAILQ_FOREACH(wp, &w->panes, entry) { + if (!window_panes_pane_visible(wp)) + continue; + + lc = wp->saved_layout_cell; + if (lc == NULL) + lc = wp->layout_cell; + if (lc == NULL || + !layout_add_horizontal_border(root, lc, status)) + continue; + + x = window_panes_map_x(lc->g.xoff, osx, dsx); + x2 = window_panes_map_x(lc->g.xoff + lc->g.sx, osx, dsx); + if (status == PANE_STATUS_TOP) + y = window_panes_map_y(lc->g.yoff, osy, dsy); + else { + y2 = window_panes_map_y(lc->g.yoff + lc->g.sy, osy, + dsy); + y = y2 - 1; + } + window_panes_mark_hline(map, dsx, dsy, x, x2, y); + } +} + +static int +window_panes_get_floating_frame(struct window_pane *wp, u_int osx, u_int osy, + u_int dsx, u_int dsy, int *xp, int *yp, int *x2p, int *y2p) +{ + struct layout_cell *lc; + + lc = wp->saved_layout_cell; + if (lc == NULL) + lc = wp->layout_cell; + if (lc == NULL || (~lc->flags & LAYOUT_CELL_FLOATING)) + return (0); + + if (lc->g.xoff == 0) + *xp = -1; + else + *xp = window_panes_map_x(lc->g.xoff - 1, osx, dsx); + if (lc->g.yoff == 0) + *yp = -1; + else + *yp = window_panes_map_y(lc->g.yoff - 1, osy, dsy); + *x2p = window_panes_map_x(lc->g.xoff + lc->g.sx, osx, dsx); + *y2p = window_panes_map_y(lc->g.yoff + lc->g.sy, osy, dsy); + return (1); +} + +static int +window_panes_clip_floating_body(struct window_pane *wp, u_int osx, u_int osy, + u_int dsx, u_int dsy, u_int *xp, u_int *yp, u_int *sxp, u_int *syp) +{ + int x, y, x2, y2, bx, by, bx2, by2; + + if (!window_panes_get_floating_frame(wp, osx, osy, dsx, dsy, &x, &y, + &x2, &y2)) + return (1); + + bx = *xp; + by = *yp; + bx2 = bx + *sxp - 1; + by2 = by + *syp - 1; + + if (x >= 0 && bx <= x) + bx = x + 1; + if (y >= 0 && by <= y) + by = y + 1; + if ((u_int)x2 < dsx && bx2 >= x2) + bx2 = x2 - 1; + if ((u_int)y2 < dsy && by2 >= y2) + by2 = y2 - 1; + if (bx2 < bx || by2 < by) + return (0); + + *xp = bx; + *yp = by; + *sxp = bx2 - bx + 1; + *syp = by2 - by + 1; + return (1); +} + +static int +window_panes_border_cell_type(u_char mask) +{ + switch (mask) { + case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R| + WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D: + return (CELL_LRUD); + case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R|WINDOW_PANES_BORDER_U: + return (CELL_LRU); + case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R|WINDOW_PANES_BORDER_D: + return (CELL_LRD); + case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R: + case WINDOW_PANES_BORDER_L: + case WINDOW_PANES_BORDER_R: + return (CELL_LR); + case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D: + return (CELL_ULD); + case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_U: + return (CELL_LU); + case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_D: + return (CELL_LD); + case WINDOW_PANES_BORDER_R|WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D: + return (CELL_URD); + case WINDOW_PANES_BORDER_R|WINDOW_PANES_BORDER_U: + return (CELL_RU); + case WINDOW_PANES_BORDER_R|WINDOW_PANES_BORDER_D: + return (CELL_RD); + case WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D: + case WINDOW_PANES_BORDER_U: + case WINDOW_PANES_BORDER_D: + return (CELL_UD); + } + return (CELL_NONE); +} + +static int +window_panes_border_has_horizontal(u_char mask) +{ + return ((mask & (WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R)) != 0); +} + +static int +window_panes_border_has_vertical(u_char mask) +{ + return ((mask & (WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D)) != 0); +} + +static void +window_panes_mark_border_joins_cell(u_char *map, struct layout_cell *lc, + u_int osx, u_int osy, u_int dsx, u_int dsy) +{ + struct layout_cell *lcchild, *lcnext; + int x, y, x2, y2; + + if (lc->type == LAYOUT_WINDOWPANE) + return; + + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + window_panes_mark_border_joins_cell(map, lcchild, osx, osy, + dsx, dsy); + if (lcchild->flags & LAYOUT_CELL_FLOATING) + continue; + lcnext = window_panes_next_tiled_cell(lcchild); + if (lcnext == NULL) + continue; + + if (lc->type == LAYOUT_LEFTRIGHT) { + x = window_panes_map_x(lcchild->g.xoff + lcchild->g.sx, + osx, dsx); + y = window_panes_map_y(lc->g.yoff, osy, dsy); + y2 = window_panes_map_y(lc->g.yoff + lc->g.sy, osy, + dsy); + if (x < 0 || (u_int)x >= dsx) + continue; + if (y > 0 && + window_panes_border_has_horizontal( + map[(y - 1) * dsx + x])) { + window_panes_mark_border(map, dsx, dsy, x, + y - 1, WINDOW_PANES_BORDER_D); + window_panes_mark_border(map, dsx, dsy, x, y, + WINDOW_PANES_BORDER_U); + } + if ((u_int)y2 < dsy && + window_panes_border_has_horizontal( + map[y2 * dsx + x])) { + window_panes_mark_border(map, dsx, dsy, x, y2, + WINDOW_PANES_BORDER_U); + window_panes_mark_border(map, dsx, dsy, x, + y2 - 1, WINDOW_PANES_BORDER_D); + } + } else { + x = window_panes_map_x(lc->g.xoff, osx, dsx); + x2 = window_panes_map_x(lc->g.xoff + lc->g.sx, osx, + dsx); + y = window_panes_map_y(lcchild->g.yoff + lcchild->g.sy, + osy, dsy); + if (y < 0 || (u_int)y >= dsy) + continue; + if (x > 0 && + window_panes_border_has_vertical( + map[y * dsx + x - 1])) { + window_panes_mark_border(map, dsx, dsy, x - 1, + y, WINDOW_PANES_BORDER_R); + window_panes_mark_border(map, dsx, dsy, x, y, + WINDOW_PANES_BORDER_L); + } + if ((u_int)x2 < dsx && + window_panes_border_has_vertical( + map[y * dsx + x2])) { + window_panes_mark_border(map, dsx, dsy, x2, y, + WINDOW_PANES_BORDER_L); + window_panes_mark_border(map, dsx, dsy, x2 - 1, + y, WINDOW_PANES_BORDER_R); + } + } + } +} + +static void +window_panes_draw_borders(struct screen_write_ctx *ctx, struct window *w, + struct layout_cell *lc, const struct grid_cell *gc, u_int osx, u_int osy, + u_int dsx, u_int dsy) +{ + struct grid_cell border_gc; + u_char *map; + u_int xx, yy; + int cell_type; + + if (dsx == 0 || dsy == 0) + return; + + map = xcalloc(dsx, dsy); + window_panes_mark_borders_cell(map, lc, osx, osy, dsx, dsy); + window_panes_mark_pane_status_borders(map, w, lc, osx, osy, dsx, + dsy); + window_panes_mark_border_joins_cell(map, lc, osx, osy, dsx, dsy); + + for (yy = 0; yy < dsy; yy++) { + for (xx = 0; xx < dsx; xx++) { + if (map[yy * dsx + xx] == 0) + continue; + cell_type = window_panes_border_cell_type( + map[yy * dsx + xx]); + memcpy(&border_gc, gc, sizeof border_gc); + border_gc.attr |= GRID_ATTR_CHARSET; + utf8_set(&border_gc.data, CELL_BORDERS[cell_type]); + screen_write_cursormove(ctx, xx, yy, 0); + screen_write_cell(ctx, &border_gc); + } + } + free(map); +} + +static void +window_panes_draw_floating_border(struct screen_write_ctx *ctx, + struct window_pane *wp, const struct grid_cell *gc, u_int osx, u_int osy, + u_int dsx, u_int dsy) +{ + struct grid_cell border_gc; + u_char *map; + u_int xx, yy; + int x, y, x2, y2, cell_type; + + if (dsx == 0 || dsy == 0) + return; + if (!window_panes_get_floating_frame(wp, osx, osy, dsx, dsy, &x, &y, + &x2, &y2)) + return; + + map = xcalloc(dsx, dsy); + window_panes_mark_hline(map, dsx, dsy, x, x2 + 1, y); + window_panes_mark_hline(map, dsx, dsy, x, x2 + 1, y2); + window_panes_mark_vline(map, dsx, dsy, x, y, y2 + 1); + window_panes_mark_vline(map, dsx, dsy, x2, y, y2 + 1); + + for (yy = 0; yy < dsy; yy++) { + for (xx = 0; xx < dsx; xx++) { + if (map[yy * dsx + xx] == 0) + continue; + cell_type = window_panes_border_cell_type( + map[yy * dsx + xx]); + memcpy(&border_gc, gc, sizeof border_gc); + border_gc.attr |= GRID_ATTR_CHARSET; + utf8_set(&border_gc.data, CELL_BORDERS[cell_type]); + screen_write_cursormove(ctx, xx, yy, 0); + screen_write_cell(ctx, &border_gc); + } + } + free(map); +} + +static void +window_panes_clear_floating_area(struct screen_write_ctx *ctx, + struct window_pane *wp, u_int osx, u_int osy, u_int dsx, u_int dsy) +{ + struct grid_cell gc; + int x, y, x2, y2, xx, yy; + + if (!window_panes_get_floating_frame(wp, osx, osy, dsx, dsy, &x, &y, + &x2, &y2)) + return; + + memcpy(&gc, &grid_default_cell, sizeof gc); + if (x < 0) + x = 0; + if (y < 0) + y = 0; + if ((u_int)x2 >= dsx) + x2 = dsx - 1; + if ((u_int)y2 >= dsy) + y2 = dsy - 1; + if (x2 < x || y2 < y) + return; + + for (yy = y; yy <= y2; yy++) { + screen_write_cursormove(ctx, x, yy, 0); + for (xx = x; xx <= x2; xx++) + screen_write_putc(ctx, &gc, ' '); + } +} + +static void +window_panes_draw_format(struct window_panes_modedata *data, + struct screen_write_ctx *ctx, struct window_pane *wp, u_int x, u_int y, + u_int sx, const struct grid_cell *gc) +{ + struct session *s = data->session; + struct winlink *wl = s->curw; + const char *format; + char *expanded; + + if (sx == 0) + return; + format = options_get_string(data->wp->window->options, + "display-panes-format"); + if (*format == '\0') + return; + + window_panes_get_source(data, &s, &wl, NULL); + if (s == NULL) + return; + + expanded = format_single(NULL, format, NULL, s, wl, wp); + if (*expanded != '\0') { + screen_write_cursormove(ctx, x, y, 0); + format_draw(ctx, gc, sx, expanded, NULL, 0); + } + free(expanded); +} + +static void +window_panes_draw_number(struct window_panes_modedata *data, + struct screen_write_ctx *ctx, struct window_pane *wp, u_int pane, u_int x, + u_int y, u_int sx, u_int sy) +{ + struct session *s = data->session; + struct window *w = wp->window; + struct winlink *wl = s->curw; + struct options *oo = data->wp->window->options; + struct grid_cell fgc, bgc; + struct format_tree *ft; + const char *name; + char buf[16], lbuf[16] = { 0 }, *ptr; + size_t len, llen = 0, width; + u_int cx, cy, px, py, idx, i, j, format; + + len = xsnprintf(buf, sizeof buf, "%u", pane); + if (pane > 9 && pane < 35) + llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10)); + if (sx < len) + return; + + window_panes_get_source(data, &s, &wl, NULL); + if (s != NULL) { + if (wl == NULL) + wl = s->curw; + } + if (w->active == wp) + name = "display-panes-active-colour"; + else + name = "display-panes-colour"; + ft = format_create_defaults(NULL, NULL, s, wl, wp); + style_apply(&fgc, oo, name, ft); + format_free(ft); + + memcpy(&bgc, &grid_default_cell, sizeof bgc); + bgc.bg = fgc.fg; + + format = 0; + if (options_get_string(oo, "display-panes-format")[0] != '\0') + format = 1; + width = (len * 6) - 1; + if (sx < width || sy < (format ? 7 : 5)) { + width = len; + if (llen != 0 && sx >= len + llen + 1) + width += llen + 1; + cx = x + (sx - width) / 2; + cy = y + sy / 2; + screen_write_cursormove(ctx, cx, cy, 0); + screen_write_puts(ctx, &fgc, "%s", buf); + if (width > len) + screen_write_puts(ctx, &fgc, " %s", lbuf); + if (format && sy > 1) + window_panes_draw_format(data, ctx, wp, x, y, sx, &fgc); + return; + } + + px = (sx - width) / 2; + py = (sy - 5) / 2; + for (ptr = buf; *ptr != '\0'; ptr++) { + if (*ptr < '0' || *ptr > '9') + continue; + idx = *ptr - '0'; + + for (j = 0; j < 5; j++) { + for (i = 0; i < 5; i++) { + if (!window_clock_table[idx][j][i]) + continue; + screen_write_cursormove(ctx, x + px + i, + y + py + j, 0); + screen_write_putc(ctx, &bgc, ' '); + } + } + px += 6; + } + + if (sy <= 6) + return; + window_panes_draw_format(data, ctx, wp, x, y, sx, &fgc); + if (llen != 0) { + cx = x + px - llen - 1; + cy = y + py + 5; + screen_write_cursormove(ctx, cx, cy, 0); + screen_write_puts(ctx, &fgc, "%s", lbuf); + } +} + +static void +window_panes_draw_pane(struct window_panes_modedata *data, + struct screen_write_ctx *ctx, struct window_pane *wp, + struct layout_cell *root, u_int osx, u_int osy, u_int dsx, u_int dsy) +{ + u_int pane, x, y, sx, sy; + + if (!window_panes_pane_visible(wp)) + return; + if (!window_panes_get_geometry(wp, root, osx, osy, dsx, dsy, &x, &y, + &sx, &sy)) + return; + if (!window_panes_clip_floating_body(wp, osx, osy, dsx, dsy, &x, &y, + &sx, &sy)) + return; + if (window_pane_index(wp, &pane) != 0) + return; + window_panes_add_area(data, wp, x, y, sx, sy); + + screen_write_cursormove(ctx, x, y, 0); + if (osx <= dsx && osy <= dsy) + screen_write_fast_copy(ctx, &wp->base, 0, wp->base.grid->hsize, + sx, sy); + else + screen_write_preview(ctx, &wp->base, sx, sy); + window_panes_draw_number(data, ctx, wp, pane, x, y, sx, sy); +} + +static void +window_panes_draw_screen(struct window_mode_entry *wme) +{ + struct window_panes_modedata *data = wme->data; + struct window *w; + struct window_pane *wp; + struct screen_write_ctx ctx; + struct layout_cell *root; + struct grid_cell border_gc; + u_int osx, osy, sx, sy; + + if (!window_panes_get_source(data, NULL, NULL, &w)) + return; + root = w->saved_layout_root; + if (root == NULL) + root = w->layout_root; + if (root == NULL) + return; + + osx = root->g.sx; + osy = root->g.sy; + sx = screen_size_x(&data->screen); + sy = screen_size_y(&data->screen); + + window_panes_free_areas(data); + screen_write_start(&ctx, &data->screen); + screen_write_clearscreen(&ctx, 8); + TAILQ_FOREACH(wp, &w->panes, entry) { + if (window_panes_pane_floating(wp)) + continue; + window_panes_draw_pane(data, &ctx, wp, root, osx, osy, sx, sy); + } + window_panes_get_border_cell(data, &border_gc); + window_panes_draw_borders(&ctx, w, root, &border_gc, osx, osy, sx, sy); + TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) { + if (!window_panes_pane_floating(wp)) + continue; + window_panes_clear_floating_area(&ctx, wp, osx, osy, sx, sy); + window_panes_draw_pane(data, &ctx, wp, root, osx, osy, sx, sy); + window_panes_draw_floating_border(&ctx, wp, &border_gc, osx, + osy, sx, sy); + } + screen_write_stop(&ctx); + + data->wp->flags |= PANE_REDRAW; +} + +static void +window_panes_timer_callback(__unused int fd, __unused short events, void *arg) +{ + struct window_mode_entry *wme = arg; + + window_pane_reset_mode(wme->wp); +} + +static struct screen * +window_panes_init(struct window_mode_entry *wme, struct cmdq_item *item, + __unused struct cmd_find_state *fs, struct args *args) +{ + struct window_pane *wp = wme->wp; + struct window *w = wp->window; + struct window_panes_modedata *data; + struct cmd *self; + struct cmd_find_state *source, *target; + struct session *s; + struct timeval tv; + char *cause; + u_int sx = screen_size_x(&wp->base); + u_int sy = screen_size_y(&wp->base); + u_int delay; + + if (item == NULL) + return (NULL); + self = cmdq_get_cmd(item); + if (self == NULL) + return (NULL); + + source = cmdq_get_source(item); + target = cmdq_get_target(item); + s = target->s; + + if (!args_has(args, 'd')) + delay = options_get_number(w->options, "display-panes-time"); + else { + delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause); + if (cause != NULL) { + cmdq_error(item, "delay %s", cause); + free(cause); + return (NULL); + } + } + + wme->data = data = xcalloc(1, sizeof *data); + data->wp = wp; + data->session = s; + + screen_init(&data->screen, sx, sy, 0); + data->screen.mode &= ~MODE_CURSOR; + + data->state = args_make_commands_prepare(self, item, 0, + "select-pane -t \"%%%\"", 0, 0); + if (args_has(args, 's')) { + data->source_session = source->s->id; + data->source_window = source->w->id; + } else { + data->source_session = target->s->id; + data->source_window = target->w->id; + } + + data->delay = delay; + data->ignore_keys = args_has(args, 'N'); + + if (args_has(args, 'Z')) + data->zoomed = -1; + else { + data->zoomed = (w->flags & WINDOW_ZOOMED); + if (!data->zoomed && window_zoom(wp) == 0) + server_redraw_window(w); + } + + evtimer_set(&data->timer, window_panes_timer_callback, wme); + if (data->delay != 0) { + tv.tv_sec = data->delay / 1000; + tv.tv_usec = (data->delay % 1000) * 1000; + evtimer_add(&data->timer, &tv); + } + + window_panes_draw_screen(wme); + return (&data->screen); +} + +static void +window_panes_free(struct window_mode_entry *wme) +{ + struct window_panes_modedata *data = wme->data; + struct window *w = wme->wp->window; + + evtimer_del(&data->timer); + + if (data->zoomed == 0) + server_unzoom_window(w); + server_redraw_window(w); + server_redraw_window_borders(w); + server_status_window(w); + + if (data->state != NULL) + args_make_commands_free(data->state); + window_panes_free_areas(data); + screen_free(&data->screen); + free(data); +} + +static void +window_panes_resize(struct window_mode_entry *wme, u_int sx, u_int sy) +{ + struct window_panes_modedata *data = wme->data; + + screen_resize(&data->screen, sx, sy, 0); + window_panes_draw_screen(wme); +} + +static void +window_panes_run_command(struct window_panes_modedata *data, struct client *c, + struct window_pane *wp) +{ + struct cmdq_item *new_item; + struct cmd_list *cmdlist; + char *expanded, *error; + + xasprintf(&expanded, "%%%u", wp->id); + cmdlist = args_make_commands(data->state, 1, &expanded, &error); + if (cmdlist == NULL) { + cmdq_append(c, cmdq_get_error(error)); + free(error); + } else { + new_item = cmdq_get_command(cmdlist, NULL); + cmdq_append(c, new_item); + } + free(expanded); +} + +static struct window_pane * +window_panes_find_pane(struct window_panes_modedata *data, u_int x, u_int y) +{ + struct window_panes_area *area; + u_int i; + + for (i = data->areas_size; i > 0; i--) { + area = &data->areas[i - 1]; + if (x < area->x || x >= area->x + area->sx) + continue; + if (y < area->y || y >= area->y + area->sy) + continue; + return (window_pane_find_by_id(area->id)); + } + return (NULL); +} + +static struct window_pane * +window_panes_key_pane(struct window_panes_modedata *data, key_code key) +{ + struct window *w; + u_int index; + + if (key >= '0' && key <= '9') + index = key - '0'; + else if ((key & KEYC_MASK_MODIFIERS) == 0) { + key &= KEYC_MASK_KEY; + if (key < 'a' || key > 'z') + return (NULL); + index = 10 + (key - 'a'); + } else + return (NULL); + if (!window_panes_get_source(data, NULL, NULL, &w)) + return (NULL); + return (window_pane_at_index(w, index)); +} + +static struct window_pane * +window_panes_get_target(struct window_mode_entry *wme, key_code key, + struct mouse_event *m) +{ + struct window_panes_modedata *data = wme->data; + u_int x, y; + + if (data->ignore_keys) + return (NULL); + + if (KEYC_IS_MOUSE(key)) { + if (key != KEYC_MOUSEDOWN1_PANE || + m == NULL || + cmd_mouse_at(wme->wp, m, &x, &y, 0) != 0) + return (NULL); + return (window_panes_find_pane(data, x, y)); + } + + return (window_panes_key_pane(data, key)); +} + +static void +window_panes_key(struct window_mode_entry *wme, struct client *c, + __unused struct session *s, __unused struct winlink *wl, key_code key, + struct mouse_event *m) +{ + struct window_pane *wp = wme->wp, *target = NULL; + struct window_panes_modedata *data = wme->data; + + if (key == '\033' || key == 'q') { + window_pane_reset_mode(wp); + return; + } + + target = window_panes_get_target(wme, key, m); + if (target == NULL) { + if (!data->ignore_keys && !KEYC_IS_MOUSE(key)) + window_pane_reset_mode(wp); + return; + } + + if (wp->window->flags & WINDOW_ZOOMED) + window_unzoom(wp->window, 1); + window_panes_run_command(data, c, target); + window_pane_reset_mode(wp); +} diff --git a/window-switch.c b/window-switch.c index d458f30af..6712d8a89 100644 --- a/window-switch.c +++ b/window-switch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-switch.c,v 1.1 2026/06/26 14:40:30 nicm Exp $ */ +/* $OpenBSD: window-switch.c,v 1.2 2026/07/14 17:17:18 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -25,7 +25,8 @@ #include "tmux.h" static struct screen *window_switch_init(struct window_mode_entry *, - struct cmd_find_state *, struct args *); + struct cmdq_item *, struct cmd_find_state *, + struct args *); static void window_switch_free(struct window_mode_entry *); static void window_switch_resize(struct window_mode_entry *, u_int, u_int); @@ -333,7 +334,8 @@ window_switch_draw_screen(struct window_mode_entry *wme) static struct screen * window_switch_init(struct window_mode_entry *wme, - struct cmd_find_state *fs, struct args *args) + __unused struct cmdq_item *item, struct cmd_find_state *fs, + struct args *args) { struct window_pane *wp = wme->wp; struct window_switch_modedata *data; diff --git a/window-tree.c b/window-tree.c index 1b19602fe..9c00e9e8a 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-tree.c,v 1.93 2026/07/04 22:09:06 nicm Exp $ */ +/* $OpenBSD: window-tree.c,v 1.94 2026/07/14 17:17:18 nicm Exp $ */ /* * Copyright (c) 2017 Nicholas Marriott @@ -25,7 +25,8 @@ #include "tmux.h" static struct screen *window_tree_init(struct window_mode_entry *, - struct cmd_find_state *, struct args *); + struct cmdq_item *, struct cmd_find_state *, + struct args *); static void window_tree_free(struct window_mode_entry *); static void window_tree_resize(struct window_mode_entry *, u_int, u_int); @@ -1097,7 +1098,8 @@ window_tree_help(u_int *width, const char **item) } static struct screen * -window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, +window_tree_init(struct window_mode_entry *wme, + __unused struct cmdq_item *item, struct cmd_find_state *fs, struct args *args) { struct window_pane *wp = wme->wp; diff --git a/window.c b/window.c index ca2eca389..7c7a7b788 100644 --- a/window.c +++ b/window.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window.c,v 1.361 2026/07/13 13:01:14 nicm Exp $ */ +/* $OpenBSD: window.c,v 1.362 2026/07/14 17:17:18 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1496,18 +1496,21 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) int window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, - const struct window_mode *mode, struct cmd_find_state *fs, - struct args *args) + const struct window_mode *mode, struct cmdq_item *item, + struct cmd_find_state *fs, struct args *args) { struct window_mode_entry *wme; struct window *w = wp->window; - const char *name = mode->name, *p = NULL; + const char *name = mode->name, *oname = NULL; if (!TAILQ_EMPTY(&wp->modes)) { if (TAILQ_FIRST(&wp->modes)->mode == mode) return (1); - p = TAILQ_FIRST(&wp->modes)->mode->name; + if (TAILQ_FIRST(&wp->modes)->mode->flags & WINDOW_MODE_NO_STACK) + window_pane_reset_mode(wp); } + if (!TAILQ_EMPTY(&wp->modes)) + oname = TAILQ_FIRST(&wp->modes)->mode->name; TAILQ_FOREACH(wme, &wp->modes, entry) { if (wme->mode == mode) @@ -1523,7 +1526,12 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, wme->mode = mode; wme->prefix = 1; TAILQ_INSERT_HEAD(&wp->modes, wme, entry); - wme->screen = wme->mode->init(wme, fs, args); + wme->screen = wme->mode->init(wme, item, fs, args); + if (wme->screen == NULL) { + TAILQ_REMOVE(&wp->modes, wme, entry); + free(wme); + return (1); + } } wme->kill = args != NULL ? args_has(args, 'k') : 0; wp->screen = wme->screen; @@ -1534,8 +1542,8 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, server_redraw_window_borders(wp->window); server_status_window(wp->window); - window_fire_pane_mode_changed("pane-mode-entered", wp, p, name, 1); - window_fire_pane_mode_changed("pane-mode-changed", wp, p, name, 1); + window_fire_pane_mode_changed("pane-mode-entered", wp, oname, name, 1); + window_fire_pane_mode_changed("pane-mode-changed", wp, oname, name, 1); return (0); } @@ -2684,8 +2692,15 @@ window_get_pane_status(struct window *w) int window_pane_get_pane_status(struct window_pane *wp) { + struct window_mode_entry *wme; int status; + wme = TAILQ_FIRST(&wp->modes); + if (wme != NULL && + (wme->mode->flags & WINDOW_MODE_HIDE_PANE_STATUS) && + (wp->flags & PANE_ZOOMED)) + return (PANE_STATUS_OFF); + if (!window_pane_is_floating(wp)) return (window_get_pane_status(wp->window)); if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE)