Files
tmux/window-panes.c
nicm 1a02c9957c Convert display-panes away from an overlay and into a mode. This is
another step on the road of getting rid of overlays altogether (they are
full of special cases; popups and menus are to go also eventually). We
lose some of the styling of borders but gain the ability to run
display-panes in another pane (perhaps not hugely useful but one never
knows). The -b flag goes away as no longer useful.
2026-07-14 19:57:30 +00:00

1068 lines
26 KiB
C

/* $OpenBSD: window-panes.c,v 1.1 2026/07/14 17:17:18 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* 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 <sys/types.h>
#include <stdlib.h>
#include <string.h>
#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);
}