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