xref: /openbsd-src/usr.bin/tmux/window-tree.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /* $OpenBSD: window-tree.c,v 1.53 2020/12/03 07:12:12 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2017 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 <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "tmux.h"
26 
27 static struct screen	*window_tree_init(struct window_mode_entry *,
28 			     struct cmd_find_state *, struct args *);
29 static void		 window_tree_free(struct window_mode_entry *);
30 static void		 window_tree_resize(struct window_mode_entry *, u_int,
31 			     u_int);
32 static void		 window_tree_update(struct window_mode_entry *);
33 static void		 window_tree_key(struct window_mode_entry *,
34 			     struct client *, struct session *,
35 			     struct winlink *, key_code, struct mouse_event *);
36 
37 #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'"
38 
39 #define WINDOW_TREE_DEFAULT_FORMAT \
40 	"#{?pane_format," \
41 		"#{?pane_marked,#[reverse],}" \
42 		"#{pane_current_command}#{?pane_active,*,}#{?pane_marked,M,}" \
43 		"#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \
44 	"," \
45 		"#{?window_format," \
46 			"#{?window_marked_flag,#[reverse],}" \
47 			"#{window_name}#{window_flags}" \
48 			"#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \
49 		"," \
50 			"#{session_windows} windows" \
51 			"#{?session_grouped, " \
52 				"(group #{session_group}: " \
53 				"#{session_group_list})," \
54 			"}" \
55 			"#{?session_attached, (attached),}" \
56 		"}" \
57 	"}"
58 
59 static const struct menu_item window_tree_menu_items[] = {
60 	{ "Select", '\r', NULL },
61 	{ "Expand", KEYC_RIGHT, NULL },
62 	{ "Mark", 'm', NULL },
63 	{ "", KEYC_NONE, NULL },
64 	{ "Tag", 't', NULL },
65 	{ "Tag All", '\024', NULL },
66 	{ "Tag None", 'T', NULL },
67 	{ "", KEYC_NONE, NULL },
68 	{ "Kill", 'x', NULL },
69 	{ "Kill Tagged", 'X', NULL },
70 	{ "", KEYC_NONE, NULL },
71 	{ "Cancel", 'q', NULL },
72 
73 	{ NULL, KEYC_NONE, NULL }
74 };
75 
76 const struct window_mode window_tree_mode = {
77 	.name = "tree-mode",
78 	.default_format = WINDOW_TREE_DEFAULT_FORMAT,
79 
80 	.init = window_tree_init,
81 	.free = window_tree_free,
82 	.resize = window_tree_resize,
83 	.update = window_tree_update,
84 	.key = window_tree_key,
85 };
86 
87 enum window_tree_sort_type {
88 	WINDOW_TREE_BY_INDEX,
89 	WINDOW_TREE_BY_NAME,
90 	WINDOW_TREE_BY_TIME,
91 };
92 static const char *window_tree_sort_list[] = {
93 	"index",
94 	"name",
95 	"time"
96 };
97 static struct mode_tree_sort_criteria *window_tree_sort;
98 
99 enum window_tree_type {
100 	WINDOW_TREE_NONE,
101 	WINDOW_TREE_SESSION,
102 	WINDOW_TREE_WINDOW,
103 	WINDOW_TREE_PANE,
104 };
105 
106 struct window_tree_itemdata {
107 	enum window_tree_type	type;
108 	int			session;
109 	int			winlink;
110 	int			pane;
111 };
112 
113 struct window_tree_modedata {
114 	struct window_pane		 *wp;
115 	int				  dead;
116 	int				  references;
117 
118 	struct mode_tree_data		 *data;
119 	char				 *format;
120 	char				 *command;
121 	int				  squash_groups;
122 
123 	struct window_tree_itemdata	**item_list;
124 	u_int				  item_size;
125 
126 	const char			 *entered;
127 
128 	struct cmd_find_state		  fs;
129 	enum window_tree_type		  type;
130 
131 	int				  offset;
132 
133 	int				  left;
134 	int				  right;
135 	u_int				  start;
136 	u_int				  end;
137 	u_int				  each;
138 };
139 
140 static void
141 window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
142     struct winlink **wlp, struct window_pane **wp)
143 {
144 	*wp = NULL;
145 	*wlp = NULL;
146 	*sp = session_find_by_id(item->session);
147 	if (*sp == NULL)
148 		return;
149 	if (item->type == WINDOW_TREE_SESSION) {
150 		*wlp = (*sp)->curw;
151 		*wp = (*wlp)->window->active;
152 		return;
153 	}
154 
155 	*wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
156 	if (*wlp == NULL) {
157 		*sp = NULL;
158 		return;
159 	}
160 	if (item->type == WINDOW_TREE_WINDOW) {
161 		*wp = (*wlp)->window->active;
162 		return;
163 	}
164 
165 	*wp = window_pane_find_by_id(item->pane);
166 	if (!window_has_pane((*wlp)->window, *wp))
167 		*wp = NULL;
168 	if (*wp == NULL) {
169 		*sp = NULL;
170 		*wlp = NULL;
171 		return;
172 	}
173 }
174 
175 static struct window_tree_itemdata *
176 window_tree_add_item(struct window_tree_modedata *data)
177 {
178 	struct window_tree_itemdata	*item;
179 
180 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
181 	    sizeof *data->item_list);
182 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
183 	return (item);
184 }
185 
186 static void
187 window_tree_free_item(struct window_tree_itemdata *item)
188 {
189 	free(item);
190 }
191 
192 static int
193 window_tree_cmp_session(const void *a0, const void *b0)
194 {
195 	const struct session *const	*a = a0;
196 	const struct session *const	*b = b0;
197 	const struct session		*sa = *a;
198 	const struct session		*sb = *b;
199 	int				 result = 0;
200 
201 	switch (window_tree_sort->field) {
202 	case WINDOW_TREE_BY_INDEX:
203 		result = sa->id - sb->id;
204 		break;
205 	case WINDOW_TREE_BY_TIME:
206 		if (timercmp(&sa->activity_time, &sb->activity_time, >)) {
207 			result = -1;
208 			break;
209 		}
210 		if (timercmp(&sa->activity_time, &sb->activity_time, <)) {
211 			result = 1;
212 			break;
213 		}
214 		/* FALLTHROUGH */
215 	case WINDOW_TREE_BY_NAME:
216 		result = strcmp(sa->name, sb->name);
217 		break;
218 	}
219 
220 	if (window_tree_sort->reversed)
221 		result = -result;
222 	return (result);
223 }
224 
225 static int
226 window_tree_cmp_window(const void *a0, const void *b0)
227 {
228 	const struct winlink *const	*a = a0;
229 	const struct winlink *const	*b = b0;
230 	const struct winlink		*wla = *a;
231 	const struct winlink		*wlb = *b;
232 	struct window			*wa = wla->window;
233 	struct window			*wb = wlb->window;
234 	int				 result = 0;
235 
236 	switch (window_tree_sort->field) {
237 	case WINDOW_TREE_BY_INDEX:
238 		result = wla->idx - wlb->idx;
239 		break;
240 	case WINDOW_TREE_BY_TIME:
241 		if (timercmp(&wa->activity_time, &wb->activity_time, >)) {
242 			result = -1;
243 			break;
244 		}
245 		if (timercmp(&wa->activity_time, &wb->activity_time, <)) {
246 			result = 1;
247 			break;
248 		}
249 		/* FALLTHROUGH */
250 	case WINDOW_TREE_BY_NAME:
251 		result = strcmp(wa->name, wb->name);
252 		break;
253 	}
254 
255 	if (window_tree_sort->reversed)
256 		result = -result;
257 	return (result);
258 }
259 
260 static int
261 window_tree_cmp_pane(const void *a0, const void *b0)
262 {
263 	const struct window_pane *const	*a = a0;
264 	const struct window_pane *const	*b = b0;
265 	int				 result;
266 
267 	if (window_tree_sort->field == WINDOW_TREE_BY_TIME)
268 		result = (*a)->active_point - (*b)->active_point;
269 	else {
270 		/*
271 		 * Panes don't have names, so use number order for any other
272 		 * sort field.
273 		 */
274 		result = (*a)->id - (*b)->id;
275 	}
276 	if (window_tree_sort->reversed)
277 		result = -result;
278 	return (result);
279 }
280 
281 static void
282 window_tree_build_pane(struct session *s, struct winlink *wl,
283     struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
284 {
285 	struct window_tree_modedata	*data = modedata;
286 	struct window_tree_itemdata	*item;
287 	char				*name, *text;
288 	u_int				 idx;
289 
290 	window_pane_index(wp, &idx);
291 
292 	item = window_tree_add_item(data);
293 	item->type = WINDOW_TREE_PANE;
294 	item->session = s->id;
295 	item->winlink = wl->idx;
296 	item->pane = wp->id;
297 
298 	text = format_single(NULL, data->format, NULL, s, wl, wp);
299 	xasprintf(&name, "%u", idx);
300 
301 	mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1);
302 	free(text);
303 	free(name);
304 }
305 
306 static int
307 window_tree_filter_pane(struct session *s, struct winlink *wl,
308     struct window_pane *wp, const char *filter)
309 {
310 	char	*cp;
311 	int	 result;
312 
313 	if (filter == NULL)
314 		return (1);
315 
316 	cp = format_single(NULL, filter, NULL, s, wl, wp);
317 	result = format_true(cp);
318 	free(cp);
319 
320 	return (result);
321 }
322 
323 static int
324 window_tree_build_window(struct session *s, struct winlink *wl,
325     void *modedata, struct mode_tree_sort_criteria *sort_crit,
326     struct mode_tree_item *parent, const char *filter)
327 {
328 	struct window_tree_modedata	*data = modedata;
329 	struct window_tree_itemdata	*item;
330 	struct mode_tree_item		*mti;
331 	char				*name, *text;
332 	struct window_pane		*wp, **l;
333 	u_int				 n, i;
334 	int				 expanded;
335 
336 	item = window_tree_add_item(data);
337 	item->type = WINDOW_TREE_WINDOW;
338 	item->session = s->id;
339 	item->winlink = wl->idx;
340 	item->pane = -1;
341 
342 	text = format_single(NULL, data->format, NULL, s, wl, NULL);
343 	xasprintf(&name, "%u", wl->idx);
344 
345 	if (data->type == WINDOW_TREE_SESSION ||
346 	    data->type == WINDOW_TREE_WINDOW)
347 		expanded = 0;
348 	else
349 		expanded = 1;
350 	mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text,
351 	    expanded);
352 	free(text);
353 	free(name);
354 
355 	if ((wp = TAILQ_FIRST(&wl->window->panes)) == NULL)
356 		goto empty;
357 	if (TAILQ_NEXT(wp, entry) == NULL) {
358 		if (!window_tree_filter_pane(s, wl, wp, filter))
359 			goto empty;
360 		return (1);
361 	}
362 
363 	l = NULL;
364 	n = 0;
365 
366 	TAILQ_FOREACH(wp, &wl->window->panes, entry) {
367 		if (!window_tree_filter_pane(s, wl, wp, filter))
368 			continue;
369 		l = xreallocarray(l, n + 1, sizeof *l);
370 		l[n++] = wp;
371 	}
372 	if (n == 0)
373 		goto empty;
374 
375 	window_tree_sort = sort_crit;
376 	qsort(l, n, sizeof *l, window_tree_cmp_pane);
377 
378 	for (i = 0; i < n; i++)
379 		window_tree_build_pane(s, wl, l[i], modedata, mti);
380 	free(l);
381 	return (1);
382 
383 empty:
384 	window_tree_free_item(item);
385 	data->item_size--;
386 	mode_tree_remove(data->data, mti);
387 	return (0);
388 }
389 
390 static void
391 window_tree_build_session(struct session *s, void *modedata,
392     struct mode_tree_sort_criteria *sort_crit, const char *filter)
393 {
394 	struct window_tree_modedata	*data = modedata;
395 	struct window_tree_itemdata	*item;
396 	struct mode_tree_item		*mti;
397 	char				*text;
398 	struct winlink			*wl, **l;
399 	u_int				 n, i, empty;
400 	int				 expanded;
401 
402 	item = window_tree_add_item(data);
403 	item->type = WINDOW_TREE_SESSION;
404 	item->session = s->id;
405 	item->winlink = -1;
406 	item->pane = -1;
407 
408 	text = format_single(NULL, data->format, NULL, s, NULL, NULL);
409 
410 	if (data->type == WINDOW_TREE_SESSION)
411 		expanded = 0;
412 	else
413 		expanded = 1;
414 	mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
415 	    expanded);
416 	free(text);
417 
418 	l = NULL;
419 	n = 0;
420 	RB_FOREACH(wl, winlinks, &s->windows) {
421 		l = xreallocarray(l, n + 1, sizeof *l);
422 		l[n++] = wl;
423 	}
424 	window_tree_sort = sort_crit;
425 	qsort(l, n, sizeof *l, window_tree_cmp_window);
426 
427 	empty = 0;
428 	for (i = 0; i < n; i++) {
429 		if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti,
430 		    filter))
431 			empty++;
432 	}
433 	if (empty == n) {
434 		window_tree_free_item(item);
435 		data->item_size--;
436 		mode_tree_remove(data->data, mti);
437 	}
438 	free(l);
439 }
440 
441 static void
442 window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
443     uint64_t *tag, const char *filter)
444 {
445 	struct window_tree_modedata	*data = modedata;
446 	struct session			*s, **l;
447 	struct session_group		*sg, *current;
448 	u_int				 n, i;
449 
450 	current = session_group_contains(data->fs.s);
451 
452 	for (i = 0; i < data->item_size; i++)
453 		window_tree_free_item(data->item_list[i]);
454 	free(data->item_list);
455 	data->item_list = NULL;
456 	data->item_size = 0;
457 
458 	l = NULL;
459 	n = 0;
460 	RB_FOREACH(s, sessions, &sessions) {
461 		if (data->squash_groups &&
462 		    (sg = session_group_contains(s)) != NULL) {
463 			if ((sg == current && s != data->fs.s) ||
464 			    (sg != current && s != TAILQ_FIRST(&sg->sessions)))
465 				continue;
466 		}
467 		l = xreallocarray(l, n + 1, sizeof *l);
468 		l[n++] = s;
469 	}
470 	window_tree_sort = sort_crit;
471 	qsort(l, n, sizeof *l, window_tree_cmp_session);
472 
473 	for (i = 0; i < n; i++)
474 		window_tree_build_session(l[i], modedata, sort_crit, filter);
475 	free(l);
476 
477 	switch (data->type) {
478 	case WINDOW_TREE_NONE:
479 		break;
480 	case WINDOW_TREE_SESSION:
481 		*tag = (uint64_t)data->fs.s;
482 		break;
483 	case WINDOW_TREE_WINDOW:
484 		*tag = (uint64_t)data->fs.wl;
485 		break;
486 	case WINDOW_TREE_PANE:
487 		if (window_count_panes(data->fs.wl->window) == 1)
488 			*tag = (uint64_t)data->fs.wl;
489 		else
490 			*tag = (uint64_t)data->fs.wp;
491 		break;
492 	}
493 }
494 
495 static void
496 window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
497     u_int sx, u_int sy, const struct grid_cell *gc, const char *label)
498 {
499 	size_t	 len;
500 	u_int	 ox, oy;
501 
502 	len = strlen(label);
503 	if (sx == 0 || sy == 1 || len > sx)
504 		return;
505 	ox = (sx - len + 1) / 2;
506 	oy = (sy + 1) / 2;
507 
508 	if (ox > 1 && ox + len < sx - 1 && sy >= 3) {
509 		screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0);
510 		screen_write_box(ctx, len + 2, 3);
511 	}
512 	screen_write_cursormove(ctx, px + ox, py + oy, 0);
513 	screen_write_puts(ctx, gc, "%s", label);
514 }
515 
516 static void
517 window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
518     struct screen_write_ctx *ctx, u_int sx, u_int sy)
519 {
520 	struct options		*oo = s->options;
521 	struct winlink		*wl;
522 	struct window		*w;
523 	u_int			 cx = ctx->s->cx, cy = ctx->s->cy;
524 	u_int			 loop, total, visible, each, width, offset;
525 	u_int			 current, start, end, remaining, i;
526 	struct grid_cell	 gc;
527 	int			 colour, active_colour, left, right;
528 	char			*label;
529 
530 	total = winlink_count(&s->windows);
531 
532 	memcpy(&gc, &grid_default_cell, sizeof gc);
533 	colour = options_get_number(oo, "display-panes-colour");
534 	active_colour = options_get_number(oo, "display-panes-active-colour");
535 
536 	if (sx / total < 24) {
537 		visible = sx / 24;
538 		if (visible == 0)
539 			visible = 1;
540 	} else
541 		visible = total;
542 
543 	current = 0;
544 	RB_FOREACH(wl, winlinks, &s->windows) {
545 		if (wl == s->curw)
546 			break;
547 		current++;
548 	}
549 
550 	if (current < visible) {
551 		start = 0;
552 		end = visible;
553 	} else if (current >= total - visible) {
554 		start = total - visible;
555 		end = total;
556 	} else {
557 		start = current - (visible / 2);
558 		end = start + visible;
559 	}
560 
561 	if (data->offset < -(int)start)
562 		data->offset = -(int)start;
563 	if (data->offset > (int)(total - end))
564 		data->offset = (int)(total - end);
565 	start += data->offset;
566 	end += data->offset;
567 
568 	left = (start != 0);
569 	right = (end != total);
570 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
571 		left = right = 0;
572 	if (left && right) {
573 		each = (sx - 6) / visible;
574 		remaining = (sx - 6) - (visible * each);
575 	} else if (left || right) {
576 		each = (sx - 3) / visible;
577 		remaining = (sx - 3) - (visible * each);
578 	} else {
579 		each = sx / visible;
580 		remaining = sx - (visible * each);
581 	}
582 	if (each == 0)
583 		return;
584 
585 	if (left) {
586 		data->left = cx + 2;
587 		screen_write_cursormove(ctx, cx + 2, cy, 0);
588 		screen_write_vline(ctx, sy, 0, 0);
589 		screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
590 		screen_write_puts(ctx, &grid_default_cell, "<");
591 	} else
592 		data->left = -1;
593 	if (right) {
594 		data->right = cx + sx - 3;
595 		screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
596 		screen_write_vline(ctx, sy, 0, 0);
597 		screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
598 		screen_write_puts(ctx, &grid_default_cell, ">");
599 	} else
600 		data->right = -1;
601 
602 	data->start = start;
603 	data->end = end;
604 	data->each = each;
605 
606 	i = loop = 0;
607 	RB_FOREACH(wl, winlinks, &s->windows) {
608 		if (loop == end)
609 			break;
610 		if (loop < start) {
611 			loop++;
612 			continue;
613 		}
614 		w = wl->window;
615 
616 		if (wl == s->curw)
617 			gc.fg = active_colour;
618 		else
619 			gc.fg = colour;
620 
621 		if (left)
622 			offset = 3 + (i * each);
623 		else
624 			offset = (i * each);
625 		if (loop == end - 1)
626 			width = each + remaining;
627 		else
628 			width = each - 1;
629 
630 		screen_write_cursormove(ctx, cx + offset, cy, 0);
631 		screen_write_preview(ctx, &w->active->base, width, sy);
632 
633 		xasprintf(&label, " %u:%s ", wl->idx, w->name);
634 		if (strlen(label) > width)
635 			xasprintf(&label, " %u ", wl->idx);
636 		window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc,
637 		    label);
638 		free(label);
639 
640 		if (loop != end - 1) {
641 			screen_write_cursormove(ctx, cx + offset + width, cy, 0);
642 			screen_write_vline(ctx, sy, 0, 0);
643 		}
644 		loop++;
645 
646 		i++;
647 	}
648 }
649 
650 static void
651 window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
652     struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
653 {
654 	struct options		*oo = s->options;
655 	struct window_pane	*wp;
656 	u_int			 cx = ctx->s->cx, cy = ctx->s->cy;
657 	u_int			 loop, total, visible, each, width, offset;
658 	u_int			 current, start, end, remaining, i;
659 	struct grid_cell	 gc;
660 	int			 colour, active_colour, left, right, pane_idx;
661 	char			*label;
662 
663 	total = window_count_panes(w);
664 
665 	memcpy(&gc, &grid_default_cell, sizeof gc);
666 	colour = options_get_number(oo, "display-panes-colour");
667 	active_colour = options_get_number(oo, "display-panes-active-colour");
668 
669 	if (sx / total < 24) {
670 		visible = sx / 24;
671 		if (visible == 0)
672 			visible = 1;
673 	} else
674 		visible = total;
675 
676 	current = 0;
677 	TAILQ_FOREACH(wp, &w->panes, entry) {
678 		if (wp == w->active)
679 			break;
680 		current++;
681 	}
682 
683 	if (current < visible) {
684 		start = 0;
685 		end = visible;
686 	} else if (current >= total - visible) {
687 		start = total - visible;
688 		end = total;
689 	} else {
690 		start = current - (visible / 2);
691 		end = start + visible;
692 	}
693 
694 	if (data->offset < -(int)start)
695 		data->offset = -(int)start;
696 	if (data->offset > (int)(total - end))
697 		data->offset = (int)(total - end);
698 	start += data->offset;
699 	end += data->offset;
700 
701 	left = (start != 0);
702 	right = (end != total);
703 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
704 		left = right = 0;
705 	if (left && right) {
706 		each = (sx - 6) / visible;
707 		remaining = (sx - 6) - (visible * each);
708 	} else if (left || right) {
709 		each = (sx - 3) / visible;
710 		remaining = (sx - 3) - (visible * each);
711 	} else {
712 		each = sx / visible;
713 		remaining = sx - (visible * each);
714 	}
715 	if (each == 0)
716 		return;
717 
718 	if (left) {
719 		data->left = cx + 2;
720 		screen_write_cursormove(ctx, cx + 2, cy, 0);
721 		screen_write_vline(ctx, sy, 0, 0);
722 		screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
723 		screen_write_puts(ctx, &grid_default_cell, "<");
724 	} else
725 		data->left = -1;
726 	if (right) {
727 		data->right = cx + sx - 3;
728 		screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
729 		screen_write_vline(ctx, sy, 0, 0);
730 		screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
731 		screen_write_puts(ctx, &grid_default_cell, ">");
732 	} else
733 		data->right = -1;
734 
735 	data->start = start;
736 	data->end = end;
737 	data->each = each;
738 
739 	i = loop = 0;
740 	TAILQ_FOREACH(wp, &w->panes, entry) {
741 		if (loop == end)
742 			break;
743 		if (loop < start) {
744 			loop++;
745 			continue;
746 		}
747 
748 		if (wp == w->active)
749 			gc.fg = active_colour;
750 		else
751 			gc.fg = colour;
752 
753 		if (left)
754 			offset = 3 + (i * each);
755 		else
756 			offset = (i * each);
757 		if (loop == end - 1)
758 			width = each + remaining;
759 		else
760 			width = each - 1;
761 
762 		screen_write_cursormove(ctx, cx + offset, cy, 0);
763 		screen_write_preview(ctx, &wp->base, width, sy);
764 
765 		if (window_pane_index(wp, &pane_idx) != 0)
766 			pane_idx = loop;
767 		xasprintf(&label, " %u ", pane_idx);
768 		window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc,
769 		    label);
770 		free(label);
771 
772 		if (loop != end - 1) {
773 			screen_write_cursormove(ctx, cx + offset + width, cy, 0);
774 			screen_write_vline(ctx, sy, 0, 0);
775 		}
776 		loop++;
777 
778 		i++;
779 	}
780 }
781 
782 static void
783 window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
784     u_int sx, u_int sy)
785 {
786 	struct window_tree_itemdata	*item = itemdata;
787 	struct session			*sp;
788 	struct winlink			*wlp;
789 	struct window_pane		*wp;
790 
791 	window_tree_pull_item(item, &sp, &wlp, &wp);
792 	if (wp == NULL)
793 		return;
794 
795 	switch (item->type) {
796 	case WINDOW_TREE_NONE:
797 		break;
798 	case WINDOW_TREE_SESSION:
799 		window_tree_draw_session(modedata, sp, ctx, sx, sy);
800 		break;
801 	case WINDOW_TREE_WINDOW:
802 		window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy);
803 		break;
804 	case WINDOW_TREE_PANE:
805 		screen_write_preview(ctx, &wp->base, sx, sy);
806 		break;
807 	}
808 }
809 
810 static int
811 window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
812 {
813 	struct window_tree_itemdata	*item = itemdata;
814 	struct session			*s;
815 	struct winlink			*wl;
816 	struct window_pane		*wp;
817 	char				*cmd;
818 	int				 retval;
819 
820 	window_tree_pull_item(item, &s, &wl, &wp);
821 
822 	switch (item->type) {
823 	case WINDOW_TREE_NONE:
824 		return (0);
825 	case WINDOW_TREE_SESSION:
826 		if (s == NULL)
827 			return (0);
828 		return (strstr(s->name, ss) != NULL);
829 	case WINDOW_TREE_WINDOW:
830 		if (s == NULL || wl == NULL)
831 			return (0);
832 		return (strstr(wl->window->name, ss) != NULL);
833 	case WINDOW_TREE_PANE:
834 		if (s == NULL || wl == NULL || wp == NULL)
835 			break;
836 		cmd = get_proc_name(wp->fd, wp->tty);
837 		if (cmd == NULL || *cmd == '\0')
838 			return (0);
839 		retval = (strstr(cmd, ss) != NULL);
840 		free(cmd);
841 		return (retval);
842 	}
843 	return (0);
844 }
845 
846 static void
847 window_tree_menu(void *modedata, struct client *c, key_code key)
848 {
849 	struct window_tree_modedata	*data = modedata;
850 	struct window_pane		*wp = data->wp;
851 	struct window_mode_entry	*wme;
852 
853 	wme = TAILQ_FIRST(&wp->modes);
854 	if (wme == NULL || wme->data != modedata)
855 		return;
856 	window_tree_key(wme, c, NULL, NULL, key, NULL);
857 }
858 
859 static struct screen *
860 window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
861     struct args *args)
862 {
863 	struct window_pane		*wp = wme->wp;
864 	struct window_tree_modedata	*data;
865 	struct screen			*s;
866 
867 	wme->data = data = xcalloc(1, sizeof *data);
868 	data->wp = wp;
869 	data->references = 1;
870 
871 	if (args_has(args, 's'))
872 		data->type = WINDOW_TREE_SESSION;
873 	else if (args_has(args, 'w'))
874 		data->type = WINDOW_TREE_WINDOW;
875 	else
876 		data->type = WINDOW_TREE_PANE;
877 	memcpy(&data->fs, fs, sizeof data->fs);
878 
879 	if (args == NULL || !args_has(args, 'F'))
880 		data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
881 	else
882 		data->format = xstrdup(args_get(args, 'F'));
883 	if (args == NULL || args->argc == 0)
884 		data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
885 	else
886 		data->command = xstrdup(args->argv[0]);
887 	data->squash_groups = !args_has(args, 'G');
888 
889 	data->data = mode_tree_start(wp, args, window_tree_build,
890 	    window_tree_draw, window_tree_search, window_tree_menu, NULL, data,
891 	    window_tree_menu_items, window_tree_sort_list,
892 	    nitems(window_tree_sort_list), &s);
893 	mode_tree_zoom(data->data, args);
894 
895 	mode_tree_build(data->data);
896 	mode_tree_draw(data->data);
897 
898 	data->type = WINDOW_TREE_NONE;
899 
900 	return (s);
901 }
902 
903 static void
904 window_tree_destroy(struct window_tree_modedata *data)
905 {
906 	u_int	i;
907 
908 	if (--data->references != 0)
909 		return;
910 
911 	for (i = 0; i < data->item_size; i++)
912 		window_tree_free_item(data->item_list[i]);
913 	free(data->item_list);
914 
915 	free(data->format);
916 	free(data->command);
917 
918 	free(data);
919 }
920 
921 static void
922 window_tree_free(struct window_mode_entry *wme)
923 {
924 	struct window_tree_modedata *data = wme->data;
925 
926 	if (data == NULL)
927 		return;
928 
929 	data->dead = 1;
930 	mode_tree_free(data->data);
931 	window_tree_destroy(data);
932 }
933 
934 static void
935 window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
936 {
937 	struct window_tree_modedata	*data = wme->data;
938 
939 	mode_tree_resize(data->data, sx, sy);
940 }
941 
942 static void
943 window_tree_update(struct window_mode_entry *wme)
944 {
945 	struct window_tree_modedata	*data = wme->data;
946 
947 	mode_tree_build(data->data);
948 	mode_tree_draw(data->data);
949 	data->wp->flags |= PANE_REDRAW;
950 }
951 
952 static char *
953 window_tree_get_target(struct window_tree_itemdata *item,
954     struct cmd_find_state *fs)
955 {
956 	struct session		*s;
957 	struct winlink		*wl;
958 	struct window_pane	*wp;
959 	char			*target;
960 
961 	window_tree_pull_item(item, &s, &wl, &wp);
962 
963 	target = NULL;
964 	switch (item->type) {
965 	case WINDOW_TREE_NONE:
966 		break;
967 	case WINDOW_TREE_SESSION:
968 		if (s == NULL)
969 			break;
970 		xasprintf(&target, "=%s:", s->name);
971 		break;
972 	case WINDOW_TREE_WINDOW:
973 		if (s == NULL || wl == NULL)
974 			break;
975 		xasprintf(&target, "=%s:%u.", s->name, wl->idx);
976 		break;
977 	case WINDOW_TREE_PANE:
978 		if (s == NULL || wl == NULL || wp == NULL)
979 			break;
980 		xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
981 		break;
982 	}
983 	if (target == NULL)
984 		cmd_find_clear_state(fs, 0);
985 	else
986 		cmd_find_from_winlink_pane(fs, wl, wp, 0);
987 	return (target);
988 }
989 
990 static void
991 window_tree_command_each(void *modedata, void *itemdata, struct client *c,
992     __unused key_code key)
993 {
994 	struct window_tree_modedata	*data = modedata;
995 	struct window_tree_itemdata	*item = itemdata;
996 	char				*name;
997 	struct cmd_find_state		 fs;
998 
999 	name = window_tree_get_target(item, &fs);
1000 	if (name != NULL)
1001 		mode_tree_run_command(c, &fs, data->entered, name);
1002 	free(name);
1003 }
1004 
1005 static enum cmd_retval
1006 window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
1007 {
1008 	struct window_tree_modedata	*data = modedata;
1009 
1010 	if (!data->dead) {
1011 		mode_tree_build(data->data);
1012 		mode_tree_draw(data->data);
1013 		data->wp->flags |= PANE_REDRAW;
1014 	}
1015 	window_tree_destroy(data);
1016 	return (CMD_RETURN_NORMAL);
1017 }
1018 
1019 static int
1020 window_tree_command_callback(struct client *c, void *modedata, const char *s,
1021     __unused int done)
1022 {
1023 	struct window_tree_modedata	*data = modedata;
1024 
1025 	if (s == NULL || *s == '\0' || data->dead)
1026 		return (0);
1027 
1028 	data->entered = s;
1029 	mode_tree_each_tagged(data->data, window_tree_command_each, c,
1030 	    KEYC_NONE, 1);
1031 	data->entered = NULL;
1032 
1033 	data->references++;
1034 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1035 
1036 	return (0);
1037 }
1038 
1039 static void
1040 window_tree_command_free(void *modedata)
1041 {
1042 	struct window_tree_modedata	*data = modedata;
1043 
1044 	window_tree_destroy(data);
1045 }
1046 
1047 static void
1048 window_tree_kill_each(__unused void *modedata, void *itemdata,
1049     __unused struct client *c, __unused key_code key)
1050 {
1051 	struct window_tree_itemdata	*item = itemdata;
1052 	struct session			*s;
1053 	struct winlink			*wl;
1054 	struct window_pane		*wp;
1055 
1056 	window_tree_pull_item(item, &s, &wl, &wp);
1057 
1058 	switch (item->type) {
1059 	case WINDOW_TREE_NONE:
1060 		break;
1061 	case WINDOW_TREE_SESSION:
1062 		if (s != NULL) {
1063 			server_destroy_session(s);
1064 			session_destroy(s, 1, __func__);
1065 		}
1066 		break;
1067 	case WINDOW_TREE_WINDOW:
1068 		if (wl != NULL)
1069 			server_kill_window(wl->window, 0);
1070 		break;
1071 	case WINDOW_TREE_PANE:
1072 		if (wp != NULL)
1073 			server_kill_pane(wp);
1074 		break;
1075 	}
1076 }
1077 
1078 static int
1079 window_tree_kill_current_callback(struct client *c, void *modedata,
1080     const char *s, __unused int done)
1081 {
1082 	struct window_tree_modedata	*data = modedata;
1083 	struct mode_tree_data		*mtd = data->data;
1084 
1085 	if (s == NULL || *s == '\0' || data->dead)
1086 		return (0);
1087 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
1088 		return (0);
1089 
1090 	window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE);
1091 	server_renumber_all();
1092 
1093 	data->references++;
1094 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1095 
1096 	return (0);
1097 }
1098 
1099 static int
1100 window_tree_kill_tagged_callback(struct client *c, void *modedata,
1101     const char *s, __unused int done)
1102 {
1103 	struct window_tree_modedata	*data = modedata;
1104 	struct mode_tree_data		*mtd = data->data;
1105 
1106 	if (s == NULL || *s == '\0' || data->dead)
1107 		return (0);
1108 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
1109 		return (0);
1110 
1111 	mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1);
1112 	server_renumber_all();
1113 
1114 	data->references++;
1115 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1116 
1117 	return (0);
1118 }
1119 
1120 static key_code
1121 window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x,
1122     struct window_tree_itemdata *item)
1123 {
1124 	struct session		*s;
1125 	struct winlink		*wl;
1126 	struct window_pane	*wp;
1127 	u_int			 loop;
1128 
1129 	if (key != KEYC_MOUSEDOWN1_PANE)
1130 		return (KEYC_NONE);
1131 
1132 	if (data->left != -1 && x <= (u_int)data->left)
1133 		return ('<');
1134 	if (data->right != -1 && x >= (u_int)data->right)
1135 		return ('>');
1136 
1137 	if (data->left != -1)
1138 		x -= data->left;
1139 	else if (x != 0)
1140 		x--;
1141 	if (x == 0 || data->end == 0)
1142 		x = 0;
1143 	else {
1144 		x = x / data->each;
1145 		if (data->start + x >= data->end)
1146 			x = data->end - 1;
1147 	}
1148 
1149 	window_tree_pull_item(item, &s, &wl, &wp);
1150 	if (item->type == WINDOW_TREE_SESSION) {
1151 		if (s == NULL)
1152 			return (KEYC_NONE);
1153 		mode_tree_expand_current(data->data);
1154 		loop = 0;
1155 		RB_FOREACH(wl, winlinks, &s->windows) {
1156 			if (loop == data->start + x)
1157 				break;
1158 			loop++;
1159 		}
1160 		if (wl != NULL)
1161 			mode_tree_set_current(data->data, (uint64_t)wl);
1162 		return ('\r');
1163 	}
1164 	if (item->type == WINDOW_TREE_WINDOW) {
1165 		if (wl == NULL)
1166 			return (KEYC_NONE);
1167 		mode_tree_expand_current(data->data);
1168 		loop = 0;
1169 		TAILQ_FOREACH(wp, &wl->window->panes, entry) {
1170 			if (loop == data->start + x)
1171 				break;
1172 			loop++;
1173 		}
1174 		if (wp != NULL)
1175 			mode_tree_set_current(data->data, (uint64_t)wp);
1176 		return ('\r');
1177 	}
1178 	return (KEYC_NONE);
1179 }
1180 
1181 static void
1182 window_tree_key(struct window_mode_entry *wme, struct client *c,
1183     __unused struct session *s, __unused struct winlink *wl, key_code key,
1184     struct mouse_event *m)
1185 {
1186 	struct window_pane		*wp = wme->wp;
1187 	struct window_tree_modedata	*data = wme->data;
1188 	struct window_tree_itemdata	*item, *new_item;
1189 	char				*name, *prompt = NULL;
1190 	struct cmd_find_state		 fs, *fsp = &data->fs;
1191 	int				 finished;
1192 	u_int				 tagged, x, y, idx;
1193 	struct session			*ns;
1194 	struct winlink			*nwl;
1195 	struct window_pane		*nwp;
1196 
1197 	item = mode_tree_get_current(data->data);
1198 	finished = mode_tree_key(data->data, c, &key, m, &x, &y);
1199 	if (item != (new_item = mode_tree_get_current(data->data))) {
1200 		item = new_item;
1201 		data->offset = 0;
1202 	}
1203 	if (KEYC_IS_MOUSE(key) && m != NULL)
1204 		key = window_tree_mouse(data, key, x, item);
1205 	switch (key) {
1206 	case '<':
1207 		data->offset--;
1208 		break;
1209 	case '>':
1210 		data->offset++;
1211 		break;
1212 	case 'H':
1213 		mode_tree_expand(data->data, (uint64_t)fsp->s);
1214 		mode_tree_expand(data->data, (uint64_t)fsp->wl);
1215 		if (!mode_tree_set_current(data->data, (uint64_t)wme->wp))
1216 			mode_tree_set_current(data->data, (uint64_t)fsp->wl);
1217 		break;
1218 	case 'm':
1219 		window_tree_pull_item(item, &ns, &nwl, &nwp);
1220 		server_set_marked(ns, nwl, nwp);
1221 		mode_tree_build(data->data);
1222 		break;
1223 	case 'M':
1224 		server_clear_marked();
1225 		mode_tree_build(data->data);
1226 		break;
1227 	case 'x':
1228 		window_tree_pull_item(item, &ns, &nwl, &nwp);
1229 		switch (item->type) {
1230 		case WINDOW_TREE_NONE:
1231 			break;
1232 		case WINDOW_TREE_SESSION:
1233 			if (ns == NULL)
1234 				break;
1235 			xasprintf(&prompt, "Kill session %s? ", ns->name);
1236 			break;
1237 		case WINDOW_TREE_WINDOW:
1238 			if (nwl == NULL)
1239 				break;
1240 			xasprintf(&prompt, "Kill window %u? ", nwl->idx);
1241 			break;
1242 		case WINDOW_TREE_PANE:
1243 			if (nwp == NULL || window_pane_index(nwp, &idx) != 0)
1244 				break;
1245 			xasprintf(&prompt, "Kill pane %u? ", idx);
1246 			break;
1247 		}
1248 		if (prompt == NULL)
1249 			break;
1250 		data->references++;
1251 		status_prompt_set(c, NULL, prompt, "",
1252 		    window_tree_kill_current_callback, window_tree_command_free,
1253 		    data, PROMPT_SINGLE|PROMPT_NOFORMAT);
1254 		free(prompt);
1255 		break;
1256 	case 'X':
1257 		tagged = mode_tree_count_tagged(data->data);
1258 		if (tagged == 0)
1259 			break;
1260 		xasprintf(&prompt, "Kill %u tagged? ", tagged);
1261 		data->references++;
1262 		status_prompt_set(c, NULL, prompt, "",
1263 		    window_tree_kill_tagged_callback, window_tree_command_free,
1264 		    data, PROMPT_SINGLE|PROMPT_NOFORMAT);
1265 		free(prompt);
1266 		break;
1267 	case ':':
1268 		tagged = mode_tree_count_tagged(data->data);
1269 		if (tagged != 0)
1270 			xasprintf(&prompt, "(%u tagged) ", tagged);
1271 		else
1272 			xasprintf(&prompt, "(current) ");
1273 		data->references++;
1274 		status_prompt_set(c, NULL, prompt, "",
1275 		    window_tree_command_callback, window_tree_command_free,
1276 		    data, PROMPT_NOFORMAT);
1277 		free(prompt);
1278 		break;
1279 	case '\r':
1280 		name = window_tree_get_target(item, &fs);
1281 		if (name != NULL)
1282 			mode_tree_run_command(c, NULL, data->command, name);
1283 		finished = 1;
1284 		free(name);
1285 		break;
1286 	}
1287 	if (finished)
1288 		window_pane_reset_mode(wp);
1289 	else {
1290 		mode_tree_draw(data->data);
1291 		wp->flags |= PANE_REDRAW;
1292 	}
1293 }
1294