xref: /openbsd-src/usr.bin/tmux/window-client.c (revision e340e6ccd74fbef991671fad152bcf32c1edf45f)
1*e340e6ccSnicm /* $OpenBSD: window-client.c,v 1.34 2023/08/08 07:41:04 nicm Exp $ */
2a42faf7dSnicm 
3a42faf7dSnicm /*
4a42faf7dSnicm  * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
5a42faf7dSnicm  *
6a42faf7dSnicm  * Permission to use, copy, modify, and distribute this software for any
7a42faf7dSnicm  * purpose with or without fee is hereby granted, provided that the above
8a42faf7dSnicm  * copyright notice and this permission notice appear in all copies.
9a42faf7dSnicm  *
10a42faf7dSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a42faf7dSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a42faf7dSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a42faf7dSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a42faf7dSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15a42faf7dSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16a42faf7dSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a42faf7dSnicm  */
18a42faf7dSnicm 
19a42faf7dSnicm #include <sys/types.h>
20a42faf7dSnicm #include <sys/time.h>
21a42faf7dSnicm 
22a42faf7dSnicm #include <stdlib.h>
23a42faf7dSnicm #include <string.h>
24a40cc5d0Snicm #include <time.h>
25a42faf7dSnicm 
26a42faf7dSnicm #include "tmux.h"
27a42faf7dSnicm 
2830a94f45Snicm static struct screen	*window_client_init(struct window_mode_entry *,
29a42faf7dSnicm 			     struct cmd_find_state *, struct args *);
3030a94f45Snicm static void		 window_client_free(struct window_mode_entry *);
3130a94f45Snicm static void		 window_client_resize(struct window_mode_entry *, u_int,
32a42faf7dSnicm 			     u_int);
33734f37e4Snicm static void		 window_client_update(struct window_mode_entry *);
3430a94f45Snicm static void		 window_client_key(struct window_mode_entry *,
35bf52409eSnicm 			     struct client *, struct session *,
36bf52409eSnicm 			     struct winlink *, key_code, struct mouse_event *);
37a42faf7dSnicm 
38a42faf7dSnicm #define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'"
39a42faf7dSnicm 
40bf38e336Snicm #define WINDOW_CLIENT_DEFAULT_FORMAT \
416c6f347cSnicm 	"#{t/p:client_activity}: session #{session_name}"
42bf38e336Snicm 
43438eed14Snicm #define WINDOW_CLIENT_DEFAULT_KEY_FORMAT \
44438eed14Snicm 	"#{?#{e|<:#{line},10}," \
45438eed14Snicm 		"#{line}" \
46438eed14Snicm 	"," \
47438eed14Snicm 		"#{?#{e|<:#{line},36},"	\
48438eed14Snicm 	        	"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
49438eed14Snicm 		"," \
50438eed14Snicm 	        	"" \
51438eed14Snicm 		"}" \
52438eed14Snicm 	"}"
53438eed14Snicm 
541335341aSnicm static const struct menu_item window_client_menu_items[] = {
551335341aSnicm 	{ "Detach", 'd', NULL },
561335341aSnicm 	{ "Detach Tagged", 'D', NULL },
571335341aSnicm 	{ "", KEYC_NONE, NULL },
581335341aSnicm 	{ "Tag", 't', NULL },
591335341aSnicm 	{ "Tag All", '\024', NULL },
601335341aSnicm 	{ "Tag None", 'T', NULL },
611335341aSnicm 	{ "", KEYC_NONE, NULL },
621335341aSnicm 	{ "Cancel", 'q', NULL },
631335341aSnicm 
641335341aSnicm 	{ NULL, KEYC_NONE, NULL }
651335341aSnicm };
66f43bc87cSnicm 
67a42faf7dSnicm const struct window_mode window_client_mode = {
68a42faf7dSnicm 	.name = "client-mode",
6971431f24Snicm 	.default_format = WINDOW_CLIENT_DEFAULT_FORMAT,
70a42faf7dSnicm 
71a42faf7dSnicm 	.init = window_client_init,
72a42faf7dSnicm 	.free = window_client_free,
73a42faf7dSnicm 	.resize = window_client_resize,
74734f37e4Snicm 	.update = window_client_update,
75a42faf7dSnicm 	.key = window_client_key,
76a42faf7dSnicm };
77a42faf7dSnicm 
78a42faf7dSnicm enum window_client_sort_type {
79a42faf7dSnicm 	WINDOW_CLIENT_BY_NAME,
8057d50a52Snicm 	WINDOW_CLIENT_BY_SIZE,
81a42faf7dSnicm 	WINDOW_CLIENT_BY_CREATION_TIME,
82a42faf7dSnicm 	WINDOW_CLIENT_BY_ACTIVITY_TIME,
83a42faf7dSnicm };
84a42faf7dSnicm static const char *window_client_sort_list[] = {
85a42faf7dSnicm 	"name",
8657d50a52Snicm 	"size",
87b38aa712Snicm 	"creation",
88b38aa712Snicm 	"activity"
89a42faf7dSnicm };
903f6f9f7dSnicm static struct mode_tree_sort_criteria *window_client_sort;
91a42faf7dSnicm 
92a42faf7dSnicm struct window_client_itemdata {
93a42faf7dSnicm 	struct client	*c;
94a42faf7dSnicm };
95a42faf7dSnicm 
96a42faf7dSnicm struct window_client_modedata {
97f43bc87cSnicm 	struct window_pane		 *wp;
98f43bc87cSnicm 
99a42faf7dSnicm 	struct mode_tree_data		 *data;
100bf38e336Snicm 	char				 *format;
101438eed14Snicm 	char				 *key_format;
102a42faf7dSnicm 	char				 *command;
103a42faf7dSnicm 
104a42faf7dSnicm 	struct window_client_itemdata	**item_list;
105a42faf7dSnicm 	u_int				  item_size;
106a42faf7dSnicm };
107a42faf7dSnicm 
108a42faf7dSnicm static struct window_client_itemdata *
window_client_add_item(struct window_client_modedata * data)109a42faf7dSnicm window_client_add_item(struct window_client_modedata *data)
110a42faf7dSnicm {
111a42faf7dSnicm 	struct window_client_itemdata	*item;
112a42faf7dSnicm 
113a42faf7dSnicm 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
114a42faf7dSnicm 	    sizeof *data->item_list);
115a42faf7dSnicm 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
116a42faf7dSnicm 	return (item);
117a42faf7dSnicm }
118a42faf7dSnicm 
119a42faf7dSnicm static void
window_client_free_item(struct window_client_itemdata * item)120a42faf7dSnicm window_client_free_item(struct window_client_itemdata *item)
121a42faf7dSnicm {
122a42faf7dSnicm 	server_client_unref(item->c);
123a42faf7dSnicm 	free(item);
124a42faf7dSnicm }
125a42faf7dSnicm 
126a42faf7dSnicm static int
window_client_cmp(const void * a0,const void * b0)1273f6f9f7dSnicm window_client_cmp(const void *a0, const void *b0)
128a42faf7dSnicm {
129a42faf7dSnicm 	const struct window_client_itemdata *const	*a = a0;
130a42faf7dSnicm 	const struct window_client_itemdata *const	*b = b0;
1313f6f9f7dSnicm 	const struct window_client_itemdata		*itema = *a;
1323f6f9f7dSnicm 	const struct window_client_itemdata		*itemb = *b;
1333f6f9f7dSnicm 	struct client					*ca = itema->c;
1343f6f9f7dSnicm 	struct client					*cb = itemb->c;
1353f6f9f7dSnicm 	int						 result = 0;
136a42faf7dSnicm 
1373f6f9f7dSnicm 	switch (window_client_sort->field) {
1383f6f9f7dSnicm 	case WINDOW_CLIENT_BY_SIZE:
1393f6f9f7dSnicm 		result = ca->tty.sx - cb->tty.sx;
1403f6f9f7dSnicm 		if (result == 0)
1413f6f9f7dSnicm 			result = ca->tty.sy - cb->tty.sy;
1423f6f9f7dSnicm 		break;
1433f6f9f7dSnicm 	case WINDOW_CLIENT_BY_CREATION_TIME:
1443f6f9f7dSnicm 		if (timercmp(&ca->creation_time, &cb->creation_time, >))
1453f6f9f7dSnicm 			result = -1;
1463f6f9f7dSnicm 		else if (timercmp(&ca->creation_time, &cb->creation_time, <))
1473f6f9f7dSnicm 			result = 1;
1483f6f9f7dSnicm 		break;
1493f6f9f7dSnicm 	case WINDOW_CLIENT_BY_ACTIVITY_TIME:
1503f6f9f7dSnicm 		if (timercmp(&ca->activity_time, &cb->activity_time, >))
1513f6f9f7dSnicm 			result = -1;
1523f6f9f7dSnicm 		else if (timercmp(&ca->activity_time, &cb->activity_time, <))
1533f6f9f7dSnicm 			result = 1;
1543f6f9f7dSnicm 		break;
155a42faf7dSnicm 	}
156a42faf7dSnicm 
1573f6f9f7dSnicm 	/* Use WINDOW_CLIENT_BY_NAME as default order and tie breaker. */
1583f6f9f7dSnicm 	if (result == 0)
1593f6f9f7dSnicm 		result = strcmp(ca->name, cb->name);
16057d50a52Snicm 
1613f6f9f7dSnicm 	if (window_client_sort->reversed)
1623f6f9f7dSnicm 		result = -result;
1633f6f9f7dSnicm 	return (result);
164a42faf7dSnicm }
165a42faf7dSnicm 
166a42faf7dSnicm static void
window_client_build(void * modedata,struct mode_tree_sort_criteria * sort_crit,__unused uint64_t * tag,const char * filter)1673f6f9f7dSnicm window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
1683f6f9f7dSnicm     __unused uint64_t *tag, const char *filter)
169a42faf7dSnicm {
170a42faf7dSnicm 	struct window_client_modedata	*data = modedata;
171a42faf7dSnicm 	struct window_client_itemdata	*item;
172a42faf7dSnicm 	u_int				 i;
173a42faf7dSnicm 	struct client			*c;
174bf38e336Snicm 	char				*text, *cp;
175a42faf7dSnicm 
176a42faf7dSnicm 	for (i = 0; i < data->item_size; i++)
177a42faf7dSnicm 		window_client_free_item(data->item_list[i]);
178a42faf7dSnicm 	free(data->item_list);
179a42faf7dSnicm 	data->item_list = NULL;
180a42faf7dSnicm 	data->item_size = 0;
181a42faf7dSnicm 
182a42faf7dSnicm 	TAILQ_FOREACH(c, &clients, entry) {
183a34cf9c8Snicm 		if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
184a42faf7dSnicm 			continue;
185a42faf7dSnicm 
186a42faf7dSnicm 		item = window_client_add_item(data);
187a42faf7dSnicm 		item->c = c;
188a42faf7dSnicm 
189a42faf7dSnicm 		c->references++;
190a42faf7dSnicm 	}
191a42faf7dSnicm 
1923f6f9f7dSnicm 	window_client_sort = sort_crit;
193a42faf7dSnicm 	qsort(data->item_list, data->item_size, sizeof *data->item_list,
1943f6f9f7dSnicm 	    window_client_cmp);
195a42faf7dSnicm 
196a42faf7dSnicm 	for (i = 0; i < data->item_size; i++) {
197a42faf7dSnicm 		item = data->item_list[i];
198a42faf7dSnicm 		c = item->c;
199a42faf7dSnicm 
200024c311aSnicm 		if (filter != NULL) {
201024c311aSnicm 			cp = format_single(NULL, filter, c, NULL, NULL, NULL);
202024c311aSnicm 			if (!format_true(cp)) {
203024c311aSnicm 				free(cp);
204024c311aSnicm 				continue;
205024c311aSnicm 			}
206024c311aSnicm 			free(cp);
207024c311aSnicm 		}
208024c311aSnicm 
209bf38e336Snicm 		text = format_single(NULL, data->format, c, NULL, NULL, NULL);
210a42faf7dSnicm 		mode_tree_add(data->data, NULL, item, (uint64_t)c, c->name,
211a42faf7dSnicm 		    text, -1);
212a42faf7dSnicm 		free(text);
213a42faf7dSnicm 	}
214a42faf7dSnicm }
215a42faf7dSnicm 
2162b7e51f7Snicm static void
window_client_draw(__unused void * modedata,void * itemdata,struct screen_write_ctx * ctx,u_int sx,u_int sy)2172b7e51f7Snicm window_client_draw(__unused void *modedata, void *itemdata,
2182b7e51f7Snicm     struct screen_write_ctx *ctx, u_int sx, u_int sy)
219a42faf7dSnicm {
220a42faf7dSnicm 	struct window_client_itemdata	*item = itemdata;
221a42faf7dSnicm 	struct client			*c = item->c;
2224ffcb1c8Snicm 	struct screen			*s = ctx->s;
223a42faf7dSnicm 	struct window_pane		*wp;
2244ffcb1c8Snicm 	u_int				 cx = s->cx, cy = s->cy, lines, at;
225a42faf7dSnicm 
226b5f8268cSnicm 	if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
2272b7e51f7Snicm 		return;
228a42faf7dSnicm 	wp = c->session->curw->window->active;
229a42faf7dSnicm 
2304ffcb1c8Snicm 	lines = status_line_size(c);
2314ffcb1c8Snicm 	if (lines >= sy)
2324ffcb1c8Snicm 		lines = 0;
2334ffcb1c8Snicm 	if (status_at_line(c) == 0)
2344ffcb1c8Snicm 		at = lines;
2354ffcb1c8Snicm 	else
2364ffcb1c8Snicm 		at = 0;
237a42faf7dSnicm 
2384ffcb1c8Snicm 	screen_write_cursormove(ctx, cx, cy + at, 0);
2394ffcb1c8Snicm 	screen_write_preview(ctx, &wp->base, sx, sy - 2 - lines);
2404ffcb1c8Snicm 
2414ffcb1c8Snicm 	if (at != 0)
2424ffcb1c8Snicm 		screen_write_cursormove(ctx, cx, cy + 2, 0);
2434ffcb1c8Snicm 	else
2444ffcb1c8Snicm 		screen_write_cursormove(ctx, cx, cy + sy - 1 - lines, 0);
245*e340e6ccSnicm 	screen_write_hline(ctx, sx, 0, 0, BOX_LINES_DEFAULT, NULL);
246a42faf7dSnicm 
2474ffcb1c8Snicm 	if (at != 0)
2484ffcb1c8Snicm 		screen_write_cursormove(ctx, cx, cy, 0);
2494ffcb1c8Snicm 	else
2504ffcb1c8Snicm 		screen_write_cursormove(ctx, cx, cy + sy - lines, 0);
2514ffcb1c8Snicm 	screen_write_fast_copy(ctx, &c->status.screen, 0, 0, sx, lines);
252a42faf7dSnicm }
253a42faf7dSnicm 
254f43bc87cSnicm static void
window_client_menu(void * modedata,struct client * c,key_code key)255f43bc87cSnicm window_client_menu(void *modedata, struct client *c, key_code key)
256f43bc87cSnicm {
257f43bc87cSnicm 	struct window_client_modedata	*data = modedata;
258f43bc87cSnicm 	struct window_pane		*wp = data->wp;
259f43bc87cSnicm 	struct window_mode_entry	*wme;
260f43bc87cSnicm 
261f43bc87cSnicm 	wme = TAILQ_FIRST(&wp->modes);
262f43bc87cSnicm 	if (wme == NULL || wme->data != modedata)
263f43bc87cSnicm 		return;
264f43bc87cSnicm 	window_client_key(wme, c, NULL, NULL, key, NULL);
265f43bc87cSnicm }
266f43bc87cSnicm 
267438eed14Snicm static key_code
window_client_get_key(void * modedata,void * itemdata,u_int line)268438eed14Snicm window_client_get_key(void *modedata, void *itemdata, u_int line)
269438eed14Snicm {
270438eed14Snicm 	struct window_client_modedata	*data = modedata;
271438eed14Snicm 	struct window_client_itemdata	*item = itemdata;
272438eed14Snicm 	struct format_tree		*ft;
273438eed14Snicm 	char				*expanded;
274438eed14Snicm 	key_code			 key;
275438eed14Snicm 
276438eed14Snicm 	ft = format_create(NULL, NULL, FORMAT_NONE, 0);
277438eed14Snicm 	format_defaults(ft, item->c, NULL, 0, NULL);
278438eed14Snicm 	format_add(ft, "line", "%u", line);
279438eed14Snicm 
280438eed14Snicm 	expanded = format_expand(ft, data->key_format);
281438eed14Snicm 	key = key_string_lookup_string(expanded);
282438eed14Snicm 	free(expanded);
283438eed14Snicm 	format_free(ft);
2843e8355bdSnicm 	return (key);
285438eed14Snicm }
286438eed14Snicm 
287a42faf7dSnicm static struct screen *
window_client_init(struct window_mode_entry * wme,__unused struct cmd_find_state * fs,struct args * args)28830a94f45Snicm window_client_init(struct window_mode_entry *wme,
28930a94f45Snicm     __unused struct cmd_find_state *fs, struct args *args)
290a42faf7dSnicm {
29130a94f45Snicm 	struct window_pane		*wp = wme->wp;
292a42faf7dSnicm 	struct window_client_modedata	*data;
293a42faf7dSnicm 	struct screen			*s;
294a42faf7dSnicm 
29530a94f45Snicm 	wme->data = data = xcalloc(1, sizeof *data);
296f43bc87cSnicm 	data->wp = wp;
297a42faf7dSnicm 
298bf38e336Snicm 	if (args == NULL || !args_has(args, 'F'))
299bf38e336Snicm 		data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT);
300bf38e336Snicm 	else
301bf38e336Snicm 		data->format = xstrdup(args_get(args, 'F'));
302438eed14Snicm 	if (args == NULL || !args_has(args, 'K'))
303438eed14Snicm 		data->key_format = xstrdup(WINDOW_CLIENT_DEFAULT_KEY_FORMAT);
304438eed14Snicm 	else
305438eed14Snicm 		data->key_format = xstrdup(args_get(args, 'K'));
3061693b10bSnicm 	if (args == NULL || args_count(args) == 0)
307a42faf7dSnicm 		data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
308a42faf7dSnicm 	else
3091693b10bSnicm 		data->command = xstrdup(args_string(args, 0));
310a42faf7dSnicm 
311b38aa712Snicm 	data->data = mode_tree_start(wp, args, window_client_build,
312438eed14Snicm 	    window_client_draw, NULL, window_client_menu, NULL,
313438eed14Snicm 	    window_client_get_key, data, window_client_menu_items,
314438eed14Snicm 	    window_client_sort_list, nitems(window_client_sort_list), &s);
3154f5e4c93Snicm 	mode_tree_zoom(data->data, args);
316a42faf7dSnicm 
317a42faf7dSnicm 	mode_tree_build(data->data);
318a42faf7dSnicm 	mode_tree_draw(data->data);
319a42faf7dSnicm 
320a42faf7dSnicm 	return (s);
321a42faf7dSnicm }
322a42faf7dSnicm 
323a42faf7dSnicm static void
window_client_free(struct window_mode_entry * wme)32430a94f45Snicm window_client_free(struct window_mode_entry *wme)
325a42faf7dSnicm {
32630a94f45Snicm 	struct window_client_modedata	*data = wme->data;
327a42faf7dSnicm 	u_int				 i;
328a42faf7dSnicm 
329a42faf7dSnicm 	if (data == NULL)
330a42faf7dSnicm 		return;
331a42faf7dSnicm 
332a42faf7dSnicm 	mode_tree_free(data->data);
333a42faf7dSnicm 
334a42faf7dSnicm 	for (i = 0; i < data->item_size; i++)
335a42faf7dSnicm 		window_client_free_item(data->item_list[i]);
336a42faf7dSnicm 	free(data->item_list);
337a42faf7dSnicm 
338bf38e336Snicm 	free(data->format);
339438eed14Snicm 	free(data->key_format);
340a42faf7dSnicm 	free(data->command);
341bf38e336Snicm 
342a42faf7dSnicm 	free(data);
343a42faf7dSnicm }
344a42faf7dSnicm 
345a42faf7dSnicm static void
window_client_resize(struct window_mode_entry * wme,u_int sx,u_int sy)34630a94f45Snicm window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
347a42faf7dSnicm {
34830a94f45Snicm 	struct window_client_modedata	*data = wme->data;
349a42faf7dSnicm 
350a42faf7dSnicm 	mode_tree_resize(data->data, sx, sy);
351a42faf7dSnicm }
352a42faf7dSnicm 
353a42faf7dSnicm static void
window_client_update(struct window_mode_entry * wme)354734f37e4Snicm window_client_update(struct window_mode_entry *wme)
355734f37e4Snicm {
356734f37e4Snicm 	struct window_client_modedata	*data = wme->data;
357734f37e4Snicm 
358734f37e4Snicm 	mode_tree_build(data->data);
359734f37e4Snicm 	mode_tree_draw(data->data);
360734f37e4Snicm 	data->wp->flags |= PANE_REDRAW;
361734f37e4Snicm }
362734f37e4Snicm 
363734f37e4Snicm static void
window_client_do_detach(void * modedata,void * itemdata,__unused struct client * c,key_code key)364d7af2c28Snicm window_client_do_detach(void *modedata, void *itemdata,
365d7af2c28Snicm     __unused struct client *c, key_code key)
366a42faf7dSnicm {
367a42faf7dSnicm 	struct window_client_modedata	*data = modedata;
368a42faf7dSnicm 	struct window_client_itemdata	*item = itemdata;
369a42faf7dSnicm 
370a42faf7dSnicm 	if (item == mode_tree_get_current(data->data))
371a42faf7dSnicm 		mode_tree_down(data->data, 0);
372a42faf7dSnicm 	if (key == 'd' || key == 'D')
373a42faf7dSnicm 		server_client_detach(item->c, MSG_DETACH);
374a42faf7dSnicm 	else if (key == 'x' || key == 'X')
375a42faf7dSnicm 		server_client_detach(item->c, MSG_DETACHKILL);
376a42faf7dSnicm 	else if (key == 'z' || key == 'Z')
377a42faf7dSnicm 		server_client_suspend(item->c);
378a42faf7dSnicm }
379a42faf7dSnicm 
380a42faf7dSnicm static void
window_client_key(struct window_mode_entry * wme,struct client * c,__unused struct session * s,__unused struct winlink * wl,key_code key,struct mouse_event * m)38130a94f45Snicm window_client_key(struct window_mode_entry *wme, struct client *c,
382bf52409eSnicm     __unused struct session *s, __unused struct winlink *wl, key_code key,
383bf52409eSnicm     struct mouse_event *m)
384a42faf7dSnicm {
38530a94f45Snicm 	struct window_pane		*wp = wme->wp;
38630a94f45Snicm 	struct window_client_modedata	*data = wme->data;
387d7af2c28Snicm 	struct mode_tree_data		*mtd = data->data;
388a42faf7dSnicm 	struct window_client_itemdata	*item;
389a42faf7dSnicm 	int				 finished;
390a42faf7dSnicm 
39163c9949dSnicm 	finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
392a42faf7dSnicm 	switch (key) {
393a42faf7dSnicm 	case 'd':
394a42faf7dSnicm 	case 'x':
395a42faf7dSnicm 	case 'z':
396d7af2c28Snicm 		item = mode_tree_get_current(mtd);
397d7af2c28Snicm 		window_client_do_detach(data, item, c, key);
398d7af2c28Snicm 		mode_tree_build(mtd);
399a42faf7dSnicm 		break;
400a42faf7dSnicm 	case 'D':
401a42faf7dSnicm 	case 'X':
402a42faf7dSnicm 	case 'Z':
403d7af2c28Snicm 		mode_tree_each_tagged(mtd, window_client_do_detach, c, key, 0);
404d7af2c28Snicm 		mode_tree_build(mtd);
405a42faf7dSnicm 		break;
406a42faf7dSnicm 	case '\r':
407d7af2c28Snicm 		item = mode_tree_get_current(mtd);
408d7af2c28Snicm 		mode_tree_run_command(c, NULL, data->command, item->c->ttyname);
409d7af2c28Snicm 		finished = 1;
410d7af2c28Snicm 		break;
411a42faf7dSnicm 	}
412a42faf7dSnicm 	if (finished || server_client_how_many() == 0)
413a42faf7dSnicm 		window_pane_reset_mode(wp);
414a42faf7dSnicm 	else {
415d7af2c28Snicm 		mode_tree_draw(mtd);
416a42faf7dSnicm 		wp->flags |= PANE_REDRAW;
417a42faf7dSnicm 	}
418a42faf7dSnicm }
419