/* $OpenBSD: window-panes.c,v 1.2 2026/07/17 16:03:15 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 screen *preview; 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_set_preview(struct window_panes_modedata *data) { struct window_pane *wp = data->wp; struct screen *src = &wp->base, *dst; struct screen_write_ctx ctx; u_int sx = screen_size_x(src), sy = screen_size_y(src); data->preview = dst = xmalloc(sizeof *data->preview); screen_init(dst, sx, sy, 0); screen_write_start(&ctx, dst); screen_write_fast_copy(&ctx, src, 0, src->grid->hsize, sx, sy); screen_write_stop(&ctx); dst->mode = src->mode; dst->cx = src->cx; dst->cy = src->cy; } 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_borders(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_pane(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_borders(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_borders(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_borders(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) { struct screen *s = &wp->base; 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_pane(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 (data->preview != NULL && wp == data->wp && sx <= screen_size_x(data->preview) && sy <= screen_size_y(data->preview)) s = data->preview; if (osx <= dsx && osy <= dsy) screen_write_fast_copy(ctx, s, 0, s->grid->hsize, sx, sy); else screen_write_preview(ctx, s, 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_panes_set_preview(data); 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) { wme->wp->flags |= PANE_NORESIZETRIM; 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); if (data->preview != NULL) { screen_free(data->preview); free(data->preview); } 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) { if (data->zoomed == 0) wp->flags |= PANE_NORESIZETRIM; window_unzoom(wp->window, 1); } window_panes_run_command(data, c, target); window_pane_reset_mode(wp); }