xref: /openbsd/usr.bin/tmux/cmd-display-panes.c (revision 0ad0daf4)
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 
30 static enum args_parse_type	cmd_display_panes_args_parse(struct args *,
31 				    u_int, char **);
32 static enum cmd_retval		cmd_display_panes_exec(struct cmd *,
33 				    struct cmdq_item *);
34 
35 const 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 
46 struct cmd_display_panes_data {
47 	struct cmdq_item		*item;
48 	struct args_command_state	*state;
49 };
50 
51 static enum args_parse_type
cmd_display_panes_args_parse(__unused struct args * args,__unused u_int idx,__unused char ** cause)52 cmd_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 
58 static void
cmd_display_panes_draw_pane(struct screen_redraw_ctx * ctx,struct window_pane * wp)59 cmd_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 
193 out:
194 	tty_cursor(tty, 0, 0);
195 }
196 
197 static void
cmd_display_panes_draw(struct client * c,__unused void * data,struct screen_redraw_ctx * ctx)198 cmd_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 
212 static void
cmd_display_panes_free(__unused struct client * c,void * data)213 cmd_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 
223 static int
cmd_display_panes_key(struct client * c,void * data,struct key_event * event)224 cmd_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 
269 static enum cmd_retval
cmd_display_panes_exec(struct cmd * self,struct cmdq_item * item)270 cmd_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