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