1/* $OpenBSD: cmd-display-panes.c,v 1.46 2024/03/21 11:30:42 nicm Exp $ */
2
3/*
4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20
21#include <stdlib.h>
22#include <string.h>
23
24#include "tmux.h"
25
26/*
27 * Display panes on a client.
28 */
29
30static enum args_parse_type	cmd_display_panes_args_parse(struct args *,
31				    u_int, char **);
32static enum cmd_retval		cmd_display_panes_exec(struct cmd *,
33				    struct cmdq_item *);
34
35const struct cmd_entry cmd_display_panes_entry = {
36	.name = "display-panes",
37	.alias = "displayp",
38
39	.args = { "bd:Nt:", 0, 1, cmd_display_panes_args_parse },
40	.usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
41
42	.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
43	.exec = cmd_display_panes_exec
44};
45
46struct cmd_display_panes_data {
47	struct cmdq_item		*item;
48	struct args_command_state	*state;
49};
50
51static enum args_parse_type
52cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
53    __unused char **cause)
54{
55	return (ARGS_PARSE_COMMANDS_OR_STRING);
56}
57
58static void
59cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
60    struct window_pane *wp)
61{
62	struct client		*c = ctx->c;
63	struct tty		*tty = &c->tty;
64	struct session		*s = c->session;
65	struct options		*oo = s->options;
66	struct window		*w = wp->window;
67	struct grid_cell	 fgc, bgc;
68	u_int			 pane, idx, px, py, i, j, xoff, yoff, sx, sy;
69	int			 colour, active_colour;
70	char			 buf[16], lbuf[16], rbuf[16], *ptr;
71	size_t			 len, llen, rlen;
72
73	if (wp->xoff + wp->sx <= ctx->ox ||
74	    wp->xoff >= ctx->ox + ctx->sx ||
75	    wp->yoff + wp->sy <= ctx->oy ||
76	    wp->yoff >= ctx->oy + ctx->sy)
77		return;
78
79	if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
80		/* All visible. */
81		xoff = wp->xoff - ctx->ox;
82		sx = wp->sx;
83	} else if (wp->xoff < ctx->ox &&
84	    wp->xoff + wp->sx > ctx->ox + ctx->sx) {
85		/* Both left and right not visible. */
86		xoff = 0;
87		sx = ctx->sx;
88	} else if (wp->xoff < ctx->ox) {
89		/* Left not visible. */
90		xoff = 0;
91		sx = wp->sx - (ctx->ox - wp->xoff);
92	} else {
93		/* Right not visible. */
94		xoff = wp->xoff - ctx->ox;
95		sx = wp->sx - xoff;
96	}
97	if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) {
98		/* All visible. */
99		yoff = wp->yoff - ctx->oy;
100		sy = wp->sy;
101	} else if (wp->yoff < ctx->oy &&
102	    wp->yoff + wp->sy > ctx->oy + ctx->sy) {
103		/* Both top and bottom not visible. */
104		yoff = 0;
105		sy = ctx->sy;
106	} else if (wp->yoff < ctx->oy) {
107		/* Top not visible. */
108		yoff = 0;
109		sy = wp->sy - (ctx->oy - wp->yoff);
110	} else {
111		/* Bottom not visible. */
112		yoff = wp->yoff - ctx->oy;
113		sy = wp->sy - yoff;
114	}
115
116	if (ctx->statustop)
117		yoff += ctx->statuslines;
118	px = sx / 2;
119	py = sy / 2;
120
121	if (window_pane_index(wp, &pane) != 0)
122		fatalx("index not found");
123	len = xsnprintf(buf, sizeof buf, "%u", pane);
124
125	if (sx < len)
126		return;
127	colour = options_get_number(oo, "display-panes-colour");
128	active_colour = options_get_number(oo, "display-panes-active-colour");
129
130	memcpy(&fgc, &grid_default_cell, sizeof fgc);
131	memcpy(&bgc, &grid_default_cell, sizeof bgc);
132	if (w->active == wp) {
133		fgc.fg = active_colour;
134		bgc.bg = active_colour;
135	} else {
136		fgc.fg = colour;
137		bgc.bg = colour;
138	}
139
140	rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy);
141	if (pane > 9 && pane < 35)
142		llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
143	else
144		llen = 0;
145
146	if (sx < len * 6 || sy < 5) {
147		tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
148		if (sx >= len + llen + 1) {
149			len += llen + 1;
150			tty_cursor(tty, xoff + px - len / 2, yoff + py);
151			tty_putn(tty, buf, len,	 len);
152			tty_putn(tty, " ", 1, 1);
153			tty_putn(tty, lbuf, llen, llen);
154		} else {
155			tty_cursor(tty, xoff + px - len / 2, yoff + py);
156			tty_putn(tty, buf, len, len);
157		}
158		goto out;
159	}
160
161	px -= len * 3;
162	py -= 2;
163
164	tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
165	for (ptr = buf; *ptr != '\0'; ptr++) {
166		if (*ptr < '0' || *ptr > '9')
167			continue;
168		idx = *ptr - '0';
169
170		for (j = 0; j < 5; j++) {
171			for (i = px; i < px + 5; i++) {
172				tty_cursor(tty, xoff + i, yoff + py + j);
173				if (window_clock_table[idx][j][i - px])
174					tty_putc(tty, ' ');
175			}
176		}
177		px += 6;
178	}
179
180	if (sy <= 6)
181		goto out;
182	tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
183	if (rlen != 0 && sx >= rlen) {
184		tty_cursor(tty, xoff + sx - rlen, yoff);
185		tty_putn(tty, rbuf, rlen, rlen);
186	}
187	if (llen != 0) {
188		tty_cursor(tty, xoff + sx / 2 + len * 3 - llen - 1,
189		    yoff + py + 5);
190		tty_putn(tty, lbuf, llen, llen);
191	}
192
193out:
194	tty_cursor(tty, 0, 0);
195}
196
197static void
198cmd_display_panes_draw(struct client *c, __unused void *data,
199    struct screen_redraw_ctx *ctx)
200{
201	struct window		*w = c->session->curw->window;
202	struct window_pane	*wp;
203
204	log_debug("%s: %s @%u", __func__, c->name, w->id);
205
206	TAILQ_FOREACH(wp, &w->panes, entry) {
207		if (window_pane_visible(wp))
208			cmd_display_panes_draw_pane(ctx, wp);
209	}
210}
211
212static void
213cmd_display_panes_free(__unused struct client *c, void *data)
214{
215	struct cmd_display_panes_data	*cdata = data;
216
217	if (cdata->item != NULL)
218		cmdq_continue(cdata->item);
219	args_make_commands_free(cdata->state);
220	free(cdata);
221}
222
223static int
224cmd_display_panes_key(struct client *c, void *data, struct key_event *event)
225{
226	struct cmd_display_panes_data	*cdata = data;
227	char				*expanded, *error;
228	struct cmdq_item		*item = cdata->item, *new_item;
229	struct cmd_list			*cmdlist;
230	struct window			*w = c->session->curw->window;
231	struct window_pane		*wp;
232	u_int				 index;
233	key_code			 key;
234
235	if (event->key >= '0' && event->key <= '9')
236		index = event->key - '0';
237	else if ((event->key & KEYC_MASK_MODIFIERS) == 0) {
238		key = (event->key & KEYC_MASK_KEY);
239		if (key >= 'a' && key <= 'z')
240			index = 10 + (key - 'a');
241		else
242			return (-1);
243	} else
244		return (-1);
245
246	wp = window_pane_at_index(w, index);
247	if (wp == NULL)
248		return (1);
249	window_unzoom(w, 1);
250
251	xasprintf(&expanded, "%%%u", wp->id);
252
253	cmdlist = args_make_commands(cdata->state, 1, &expanded, &error);
254	if (cmdlist == NULL) {
255		cmdq_append(c, cmdq_get_error(error));
256		free(error);
257	} else if (item == NULL) {
258		new_item = cmdq_get_command(cmdlist, NULL);
259		cmdq_append(c, new_item);
260	} else {
261		new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
262		cmdq_insert_after(item, new_item);
263	}
264
265	free(expanded);
266	return (1);
267}
268
269static enum cmd_retval
270cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
271{
272	struct args			*args = cmd_get_args(self);
273	struct client			*tc = cmdq_get_target_client(item);
274	struct session			*s = tc->session;
275	u_int				 delay;
276	char				*cause;
277	struct cmd_display_panes_data	*cdata;
278	int				 wait = !args_has(args, 'b');
279
280	if (tc->overlay_draw != NULL)
281		return (CMD_RETURN_NORMAL);
282
283	if (args_has(args, 'd')) {
284		delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
285		if (cause != NULL) {
286			cmdq_error(item, "delay %s", cause);
287			free(cause);
288			return (CMD_RETURN_ERROR);
289		}
290	} else
291		delay = options_get_number(s->options, "display-panes-time");
292
293	cdata = xcalloc(1, sizeof *cdata);
294	if (wait)
295		cdata->item = item;
296	cdata->state = args_make_commands_prepare(self, item, 0,
297	    "select-pane -t \"%%%\"", wait, 0);
298
299	if (args_has(args, 'N')) {
300		server_client_set_overlay(tc, delay, NULL, NULL,
301		    cmd_display_panes_draw, NULL, cmd_display_panes_free, NULL,
302		    cdata);
303	} else {
304		server_client_set_overlay(tc, delay, NULL, NULL,
305		    cmd_display_panes_draw, cmd_display_panes_key,
306		    cmd_display_panes_free, NULL, cdata);
307	}
308
309	if (!wait)
310		return (CMD_RETURN_NORMAL);
311	return (CMD_RETURN_WAIT);
312}
313