xref: /netbsd-src/external/bsd/tmux/dist/window-client.c (revision f844e94ef29eebc7999c12636b87f541bb86868b)
1c9ad075bSchristos /* $OpenBSD$ */
2c9ad075bSchristos 
3c9ad075bSchristos /*
4c9ad075bSchristos  * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
5c9ad075bSchristos  *
6c9ad075bSchristos  * Permission to use, copy, modify, and distribute this software for any
7c9ad075bSchristos  * purpose with or without fee is hereby granted, provided that the above
8c9ad075bSchristos  * copyright notice and this permission notice appear in all copies.
9c9ad075bSchristos  *
10c9ad075bSchristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c9ad075bSchristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c9ad075bSchristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c9ad075bSchristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c9ad075bSchristos  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15c9ad075bSchristos  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16c9ad075bSchristos  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c9ad075bSchristos  */
18c9ad075bSchristos 
19c9ad075bSchristos #include <sys/types.h>
20c9ad075bSchristos #include <sys/time.h>
21c9ad075bSchristos 
22c9ad075bSchristos #include <stdlib.h>
23c9ad075bSchristos #include <string.h>
24c9ad075bSchristos #include <time.h>
25c9ad075bSchristos 
26c9ad075bSchristos #include "tmux.h"
27c9ad075bSchristos 
280a274e86Schristos static struct screen	*window_client_init(struct window_mode_entry *,
29c9ad075bSchristos 			     struct cmd_find_state *, struct args *);
300a274e86Schristos static void		 window_client_free(struct window_mode_entry *);
310a274e86Schristos static void		 window_client_resize(struct window_mode_entry *, u_int,
32c9ad075bSchristos 			     u_int);
33e271dbb8Schristos static void		 window_client_update(struct window_mode_entry *);
340a274e86Schristos static void		 window_client_key(struct window_mode_entry *,
350a274e86Schristos 			     struct client *, struct session *,
360a274e86Schristos 			     struct winlink *, key_code, struct mouse_event *);
37c9ad075bSchristos 
38c9ad075bSchristos #define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'"
39c9ad075bSchristos 
40c9ad075bSchristos #define WINDOW_CLIENT_DEFAULT_FORMAT \
41e271dbb8Schristos 	"#{t/p:client_activity}: session #{session_name}"
42e271dbb8Schristos 
43e271dbb8Schristos #define WINDOW_CLIENT_DEFAULT_KEY_FORMAT \
44e271dbb8Schristos 	"#{?#{e|<:#{line},10}," \
45e271dbb8Schristos 		"#{line}" \
46e271dbb8Schristos 	"," \
47e271dbb8Schristos 		"#{?#{e|<:#{line},36},"	\
48e271dbb8Schristos 	        	"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
49e271dbb8Schristos 		"," \
50e271dbb8Schristos 	        	"" \
51e271dbb8Schristos 		"}" \
52e271dbb8Schristos 	"}"
53c9ad075bSchristos 
5430744affSchristos static const struct menu_item window_client_menu_items[] = {
5530744affSchristos 	{ "Detach", 'd', NULL },
5630744affSchristos 	{ "Detach Tagged", 'D', NULL },
5730744affSchristos 	{ "", KEYC_NONE, NULL },
5830744affSchristos 	{ "Tag", 't', NULL },
5930744affSchristos 	{ "Tag All", '\024', NULL },
6030744affSchristos 	{ "Tag None", 'T', NULL },
6130744affSchristos 	{ "", KEYC_NONE, NULL },
6230744affSchristos 	{ "Cancel", 'q', NULL },
6330744affSchristos 
6430744affSchristos 	{ NULL, KEYC_NONE, NULL }
6530744affSchristos };
6630744affSchristos 
67c9ad075bSchristos const struct window_mode window_client_mode = {
68c9ad075bSchristos 	.name = "client-mode",
690a274e86Schristos 	.default_format = WINDOW_CLIENT_DEFAULT_FORMAT,
70c9ad075bSchristos 
71c9ad075bSchristos 	.init = window_client_init,
72c9ad075bSchristos 	.free = window_client_free,
73c9ad075bSchristos 	.resize = window_client_resize,
74e271dbb8Schristos 	.update = window_client_update,
75c9ad075bSchristos 	.key = window_client_key,
76c9ad075bSchristos };
77c9ad075bSchristos 
78c9ad075bSchristos enum window_client_sort_type {
79c9ad075bSchristos 	WINDOW_CLIENT_BY_NAME,
80c9ad075bSchristos 	WINDOW_CLIENT_BY_SIZE,
81c9ad075bSchristos 	WINDOW_CLIENT_BY_CREATION_TIME,
82c9ad075bSchristos 	WINDOW_CLIENT_BY_ACTIVITY_TIME,
83c9ad075bSchristos };
84c9ad075bSchristos static const char *window_client_sort_list[] = {
85c9ad075bSchristos 	"name",
86c9ad075bSchristos 	"size",
87c9ad075bSchristos 	"creation",
88c9ad075bSchristos 	"activity"
89c9ad075bSchristos };
9068e6ba84Schristos static struct mode_tree_sort_criteria *window_client_sort;
91c9ad075bSchristos 
92c9ad075bSchristos struct window_client_itemdata {
93c9ad075bSchristos 	struct client	*c;
94c9ad075bSchristos };
95c9ad075bSchristos 
96c9ad075bSchristos struct window_client_modedata {
9730744affSchristos 	struct window_pane		 *wp;
9830744affSchristos 
99c9ad075bSchristos 	struct mode_tree_data		 *data;
100c9ad075bSchristos 	char				 *format;
101e271dbb8Schristos 	char				 *key_format;
102c9ad075bSchristos 	char				 *command;
103c9ad075bSchristos 
104c9ad075bSchristos 	struct window_client_itemdata	**item_list;
105c9ad075bSchristos 	u_int				  item_size;
106c9ad075bSchristos };
107c9ad075bSchristos 
108c9ad075bSchristos static struct window_client_itemdata *
window_client_add_item(struct window_client_modedata * data)109c9ad075bSchristos window_client_add_item(struct window_client_modedata *data)
110c9ad075bSchristos {
111c9ad075bSchristos 	struct window_client_itemdata	*item;
112c9ad075bSchristos 
113c9ad075bSchristos 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
114c9ad075bSchristos 	    sizeof *data->item_list);
115c9ad075bSchristos 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
116c9ad075bSchristos 	return (item);
117c9ad075bSchristos }
118c9ad075bSchristos 
119c9ad075bSchristos static void
window_client_free_item(struct window_client_itemdata * item)120c9ad075bSchristos window_client_free_item(struct window_client_itemdata *item)
121c9ad075bSchristos {
122c9ad075bSchristos 	server_client_unref(item->c);
123c9ad075bSchristos 	free(item);
124c9ad075bSchristos }
125c9ad075bSchristos 
126c9ad075bSchristos static int
window_client_cmp(const void * a0,const void * b0)12768e6ba84Schristos window_client_cmp(const void *a0, const void *b0)
128c9ad075bSchristos {
129c9ad075bSchristos 	const struct window_client_itemdata *const	*a = a0;
130c9ad075bSchristos 	const struct window_client_itemdata *const	*b = b0;
13168e6ba84Schristos 	const struct window_client_itemdata		*itema = *a;
13268e6ba84Schristos 	const struct window_client_itemdata		*itemb = *b;
13368e6ba84Schristos 	struct client					*ca = itema->c;
13468e6ba84Schristos 	struct client					*cb = itemb->c;
13568e6ba84Schristos 	int						 result = 0;
136c9ad075bSchristos 
13768e6ba84Schristos 	switch (window_client_sort->field) {
13868e6ba84Schristos 	case WINDOW_CLIENT_BY_SIZE:
13968e6ba84Schristos 		result = ca->tty.sx - cb->tty.sx;
14068e6ba84Schristos 		if (result == 0)
14168e6ba84Schristos 			result = ca->tty.sy - cb->tty.sy;
14268e6ba84Schristos 		break;
14368e6ba84Schristos 	case WINDOW_CLIENT_BY_CREATION_TIME:
14468e6ba84Schristos 		if (timercmp(&ca->creation_time, &cb->creation_time, >))
14568e6ba84Schristos 			result = -1;
14668e6ba84Schristos 		else if (timercmp(&ca->creation_time, &cb->creation_time, <))
14768e6ba84Schristos 			result = 1;
14868e6ba84Schristos 		break;
14968e6ba84Schristos 	case WINDOW_CLIENT_BY_ACTIVITY_TIME:
15068e6ba84Schristos 		if (timercmp(&ca->activity_time, &cb->activity_time, >))
15168e6ba84Schristos 			result = -1;
15268e6ba84Schristos 		else if (timercmp(&ca->activity_time, &cb->activity_time, <))
15368e6ba84Schristos 			result = 1;
15468e6ba84Schristos 		break;
155c9ad075bSchristos 	}
156c9ad075bSchristos 
15768e6ba84Schristos 	/* Use WINDOW_CLIENT_BY_NAME as default order and tie breaker. */
15868e6ba84Schristos 	if (result == 0)
15968e6ba84Schristos 		result = strcmp(ca->name, cb->name);
160c9ad075bSchristos 
16168e6ba84Schristos 	if (window_client_sort->reversed)
16268e6ba84Schristos 		result = -result;
16368e6ba84Schristos 	return (result);
164c9ad075bSchristos }
165c9ad075bSchristos 
166c9ad075bSchristos static void
window_client_build(void * modedata,struct mode_tree_sort_criteria * sort_crit,__unused uint64_t * tag,const char * filter)16768e6ba84Schristos window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
16868e6ba84Schristos     __unused uint64_t *tag, const char *filter)
169c9ad075bSchristos {
170c9ad075bSchristos 	struct window_client_modedata	*data = modedata;
171c9ad075bSchristos 	struct window_client_itemdata	*item;
172c9ad075bSchristos 	u_int				 i;
173c9ad075bSchristos 	struct client			*c;
174c9ad075bSchristos 	char				*text, *cp;
175c9ad075bSchristos 
176c9ad075bSchristos 	for (i = 0; i < data->item_size; i++)
177c9ad075bSchristos 		window_client_free_item(data->item_list[i]);
178c9ad075bSchristos 	free(data->item_list);
179c9ad075bSchristos 	data->item_list = NULL;
180c9ad075bSchristos 	data->item_size = 0;
181c9ad075bSchristos 
182c9ad075bSchristos 	TAILQ_FOREACH(c, &clients, entry) {
183e271dbb8Schristos 		if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
184c9ad075bSchristos 			continue;
185c9ad075bSchristos 
186c9ad075bSchristos 		item = window_client_add_item(data);
187c9ad075bSchristos 		item->c = c;
188c9ad075bSchristos 
189c9ad075bSchristos 		c->references++;
190c9ad075bSchristos 	}
191c9ad075bSchristos 
19268e6ba84Schristos 	window_client_sort = sort_crit;
193c9ad075bSchristos 	qsort(data->item_list, data->item_size, sizeof *data->item_list,
19468e6ba84Schristos 	    window_client_cmp);
195c9ad075bSchristos 
196c9ad075bSchristos 	for (i = 0; i < data->item_size; i++) {
197c9ad075bSchristos 		item = data->item_list[i];
198c9ad075bSchristos 		c = item->c;
199c9ad075bSchristos 
200c9ad075bSchristos 		if (filter != NULL) {
201c9ad075bSchristos 			cp = format_single(NULL, filter, c, NULL, NULL, NULL);
202c9ad075bSchristos 			if (!format_true(cp)) {
203c9ad075bSchristos 				free(cp);
204c9ad075bSchristos 				continue;
205c9ad075bSchristos 			}
206c9ad075bSchristos 			free(cp);
207c9ad075bSchristos 		}
208c9ad075bSchristos 
209c9ad075bSchristos 		text = format_single(NULL, data->format, c, NULL, NULL, NULL);
2106cb901deSkre 		mode_tree_add(data->data, NULL, item, (uintptr_t)c, c->name,
211c9ad075bSchristos 		    text, -1);
212c9ad075bSchristos 		free(text);
213c9ad075bSchristos 	}
214c9ad075bSchristos }
215c9ad075bSchristos 
216c7e17de0Schristos static void
window_client_draw(__unused void * modedata,void * itemdata,struct screen_write_ctx * ctx,u_int sx,u_int sy)217c7e17de0Schristos window_client_draw(__unused void *modedata, void *itemdata,
218c7e17de0Schristos     struct screen_write_ctx *ctx, u_int sx, u_int sy)
219c9ad075bSchristos {
220c9ad075bSchristos 	struct window_client_itemdata	*item = itemdata;
221c9ad075bSchristos 	struct client			*c = item->c;
2220a274e86Schristos 	struct screen			*s = ctx->s;
223c9ad075bSchristos 	struct window_pane		*wp;
2240a274e86Schristos 	u_int				 cx = s->cx, cy = s->cy, lines, at;
225c9ad075bSchristos 
22668e6ba84Schristos 	if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
227c7e17de0Schristos 		return;
228c9ad075bSchristos 	wp = c->session->curw->window->active;
229c9ad075bSchristos 
2300a274e86Schristos 	lines = status_line_size(c);
2310a274e86Schristos 	if (lines >= sy)
2320a274e86Schristos 		lines = 0;
2330a274e86Schristos 	if (status_at_line(c) == 0)
2340a274e86Schristos 		at = lines;
2350a274e86Schristos 	else
2360a274e86Schristos 		at = 0;
237c9ad075bSchristos 
2380a274e86Schristos 	screen_write_cursormove(ctx, cx, cy + at, 0);
2390a274e86Schristos 	screen_write_preview(ctx, &wp->base, sx, sy - 2 - lines);
2400a274e86Schristos 
2410a274e86Schristos 	if (at != 0)
2420a274e86Schristos 		screen_write_cursormove(ctx, cx, cy + 2, 0);
2430a274e86Schristos 	else
2440a274e86Schristos 		screen_write_cursormove(ctx, cx, cy + sy - 1 - lines, 0);
245*f844e94eSwiz 	screen_write_hline(ctx, sx, 0, 0, BOX_LINES_DEFAULT, NULL);
246c9ad075bSchristos 
2470a274e86Schristos 	if (at != 0)
2480a274e86Schristos 		screen_write_cursormove(ctx, cx, cy, 0);
249c9ad075bSchristos 	else
2500a274e86Schristos 		screen_write_cursormove(ctx, cx, cy + sy - lines, 0);
2510a274e86Schristos 	screen_write_fast_copy(ctx, &c->status.screen, 0, 0, sx, lines);
252c9ad075bSchristos }
253c9ad075bSchristos 
25430744affSchristos static void
window_client_menu(void * modedata,struct client * c,key_code key)25530744affSchristos window_client_menu(void *modedata, struct client *c, key_code key)
25630744affSchristos {
25730744affSchristos 	struct window_client_modedata	*data = modedata;
25830744affSchristos 	struct window_pane		*wp = data->wp;
25930744affSchristos 	struct window_mode_entry	*wme;
26030744affSchristos 
26130744affSchristos 	wme = TAILQ_FIRST(&wp->modes);
26230744affSchristos 	if (wme == NULL || wme->data != modedata)
26330744affSchristos 		return;
26430744affSchristos 	window_client_key(wme, c, NULL, NULL, key, NULL);
26530744affSchristos }
26630744affSchristos 
267e271dbb8Schristos static key_code
window_client_get_key(void * modedata,void * itemdata,u_int line)268e271dbb8Schristos window_client_get_key(void *modedata, void *itemdata, u_int line)
269e271dbb8Schristos {
270e271dbb8Schristos 	struct window_client_modedata	*data = modedata;
271e271dbb8Schristos 	struct window_client_itemdata	*item = itemdata;
272e271dbb8Schristos 	struct format_tree		*ft;
273e271dbb8Schristos 	char				*expanded;
274e271dbb8Schristos 	key_code			 key;
275e271dbb8Schristos 
276e271dbb8Schristos 	ft = format_create(NULL, NULL, FORMAT_NONE, 0);
277e271dbb8Schristos 	format_defaults(ft, item->c, NULL, 0, NULL);
278e271dbb8Schristos 	format_add(ft, "line", "%u", line);
279e271dbb8Schristos 
280e271dbb8Schristos 	expanded = format_expand(ft, data->key_format);
281e271dbb8Schristos 	key = key_string_lookup_string(expanded);
282e271dbb8Schristos 	free(expanded);
283e271dbb8Schristos 	format_free(ft);
28446548964Swiz 	return (key);
285e271dbb8Schristos }
286e271dbb8Schristos 
287c9ad075bSchristos static struct screen *
window_client_init(struct window_mode_entry * wme,__unused struct cmd_find_state * fs,struct args * args)2880a274e86Schristos window_client_init(struct window_mode_entry *wme,
2890a274e86Schristos     __unused struct cmd_find_state *fs, struct args *args)
290c9ad075bSchristos {
2910a274e86Schristos 	struct window_pane		*wp = wme->wp;
292c9ad075bSchristos 	struct window_client_modedata	*data;
293c9ad075bSchristos 	struct screen			*s;
294c9ad075bSchristos 
2950a274e86Schristos 	wme->data = data = xcalloc(1, sizeof *data);
29630744affSchristos 	data->wp = wp;
297c9ad075bSchristos 
298c9ad075bSchristos 	if (args == NULL || !args_has(args, 'F'))
299c9ad075bSchristos 		data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT);
300c9ad075bSchristos 	else
301c9ad075bSchristos 		data->format = xstrdup(args_get(args, 'F'));
302e271dbb8Schristos 	if (args == NULL || !args_has(args, 'K'))
303e271dbb8Schristos 		data->key_format = xstrdup(WINDOW_CLIENT_DEFAULT_KEY_FORMAT);
304e271dbb8Schristos 	else
305e271dbb8Schristos 		data->key_format = xstrdup(args_get(args, 'K'));
30646548964Swiz 	if (args == NULL || args_count(args) == 0)
307c9ad075bSchristos 		data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
308c9ad075bSchristos 	else
30946548964Swiz 		data->command = xstrdup(args_string(args, 0));
310c9ad075bSchristos 
311c9ad075bSchristos 	data->data = mode_tree_start(wp, args, window_client_build,
312e271dbb8Schristos 	    window_client_draw, NULL, window_client_menu, NULL,
313e271dbb8Schristos 	    window_client_get_key, data, window_client_menu_items,
314e271dbb8Schristos 	    window_client_sort_list, nitems(window_client_sort_list), &s);
315c7e17de0Schristos 	mode_tree_zoom(data->data, args);
316c9ad075bSchristos 
317c9ad075bSchristos 	mode_tree_build(data->data);
318c9ad075bSchristos 	mode_tree_draw(data->data);
319c9ad075bSchristos 
320c9ad075bSchristos 	return (s);
321c9ad075bSchristos }
322c9ad075bSchristos 
323c9ad075bSchristos static void
window_client_free(struct window_mode_entry * wme)3240a274e86Schristos window_client_free(struct window_mode_entry *wme)
325c9ad075bSchristos {
3260a274e86Schristos 	struct window_client_modedata	*data = wme->data;
327c9ad075bSchristos 	u_int				 i;
328c9ad075bSchristos 
329c9ad075bSchristos 	if (data == NULL)
330c9ad075bSchristos 		return;
331c9ad075bSchristos 
332c9ad075bSchristos 	mode_tree_free(data->data);
333c9ad075bSchristos 
334c9ad075bSchristos 	for (i = 0; i < data->item_size; i++)
335c9ad075bSchristos 		window_client_free_item(data->item_list[i]);
336c9ad075bSchristos 	free(data->item_list);
337c9ad075bSchristos 
338c9ad075bSchristos 	free(data->format);
339e271dbb8Schristos 	free(data->key_format);
340c9ad075bSchristos 	free(data->command);
341c9ad075bSchristos 
342c9ad075bSchristos 	free(data);
343c9ad075bSchristos }
344c9ad075bSchristos 
345c9ad075bSchristos static void
window_client_resize(struct window_mode_entry * wme,u_int sx,u_int sy)3460a274e86Schristos window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
347c9ad075bSchristos {
3480a274e86Schristos 	struct window_client_modedata	*data = wme->data;
349c9ad075bSchristos 
350c9ad075bSchristos 	mode_tree_resize(data->data, sx, sy);
351c9ad075bSchristos }
352c9ad075bSchristos 
353c9ad075bSchristos static void
window_client_update(struct window_mode_entry * wme)354e271dbb8Schristos window_client_update(struct window_mode_entry *wme)
355e271dbb8Schristos {
356e271dbb8Schristos 	struct window_client_modedata	*data = wme->data;
357e271dbb8Schristos 
358e271dbb8Schristos 	mode_tree_build(data->data);
359e271dbb8Schristos 	mode_tree_draw(data->data);
360e271dbb8Schristos 	data->wp->flags |= PANE_REDRAW;
361e271dbb8Schristos }
362e271dbb8Schristos 
363e271dbb8Schristos static void
window_client_do_detach(void * modedata,void * itemdata,__unused struct client * c,key_code key)364c7e17de0Schristos window_client_do_detach(void *modedata, void *itemdata,
365c7e17de0Schristos     __unused struct client *c, key_code key)
366c9ad075bSchristos {
367c9ad075bSchristos 	struct window_client_modedata	*data = modedata;
368c9ad075bSchristos 	struct window_client_itemdata	*item = itemdata;
369c9ad075bSchristos 
370c9ad075bSchristos 	if (item == mode_tree_get_current(data->data))
371c9ad075bSchristos 		mode_tree_down(data->data, 0);
372c9ad075bSchristos 	if (key == 'd' || key == 'D')
373c9ad075bSchristos 		server_client_detach(item->c, MSG_DETACH);
374c9ad075bSchristos 	else if (key == 'x' || key == 'X')
375c9ad075bSchristos 		server_client_detach(item->c, MSG_DETACHKILL);
376c9ad075bSchristos 	else if (key == 'z' || key == 'Z')
377c9ad075bSchristos 		server_client_suspend(item->c);
378c9ad075bSchristos }
379c9ad075bSchristos 
380c9ad075bSchristos 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)3810a274e86Schristos window_client_key(struct window_mode_entry *wme, struct client *c,
3820a274e86Schristos     __unused struct session *s, __unused struct winlink *wl, key_code key,
3830a274e86Schristos     struct mouse_event *m)
384c9ad075bSchristos {
3850a274e86Schristos 	struct window_pane		*wp = wme->wp;
3860a274e86Schristos 	struct window_client_modedata	*data = wme->data;
387c7e17de0Schristos 	struct mode_tree_data		*mtd = data->data;
388c9ad075bSchristos 	struct window_client_itemdata	*item;
389c9ad075bSchristos 	int				 finished;
390c9ad075bSchristos 
391c7e17de0Schristos 	finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
392c9ad075bSchristos 	switch (key) {
393c9ad075bSchristos 	case 'd':
394c9ad075bSchristos 	case 'x':
395c9ad075bSchristos 	case 'z':
396c7e17de0Schristos 		item = mode_tree_get_current(mtd);
397c7e17de0Schristos 		window_client_do_detach(data, item, c, key);
398c7e17de0Schristos 		mode_tree_build(mtd);
399c9ad075bSchristos 		break;
400c9ad075bSchristos 	case 'D':
401c9ad075bSchristos 	case 'X':
402c9ad075bSchristos 	case 'Z':
403c7e17de0Schristos 		mode_tree_each_tagged(mtd, window_client_do_detach, c, key, 0);
404c7e17de0Schristos 		mode_tree_build(mtd);
405c9ad075bSchristos 		break;
406c9ad075bSchristos 	case '\r':
407c7e17de0Schristos 		item = mode_tree_get_current(mtd);
408c7e17de0Schristos 		mode_tree_run_command(c, NULL, data->command, item->c->ttyname);
409c7e17de0Schristos 		finished = 1;
410c7e17de0Schristos 		break;
411c9ad075bSchristos 	}
412c9ad075bSchristos 	if (finished || server_client_how_many() == 0)
413c9ad075bSchristos 		window_pane_reset_mode(wp);
414c9ad075bSchristos 	else {
415c7e17de0Schristos 		mode_tree_draw(mtd);
416c9ad075bSchristos 		wp->flags |= PANE_REDRAW;
417c9ad075bSchristos 	}
418c9ad075bSchristos }
419