xref: /netbsd-src/external/bsd/tmux/dist/cmd-display-menu.c (revision 890b6d91a44b7fcb2dfbcbc1e93463086e462d2d)
16483eba0Schristos /* $OpenBSD$ */
26483eba0Schristos 
36483eba0Schristos /*
46483eba0Schristos  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
56483eba0Schristos  *
66483eba0Schristos  * Permission to use, copy, modify, and distribute this software for any
76483eba0Schristos  * purpose with or without fee is hereby granted, provided that the above
86483eba0Schristos  * copyright notice and this permission notice appear in all copies.
96483eba0Schristos  *
106483eba0Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116483eba0Schristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126483eba0Schristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136483eba0Schristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146483eba0Schristos  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
156483eba0Schristos  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
166483eba0Schristos  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176483eba0Schristos  */
186483eba0Schristos 
196483eba0Schristos #include <sys/types.h>
206483eba0Schristos 
216483eba0Schristos #include <stdlib.h>
226483eba0Schristos #include <string.h>
236483eba0Schristos 
246483eba0Schristos #include "tmux.h"
256483eba0Schristos 
266483eba0Schristos /*
276483eba0Schristos  * Display a menu on a client.
286483eba0Schristos  */
296483eba0Schristos 
3046548964Swiz static enum args_parse_type	cmd_display_menu_args_parse(struct args *,
3146548964Swiz 				    u_int, char **);
326483eba0Schristos static enum cmd_retval		cmd_display_menu_exec(struct cmd *,
336483eba0Schristos 				    struct cmdq_item *);
349fb66d81Schristos static enum cmd_retval		cmd_display_popup_exec(struct cmd *,
359fb66d81Schristos 				    struct cmdq_item *);
366483eba0Schristos 
376483eba0Schristos const struct cmd_entry cmd_display_menu_entry = {
386483eba0Schristos 	.name = "display-menu",
396483eba0Schristos 	.alias = "menu",
406483eba0Schristos 
41*890b6d91Swiz 	.args = { "b:c:C:H:s:S:MOt:T:x:y:", 1, -1, cmd_display_menu_args_parse },
42*890b6d91Swiz 	.usage = "[-MO] [-b border-lines] [-c target-client] "
43f844e94eSwiz 		 "[-C starting-choice] [-H selected-style] [-s style] "
44f844e94eSwiz 		 "[-S border-style] " CMD_TARGET_PANE_USAGE "[-T title] "
456483eba0Schristos 		 "[-x position] [-y position] name key command ...",
466483eba0Schristos 
476483eba0Schristos 	.target = { 't', CMD_FIND_PANE, 0 },
486483eba0Schristos 
499fb66d81Schristos 	.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG,
506483eba0Schristos 	.exec = cmd_display_menu_exec
516483eba0Schristos };
526483eba0Schristos 
539fb66d81Schristos const struct cmd_entry cmd_display_popup_entry = {
549fb66d81Schristos 	.name = "display-popup",
559fb66d81Schristos 	.alias = "popup",
569fb66d81Schristos 
5746548964Swiz 	.args = { "Bb:Cc:d:e:Eh:s:S:t:T:w:x:y:", 0, -1, NULL },
5846548964Swiz 	.usage = "[-BCE] [-b border-lines] [-c target-client] "
5946548964Swiz 		 "[-d start-directory] [-e environment] [-h height] "
6046548964Swiz 		 "[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE
6146548964Swiz 		 "[-T title] [-w width] [-x position] [-y position] "
6246548964Swiz 		 "[shell-command]",
639fb66d81Schristos 
649fb66d81Schristos 	.target = { 't', CMD_FIND_PANE, 0 },
659fb66d81Schristos 
669fb66d81Schristos 	.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG,
679fb66d81Schristos 	.exec = cmd_display_popup_exec
689fb66d81Schristos };
699fb66d81Schristos 
7046548964Swiz static enum args_parse_type
7146548964Swiz cmd_display_menu_args_parse(struct args *args, u_int idx, __unused char **cause)
7246548964Swiz {
7346548964Swiz 	u_int			 i = 0;
7446548964Swiz 	enum args_parse_type	 type = ARGS_PARSE_STRING;
7546548964Swiz 
7646548964Swiz 	for (;;) {
7746548964Swiz 		type = ARGS_PARSE_STRING;
7846548964Swiz 		if (i == idx)
7946548964Swiz 			break;
8046548964Swiz 		if (*args_string(args, i++) == '\0')
8146548964Swiz 			continue;
8246548964Swiz 
8346548964Swiz 		type = ARGS_PARSE_STRING;
8446548964Swiz 		if (i++ == idx)
8546548964Swiz 			break;
8646548964Swiz 
8746548964Swiz 		type = ARGS_PARSE_COMMANDS_OR_STRING;
8846548964Swiz 		if (i++ == idx)
8946548964Swiz 			break;
9046548964Swiz 	}
9146548964Swiz 	return (type);
9246548964Swiz }
9346548964Swiz 
949fb66d81Schristos static int
959fb66d81Schristos cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
969fb66d81Schristos     struct args *args, u_int *px, u_int *py, u_int w, u_int h)
979fb66d81Schristos {
989fb66d81Schristos 	struct tty		*tty = &tc->tty;
999fb66d81Schristos 	struct cmd_find_state	*target = cmdq_get_target(item);
1009fb66d81Schristos 	struct key_event	*event = cmdq_get_event(item);
1019fb66d81Schristos 	struct session		*s = tc->session;
1029fb66d81Schristos 	struct winlink		*wl = target->wl;
1039fb66d81Schristos 	struct window_pane	*wp = target->wp;
1049fb66d81Schristos 	struct style_ranges	*ranges = NULL;
1059fb66d81Schristos 	struct style_range	*sr = NULL;
1069fb66d81Schristos 	const char		*xp, *yp;
1079fb66d81Schristos 	char			*p;
1089fb66d81Schristos 	int			 top;
1099fb66d81Schristos 	u_int			 line, ox, oy, sx, sy, lines, position;
1109fb66d81Schristos 	long			 n;
1119fb66d81Schristos 	struct format_tree	*ft;
1129fb66d81Schristos 
1139fb66d81Schristos 	/*
1149fb66d81Schristos 	 * Work out the position from the -x and -y arguments. This is the
1159fb66d81Schristos 	 * bottom-left position.
1169fb66d81Schristos 	 */
1179fb66d81Schristos 
1189fb66d81Schristos 	/* If the popup is too big, stop now. */
1199fb66d81Schristos 	if (w > tty->sx || h > tty->sy)
1209fb66d81Schristos 		return (0);
1219fb66d81Schristos 
1229fb66d81Schristos 	/* Create format with mouse position if any. */
1239fb66d81Schristos 	ft = format_create_from_target(item);
1249fb66d81Schristos 	if (event->m.valid) {
1259fb66d81Schristos 		format_add(ft, "popup_mouse_x", "%u", event->m.x);
1269fb66d81Schristos 		format_add(ft, "popup_mouse_y", "%u", event->m.y);
1279fb66d81Schristos 	}
1289fb66d81Schristos 
1299fb66d81Schristos 	/*
1309fb66d81Schristos 	 * If there are any status lines, add this window position and the
1319fb66d81Schristos 	 * status line position.
1329fb66d81Schristos 	 */
1339fb66d81Schristos 	top = status_at_line(tc);
1349fb66d81Schristos 	if (top != -1) {
1359fb66d81Schristos 		lines = status_line_size(tc);
1369fb66d81Schristos 		if (top == 0)
1379fb66d81Schristos 			top = lines;
1389fb66d81Schristos 		else
1399fb66d81Schristos 			top = 0;
1409fb66d81Schristos 		position = options_get_number(s->options, "status-position");
1419fb66d81Schristos 
1429fb66d81Schristos 		for (line = 0; line < lines; line++) {
1439fb66d81Schristos 			ranges = &tc->status.entries[line].ranges;
1449fb66d81Schristos 			TAILQ_FOREACH(sr, ranges, entry) {
1459fb66d81Schristos 				if (sr->type != STYLE_RANGE_WINDOW)
1469fb66d81Schristos 					continue;
1479fb66d81Schristos 				if (sr->argument == (u_int)wl->idx)
1489fb66d81Schristos 					break;
1499fb66d81Schristos 			}
1509fb66d81Schristos 			if (sr != NULL)
1519fb66d81Schristos 				break;
1529fb66d81Schristos 		}
1539fb66d81Schristos 
1549fb66d81Schristos 		if (sr != NULL) {
1559fb66d81Schristos 			format_add(ft, "popup_window_status_line_x", "%u",
1569fb66d81Schristos 			    sr->start);
1579fb66d81Schristos 			if (position == 0) {
1589fb66d81Schristos 				format_add(ft, "popup_window_status_line_y",
1599fb66d81Schristos 				    "%u", line + 1 + h);
1609fb66d81Schristos 			} else {
1619fb66d81Schristos 				format_add(ft, "popup_window_status_line_y",
1629fb66d81Schristos 				    "%u", tty->sy - lines + line);
1639fb66d81Schristos 			}
1649fb66d81Schristos 		}
1659fb66d81Schristos 
1669fb66d81Schristos 		if (position == 0)
1679fb66d81Schristos 			format_add(ft, "popup_status_line_y", "%u", lines + h);
1689fb66d81Schristos 		else {
1699fb66d81Schristos 			format_add(ft, "popup_status_line_y", "%u",
1709fb66d81Schristos 			    tty->sy - lines);
1719fb66d81Schristos 		}
1729fb66d81Schristos 	} else
1739fb66d81Schristos 		top = 0;
1749fb66d81Schristos 
1759fb66d81Schristos 	/* Popup width and height. */
1769fb66d81Schristos 	format_add(ft, "popup_width", "%u", w);
1779fb66d81Schristos 	format_add(ft, "popup_height", "%u", h);
1789fb66d81Schristos 
1799fb66d81Schristos 	/* Position so popup is in the centre. */
1809fb66d81Schristos 	n = (long)(tty->sx - 1) / 2 - w / 2;
1819fb66d81Schristos 	if (n < 0)
1829fb66d81Schristos 		format_add(ft, "popup_centre_x", "%u", 0);
1839fb66d81Schristos 	else
1849fb66d81Schristos 		format_add(ft, "popup_centre_x", "%ld", n);
1859fb66d81Schristos 	n = (tty->sy - 1) / 2 + h / 2;
186c745c111Schristos 	if ((unsigned long)n >= tty->sy)
1879fb66d81Schristos 		format_add(ft, "popup_centre_y", "%u", tty->sy - h);
1889fb66d81Schristos 	else
1899fb66d81Schristos 		format_add(ft, "popup_centre_y", "%ld", n);
1909fb66d81Schristos 
1919fb66d81Schristos 	/* Position of popup relative to mouse. */
1929fb66d81Schristos 	if (event->m.valid) {
1939fb66d81Schristos 		n = (long)event->m.x - w / 2;
1949fb66d81Schristos 		if (n < 0)
1959fb66d81Schristos 			format_add(ft, "popup_mouse_centre_x", "%u", 0);
1969fb66d81Schristos 		else
1979fb66d81Schristos 			format_add(ft, "popup_mouse_centre_x", "%ld", n);
1989fb66d81Schristos 		n = event->m.y - h / 2;
1999fb66d81Schristos 		if (n + h >= tty->sy) {
2009fb66d81Schristos 			format_add(ft, "popup_mouse_centre_y", "%u",
2019fb66d81Schristos 			    tty->sy - h);
2029fb66d81Schristos 		} else
2039fb66d81Schristos 			format_add(ft, "popup_mouse_centre_y", "%ld", n);
2049fb66d81Schristos 		n = (long)event->m.y + h;
2053afd71f9Skre 		if (n >= (long)tty->sy)
20646548964Swiz 			format_add(ft, "popup_mouse_top", "%u", tty->sy - 1);
2079fb66d81Schristos 		else
2089fb66d81Schristos 			format_add(ft, "popup_mouse_top", "%ld", n);
2099fb66d81Schristos 		n = event->m.y - h;
2109fb66d81Schristos 		if (n < 0)
2119fb66d81Schristos 			format_add(ft, "popup_mouse_bottom", "%u", 0);
2129fb66d81Schristos 		else
2139fb66d81Schristos 			format_add(ft, "popup_mouse_bottom", "%ld", n);
2149fb66d81Schristos 	}
2159fb66d81Schristos 
2169fb66d81Schristos 	/* Position in pane. */
2179fb66d81Schristos 	tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
2189fb66d81Schristos 	n = top + wp->yoff - oy + h;
219c745c111Schristos 	if ((unsigned long)n >= tty->sy)
2209fb66d81Schristos 		format_add(ft, "popup_pane_top", "%u", tty->sy - h);
2219fb66d81Schristos 	else
2229fb66d81Schristos 		format_add(ft, "popup_pane_top", "%ld", n);
2239fb66d81Schristos 	format_add(ft, "popup_pane_bottom", "%u", top + wp->yoff + wp->sy - oy);
2249fb66d81Schristos 	format_add(ft, "popup_pane_left", "%u", wp->xoff - ox);
2259fb66d81Schristos 	n = (long)wp->xoff + wp->sx - ox - w;
2269fb66d81Schristos 	if (n < 0)
2279fb66d81Schristos 		format_add(ft, "popup_pane_right", "%u", 0);
2289fb66d81Schristos 	else
2299fb66d81Schristos 		format_add(ft, "popup_pane_right", "%ld", n);
2309fb66d81Schristos 
2319fb66d81Schristos 	/* Expand horizontal position. */
2329fb66d81Schristos 	xp = args_get(args, 'x');
2339fb66d81Schristos 	if (xp == NULL || strcmp(xp, "C") == 0)
2349fb66d81Schristos 		xp = "#{popup_centre_x}";
2359fb66d81Schristos 	else if (strcmp(xp, "R") == 0)
23659b94b2cSchristos 		xp = "#{popup_pane_right}";
2379fb66d81Schristos 	else if (strcmp(xp, "P") == 0)
2389fb66d81Schristos 		xp = "#{popup_pane_left}";
2399fb66d81Schristos 	else if (strcmp(xp, "M") == 0)
2409fb66d81Schristos 		xp = "#{popup_mouse_centre_x}";
2419fb66d81Schristos 	else if (strcmp(xp, "W") == 0)
2429fb66d81Schristos 		xp = "#{popup_window_status_line_x}";
2439fb66d81Schristos 	p = format_expand(ft, xp);
2449fb66d81Schristos 	n = strtol(p, NULL, 10);
2459fb66d81Schristos 	if (n + w >= tty->sx)
2469fb66d81Schristos 		n = tty->sx - w;
2479fb66d81Schristos 	else if (n < 0)
2489fb66d81Schristos 		n = 0;
2499fb66d81Schristos 	*px = n;
25046548964Swiz 	log_debug("%s: -x: %s = %s = %u (-w %u)", __func__, xp, p, *px, w);
2519fb66d81Schristos 	free(p);
2529fb66d81Schristos 
2539fb66d81Schristos 	/* Expand vertical position  */
2549fb66d81Schristos 	yp = args_get(args, 'y');
2559fb66d81Schristos 	if (yp == NULL || strcmp(yp, "C") == 0)
2569fb66d81Schristos 		yp = "#{popup_centre_y}";
2579fb66d81Schristos 	else if (strcmp(yp, "P") == 0)
2589fb66d81Schristos 		yp = "#{popup_pane_bottom}";
2599fb66d81Schristos 	else if (strcmp(yp, "M") == 0)
2609fb66d81Schristos 		yp = "#{popup_mouse_top}";
2619fb66d81Schristos 	else if (strcmp(yp, "S") == 0)
2629fb66d81Schristos 		yp = "#{popup_status_line_y}";
2639fb66d81Schristos 	else if (strcmp(yp, "W") == 0)
2649fb66d81Schristos 		yp = "#{popup_window_status_line_y}";
2659fb66d81Schristos 	p = format_expand(ft, yp);
2669fb66d81Schristos 	n = strtol(p, NULL, 10);
267c745c111Schristos 	if ((unsigned long)n < h)
2689fb66d81Schristos 		n = 0;
2699fb66d81Schristos 	else
2709fb66d81Schristos 		n -= h;
2719fb66d81Schristos 	if (n + h >= tty->sy)
2729fb66d81Schristos 		n = tty->sy - h;
2739fb66d81Schristos 	else if (n < 0)
2749fb66d81Schristos 		n = 0;
2759fb66d81Schristos 	*py = n;
27646548964Swiz 	log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
2779fb66d81Schristos 	free(p);
2789fb66d81Schristos 
279f844e94eSwiz 	format_free(ft);
2809fb66d81Schristos 	return (1);
2819fb66d81Schristos }
2829fb66d81Schristos 
2836483eba0Schristos static enum cmd_retval
2846483eba0Schristos cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
2856483eba0Schristos {
2869fb66d81Schristos 	struct args		*args = cmd_get_args(self);
2879fb66d81Schristos 	struct cmd_find_state	*target = cmdq_get_target(item);
2889fb66d81Schristos 	struct key_event	*event = cmdq_get_event(item);
2899fb66d81Schristos 	struct client		*tc = cmdq_get_target_client(item);
2906483eba0Schristos 	struct menu		*menu = NULL;
2916483eba0Schristos 	struct menu_item	 menu_item;
292f844e94eSwiz 	const char		*key, *name, *value;
293f844e94eSwiz 	const char		*style = args_get(args, 's');
294f844e94eSwiz 	const char		*border_style = args_get(args, 'S');
295f844e94eSwiz 	const char		*selected_style = args_get(args, 'H');
296f844e94eSwiz 	enum box_lines		 lines = BOX_LINES_DEFAULT;
297f844e94eSwiz 	char			*title, *cause;
298f844e94eSwiz 	int			 flags = 0, starting_choice = 0;
29946548964Swiz 	u_int			 px, py, i, count = args_count(args);
300f844e94eSwiz 	struct options		*o = target->s->curw->window->options;
301f844e94eSwiz 	struct options_entry	*oe;
302f844e94eSwiz 
3036483eba0Schristos 
3049fb66d81Schristos 	if (tc->overlay_draw != NULL)
3056483eba0Schristos 		return (CMD_RETURN_NORMAL);
3066483eba0Schristos 
307f844e94eSwiz 	if (args_has(args, 'C')) {
308f844e94eSwiz 		if (strcmp(args_get(args, 'C'), "-") == 0)
309f844e94eSwiz 			starting_choice = -1;
310f844e94eSwiz 		else {
311f844e94eSwiz 			starting_choice = args_strtonum(args, 'C', 0, UINT_MAX,
312f844e94eSwiz 			    &cause);
313f844e94eSwiz 			if (cause != NULL) {
314f844e94eSwiz 				cmdq_error(item, "starting choice %s", cause);
315f844e94eSwiz 				free(cause);
316f844e94eSwiz 				return (CMD_RETURN_ERROR);
317f844e94eSwiz 			}
318f844e94eSwiz 		}
319f844e94eSwiz 	}
320f844e94eSwiz 
3216483eba0Schristos 	if (args_has(args, 'T'))
3229fb66d81Schristos 		title = format_single_from_target(item, args_get(args, 'T'));
3236483eba0Schristos 	else
3246483eba0Schristos 		title = xstrdup("");
3256483eba0Schristos 	menu = menu_create(title);
326f844e94eSwiz 	free(title);
3276483eba0Schristos 
32846548964Swiz 	for (i = 0; i != count; /* nothing */) {
32946548964Swiz 		name = args_string(args, i++);
3306483eba0Schristos 		if (*name == '\0') {
3319fb66d81Schristos 			menu_add_item(menu, NULL, item, tc, target);
3326483eba0Schristos 			continue;
3336483eba0Schristos 		}
3346483eba0Schristos 
33546548964Swiz 		if (count - i < 2) {
3366483eba0Schristos 			cmdq_error(item, "not enough arguments");
3376483eba0Schristos 			menu_free(menu);
3386483eba0Schristos 			return (CMD_RETURN_ERROR);
3396483eba0Schristos 		}
34046548964Swiz 		key = args_string(args, i++);
3416483eba0Schristos 
3426483eba0Schristos 		menu_item.name = name;
3436483eba0Schristos 		menu_item.key = key_string_lookup_string(key);
34446548964Swiz 		menu_item.command = args_string(args, i++);
3456483eba0Schristos 
3469fb66d81Schristos 		menu_add_item(menu, &menu_item, item, tc, target);
3476483eba0Schristos 	}
3486483eba0Schristos 	if (menu == NULL) {
3496483eba0Schristos 		cmdq_error(item, "invalid menu arguments");
3506483eba0Schristos 		return (CMD_RETURN_ERROR);
3516483eba0Schristos 	}
3526483eba0Schristos 	if (menu->count == 0) {
3536483eba0Schristos 		menu_free(menu);
3546483eba0Schristos 		return (CMD_RETURN_NORMAL);
3556483eba0Schristos 	}
3569fb66d81Schristos 	if (!cmd_display_menu_get_position(tc, item, args, &px, &py,
3579fb66d81Schristos 	    menu->width + 4, menu->count + 2)) {
3589fb66d81Schristos 		menu_free(menu);
3599fb66d81Schristos 		return (CMD_RETURN_NORMAL);
3606483eba0Schristos 	}
3616483eba0Schristos 
362f844e94eSwiz 	value = args_get(args, 'b');
363f844e94eSwiz 	if (value != NULL) {
364f844e94eSwiz 		oe = options_get(o, "menu-border-lines");
365f844e94eSwiz 		lines = options_find_choice(options_table_entry(oe), value,
366f844e94eSwiz 		    &cause);
367f844e94eSwiz 		if (lines == -1) {
368f844e94eSwiz 			cmdq_error(item, "menu-border-lines %s", cause);
369f844e94eSwiz 			free(cause);
370f844e94eSwiz 			return (CMD_RETURN_ERROR);
371f844e94eSwiz 		}
372f844e94eSwiz 	}
373f844e94eSwiz 
3749fb66d81Schristos 	if (args_has(args, 'O'))
3759fb66d81Schristos 		flags |= MENU_STAYOPEN;
376*890b6d91Swiz 	if (!event->m.valid && !args_has(args, 'M'))
3776483eba0Schristos 		flags |= MENU_NOMOUSE;
378f844e94eSwiz 	if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
379f844e94eSwiz 	    style, selected_style, border_style, target, NULL, NULL) != 0)
3809fb66d81Schristos 		return (CMD_RETURN_NORMAL);
3819fb66d81Schristos 	return (CMD_RETURN_WAIT);
3829fb66d81Schristos }
3839fb66d81Schristos 
3849fb66d81Schristos static enum cmd_retval
3859fb66d81Schristos cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
3869fb66d81Schristos {
3879fb66d81Schristos 	struct args		*args = cmd_get_args(self);
3889fb66d81Schristos 	struct cmd_find_state	*target = cmdq_get_target(item);
3899fb66d81Schristos 	struct session		*s = target->s;
3909fb66d81Schristos 	struct client		*tc = cmdq_get_target_client(item);
3919fb66d81Schristos 	struct tty		*tty = &tc->tty;
39246548964Swiz 	const char		*value, *shell, *shellcmd = NULL;
39346548964Swiz 	const char		*style = args_get(args, 's');
39446548964Swiz 	const char		*border_style = args_get(args, 'S');
39546548964Swiz 	char			*cwd, *cause = NULL, **argv = NULL, *title;
39646548964Swiz 	int			 flags = 0, argc = 0;
39746548964Swiz 	enum box_lines		 lines = BOX_LINES_DEFAULT;
39846548964Swiz 	u_int			 px, py, w, h, count = args_count(args);
39946548964Swiz 	struct args_value	*av;
40046548964Swiz 	struct environ		*env = NULL;
40146548964Swiz 	struct options		*o = s->curw->window->options;
40246548964Swiz 	struct options_entry	*oe;
4039fb66d81Schristos 
4049fb66d81Schristos 	if (args_has(args, 'C')) {
4059fb66d81Schristos 		server_client_clear_overlay(tc);
4069fb66d81Schristos 		return (CMD_RETURN_NORMAL);
4079fb66d81Schristos 	}
4089fb66d81Schristos 	if (tc->overlay_draw != NULL)
4099fb66d81Schristos 		return (CMD_RETURN_NORMAL);
4109fb66d81Schristos 
4119fb66d81Schristos 	h = tty->sy / 2;
4129fb66d81Schristos 	if (args_has(args, 'h')) {
4139fb66d81Schristos 		h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause);
4149fb66d81Schristos 		if (cause != NULL) {
4159fb66d81Schristos 			cmdq_error(item, "height %s", cause);
4169fb66d81Schristos 			free(cause);
4179fb66d81Schristos 			return (CMD_RETURN_ERROR);
4189fb66d81Schristos 		}
4199fb66d81Schristos 	}
4209fb66d81Schristos 
4219fb66d81Schristos 	w = tty->sx / 2;
4229fb66d81Schristos 	if (args_has(args, 'w')) {
4239fb66d81Schristos 		w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause);
4249fb66d81Schristos 		if (cause != NULL) {
4259fb66d81Schristos 			cmdq_error(item, "width %s", cause);
4269fb66d81Schristos 			free(cause);
4279fb66d81Schristos 			return (CMD_RETURN_ERROR);
4289fb66d81Schristos 		}
4299fb66d81Schristos 	}
4309fb66d81Schristos 
43146548964Swiz 	if (w > tty->sx)
43246548964Swiz 		w = tty->sx;
43346548964Swiz 	if (h > tty->sy)
43446548964Swiz 		h = tty->sy;
4359fb66d81Schristos 	if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h))
4369fb66d81Schristos 		return (CMD_RETURN_NORMAL);
4379fb66d81Schristos 
43846548964Swiz 	value = args_get(args, 'b');
43946548964Swiz 	if (args_has(args, 'B'))
44046548964Swiz 		lines = BOX_LINES_NONE;
44146548964Swiz 	else if (value != NULL) {
44246548964Swiz 		oe = options_get(o, "popup-border-lines");
44346548964Swiz 		lines = options_find_choice(options_table_entry(oe), value,
44446548964Swiz 		    &cause);
44546548964Swiz 		if (cause != NULL) {
44646548964Swiz 			cmdq_error(item, "popup-border-lines %s", cause);
44746548964Swiz 			free(cause);
44846548964Swiz 			return (CMD_RETURN_ERROR);
44946548964Swiz 		}
45046548964Swiz 	}
45146548964Swiz 
4529fb66d81Schristos 	value = args_get(args, 'd');
4539fb66d81Schristos 	if (value != NULL)
4549fb66d81Schristos 		cwd = format_single_from_target(item, value);
4559fb66d81Schristos 	else
4569fb66d81Schristos 		cwd = xstrdup(server_client_get_cwd(tc, s));
45746548964Swiz 	if (count == 0)
4589fb66d81Schristos 		shellcmd = options_get_string(s->options, "default-command");
45946548964Swiz 	else if (count == 1)
46046548964Swiz 		shellcmd = args_string(args, 0);
46146548964Swiz 	if (count <= 1 && (shellcmd == NULL || *shellcmd == '\0')) {
4629fb66d81Schristos 		shellcmd = NULL;
46346548964Swiz 		shell = options_get_string(s->options, "default-shell");
46446548964Swiz 		if (!checkshell(shell))
46546548964Swiz 			shell = _PATH_BSHELL;
46646548964Swiz 		cmd_append_argv(&argc, &argv, shell);
46746548964Swiz 	} else
46846548964Swiz 		args_to_vector(args, &argc, &argv);
46946548964Swiz 
47046548964Swiz 	if (args_has(args, 'e') >= 1) {
47146548964Swiz 		env = environ_create();
47246548964Swiz 		av = args_first_value(args, 'e');
47346548964Swiz 		while (av != NULL) {
47446548964Swiz 			environ_put(env, av->string, 0);
47546548964Swiz 			av = args_next_value(av);
47646548964Swiz 		}
4779fb66d81Schristos 	}
4789fb66d81Schristos 
47946548964Swiz 	if (args_has(args, 'T'))
48046548964Swiz 		title = format_single_from_target(item, args_get(args, 'T'));
48146548964Swiz 	else
48246548964Swiz 		title = xstrdup("");
4839fb66d81Schristos 	if (args_has(args, 'E') > 1)
4849fb66d81Schristos 		flags |= POPUP_CLOSEEXITZERO;
4859fb66d81Schristos 	else if (args_has(args, 'E'))
4869fb66d81Schristos 		flags |= POPUP_CLOSEEXIT;
48746548964Swiz 	if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc,
48846548964Swiz 	    argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0) {
48946548964Swiz 		cmd_free_argv(argc, argv);
49046548964Swiz 		if (env != NULL)
49146548964Swiz 			environ_free(env);
492f844e94eSwiz 		free(cwd);
49346548964Swiz 		free(title);
4946483eba0Schristos 		return (CMD_RETURN_NORMAL);
49546548964Swiz 	}
49646548964Swiz 	if (env != NULL)
49746548964Swiz 		environ_free(env);
498f844e94eSwiz 	free(cwd);
49946548964Swiz 	free(title);
50046548964Swiz 	cmd_free_argv(argc, argv);
5016483eba0Schristos 	return (CMD_RETURN_WAIT);
5026483eba0Schristos }
503