xref: /openbsd-src/usr.bin/tmux/window-tree.c (revision c90a81c56dcebd6a1b73fe4aff9b03385b8e63b3)
1 /* $OpenBSD: window-tree.c,v 1.32 2018/12/18 13:20: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 *,
33 			     struct winlink *, key_code, 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 static void
465 window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
466     u_int sx, u_int sy, const struct grid_cell *gc, const char *label)
467 {
468 	size_t	 len;
469 	u_int	 ox, oy;
470 
471 	len = strlen(label);
472 	if (sx == 0 || sy == 1 || len > sx)
473 		return;
474 	ox = (sx - len + 1) / 2;
475 	oy = (sy + 1) / 2;
476 
477 	if (ox > 1 && ox + len < sx - 1 && sy >= 3) {
478 		screen_write_cursormove(ctx, px + ox - 1, py + oy - 1);
479 		screen_write_box(ctx, len + 2, 3);
480 	}
481 	screen_write_cursormove(ctx, px + ox, py + oy);
482 	screen_write_puts(ctx, gc, "%s", label);
483 }
484 
485 static void
486 window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
487     struct screen_write_ctx *ctx, u_int sx, u_int sy)
488 {
489 	struct options		*oo = s->options;
490 	struct winlink		*wl;
491 	struct window		*w;
492 	u_int			 cx = ctx->s->cx, cy = ctx->s->cy;
493 	u_int			 loop, total, visible, each, width, offset;
494 	u_int			 current, start, end, remaining, i;
495 	struct grid_cell	 gc;
496 	int			 colour, active_colour, left, right;
497 	char			*label;
498 
499 	total = winlink_count(&s->windows);
500 
501 	memcpy(&gc, &grid_default_cell, sizeof gc);
502 	colour = options_get_number(oo, "display-panes-colour");
503 	active_colour = options_get_number(oo, "display-panes-active-colour");
504 
505 	if (sx / total < 24) {
506 		visible = sx / 24;
507 		if (visible == 0)
508 			visible = 1;
509 	} else
510 		visible = total;
511 
512 	current = 0;
513 	RB_FOREACH(wl, winlinks, &s->windows) {
514 		if (wl == s->curw)
515 			break;
516 		current++;
517 	}
518 
519 	if (current < visible) {
520 		start = 0;
521 		end = visible;
522 	} else if (current >= total - visible) {
523 		start = total - visible;
524 		end = total;
525 	} else {
526 		start = current - (visible / 2);
527 		end = start + visible;
528 	}
529 
530 	if (data->offset < -(int)start)
531 		data->offset = -(int)start;
532 	if (data->offset > (int)(total - end))
533 		data->offset = (int)(total - end);
534 	start += data->offset;
535 	end += data->offset;
536 
537 	left = (start != 0);
538 	right = (end != total);
539 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
540 		left = right = 0;
541 	if (left && right) {
542 		each = (sx - 6) / visible;
543 		remaining = (sx - 6) - (visible * each);
544 	} else if (left || right) {
545 		each = (sx - 3) / visible;
546 		remaining = (sx - 3) - (visible * each);
547 	} else {
548 		each = sx / visible;
549 		remaining = sx - (visible * each);
550 	}
551 	if (each == 0)
552 		return;
553 
554 	if (left) {
555 		data->left = cx + 2;
556 		screen_write_cursormove(ctx, cx + 2, cy);
557 		screen_write_vline(ctx, sy, 0, 0);
558 		screen_write_cursormove(ctx, cx, cy + sy / 2);
559 		screen_write_puts(ctx, &grid_default_cell, "<");
560 	} else
561 		data->left = -1;
562 	if (right) {
563 		data->right = cx + sx - 3;
564 		screen_write_cursormove(ctx, cx + sx - 3, cy);
565 		screen_write_vline(ctx, sy, 0, 0);
566 		screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2);
567 		screen_write_puts(ctx, &grid_default_cell, ">");
568 	} else
569 		data->right = -1;
570 
571 	data->start = start;
572 	data->end = end;
573 	data->each = each;
574 
575 	i = loop = 0;
576 	RB_FOREACH(wl, winlinks, &s->windows) {
577 		if (loop == end)
578 			break;
579 		if (loop < start) {
580 			loop++;
581 			continue;
582 		}
583 		w = wl->window;
584 
585 		if (wl == s->curw)
586 			gc.fg = active_colour;
587 		else
588 			gc.fg = colour;
589 
590 		if (left)
591 			offset = 3 + (i * each);
592 		else
593 			offset = (i * each);
594 		if (loop == end - 1)
595 			width = each + remaining;
596 		else
597 			width = each - 1;
598 
599 		screen_write_cursormove(ctx, cx + offset, cy);
600 		screen_write_preview(ctx, &w->active->base, width, sy);
601 
602 		xasprintf(&label, " %u:%s ", wl->idx, w->name);
603 		if (strlen(label) > width)
604 			xasprintf(&label, " %u ", wl->idx);
605 		window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc,
606 		    label);
607 		free(label);
608 
609 		if (loop != end - 1) {
610 			screen_write_cursormove(ctx, cx + offset + width, cy);
611 			screen_write_vline(ctx, sy, 0, 0);
612 		}
613 		loop++;
614 
615 		i++;
616 	}
617 }
618 
619 static void
620 window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
621     struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
622 {
623 	struct options		*oo = s->options;
624 	struct window_pane	*wp;
625 	u_int			 cx = ctx->s->cx, cy = ctx->s->cy;
626 	u_int			 loop, total, visible, each, width, offset;
627 	u_int			 current, start, end, remaining, i;
628 	struct grid_cell	 gc;
629 	int			 colour, active_colour, left, right, pane_idx;
630 	char			*label;
631 
632 	total = window_count_panes(w);
633 
634 	memcpy(&gc, &grid_default_cell, sizeof gc);
635 	colour = options_get_number(oo, "display-panes-colour");
636 	active_colour = options_get_number(oo, "display-panes-active-colour");
637 
638 	if (sx / total < 24) {
639 		visible = sx / 24;
640 		if (visible == 0)
641 			visible = 1;
642 	} else
643 		visible = total;
644 
645 	current = 0;
646 	TAILQ_FOREACH(wp, &w->panes, entry) {
647 		if (wp == w->active)
648 			break;
649 		current++;
650 	}
651 
652 	if (current < visible) {
653 		start = 0;
654 		end = visible;
655 	} else if (current >= total - visible) {
656 		start = total - visible;
657 		end = total;
658 	} else {
659 		start = current - (visible / 2);
660 		end = start + visible;
661 	}
662 
663 	if (data->offset < -(int)start)
664 		data->offset = -(int)start;
665 	if (data->offset > (int)(total - end))
666 		data->offset = (int)(total - end);
667 	start += data->offset;
668 	end += data->offset;
669 
670 	left = (start != 0);
671 	right = (end != total);
672 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
673 		left = right = 0;
674 	if (left && right) {
675 		each = (sx - 6) / visible;
676 		remaining = (sx - 6) - (visible * each);
677 	} else if (left || right) {
678 		each = (sx - 3) / visible;
679 		remaining = (sx - 3) - (visible * each);
680 	} else {
681 		each = sx / visible;
682 		remaining = sx - (visible * each);
683 	}
684 	if (each == 0)
685 		return;
686 
687 	if (left) {
688 		data->left = cx + 2;
689 		screen_write_cursormove(ctx, cx + 2, cy);
690 		screen_write_vline(ctx, sy, 0, 0);
691 		screen_write_cursormove(ctx, cx, cy + sy / 2);
692 		screen_write_puts(ctx, &grid_default_cell, "<");
693 	} else
694 		data->left = -1;
695 	if (right) {
696 		data->right = cx + sx - 3;
697 		screen_write_cursormove(ctx, cx + sx - 3, cy);
698 		screen_write_vline(ctx, sy, 0, 0);
699 		screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2);
700 		screen_write_puts(ctx, &grid_default_cell, ">");
701 	} else
702 		data->right = -1;
703 
704 	data->start = start;
705 	data->end = end;
706 	data->each = each;
707 
708 	i = loop = 0;
709 	TAILQ_FOREACH(wp, &w->panes, entry) {
710 		if (loop == end)
711 			break;
712 		if (loop < start) {
713 			loop++;
714 			continue;
715 		}
716 
717 		if (wp == w->active)
718 			gc.fg = active_colour;
719 		else
720 			gc.fg = colour;
721 
722 		if (left)
723 			offset = 3 + (i * each);
724 		else
725 			offset = (i * each);
726 		if (loop == end - 1)
727 			width = each + remaining;
728 		else
729 			width = each - 1;
730 
731 		screen_write_cursormove(ctx, cx + offset, cy);
732 		screen_write_preview(ctx, &wp->base, width, sy);
733 
734 		if (window_pane_index(wp, &pane_idx) != 0)
735 			pane_idx = loop;
736 		xasprintf(&label, " %u ", pane_idx);
737 		window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc,
738 		    label);
739 		free(label);
740 
741 		if (loop != end - 1) {
742 			screen_write_cursormove(ctx, cx + offset + width, cy);
743 			screen_write_vline(ctx, sy, 0, 0);
744 		}
745 		loop++;
746 
747 		i++;
748 	}
749 }
750 
751 static void
752 window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
753     u_int sx, u_int sy)
754 {
755 	struct window_tree_itemdata	*item = itemdata;
756 	struct session			*sp;
757 	struct winlink			*wlp;
758 	struct window_pane		*wp;
759 
760 	window_tree_pull_item(item, &sp, &wlp, &wp);
761 	if (wp == NULL)
762 		return;
763 
764 	switch (item->type) {
765 	case WINDOW_TREE_NONE:
766 		break;
767 	case WINDOW_TREE_SESSION:
768 		window_tree_draw_session(modedata, sp, ctx, sx, sy);
769 		break;
770 	case WINDOW_TREE_WINDOW:
771 		window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy);
772 		break;
773 	case WINDOW_TREE_PANE:
774 		screen_write_preview(ctx, &wp->base, sx, sy);
775 		break;
776 	}
777 }
778 
779 static int
780 window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
781 {
782 	struct window_tree_itemdata	*item = itemdata;
783 	struct session			*s;
784 	struct winlink			*wl;
785 	struct window_pane		*wp;
786 	const char			*cmd;
787 
788 	window_tree_pull_item(item, &s, &wl, &wp);
789 
790 	switch (item->type) {
791 	case WINDOW_TREE_NONE:
792 		return (0);
793 	case WINDOW_TREE_SESSION:
794 		if (s == NULL)
795 			return (0);
796 		return (strstr(s->name, ss) != NULL);
797 	case WINDOW_TREE_WINDOW:
798 		if (s == NULL || wl == NULL)
799 			return (0);
800 		return (strstr(wl->window->name, ss) != NULL);
801 	case WINDOW_TREE_PANE:
802 		if (s == NULL || wl == NULL || wp == NULL)
803 			break;
804 		cmd = get_proc_name(wp->fd, wp->tty);
805 		if (cmd == NULL || *cmd == '\0')
806 			return (0);
807 		return (strstr(cmd, ss) != NULL);
808 	}
809 	return (0);
810 }
811 
812 static struct screen *
813 window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
814     struct args *args)
815 {
816 	struct window_tree_modedata	*data;
817 	struct screen			*s;
818 
819 	wp->modedata = data = xcalloc(1, sizeof *data);
820 
821 	if (args_has(args, 's'))
822 		data->type = WINDOW_TREE_SESSION;
823 	else if (args_has(args, 'w'))
824 		data->type = WINDOW_TREE_WINDOW;
825 	else
826 		data->type = WINDOW_TREE_PANE;
827 	memcpy(&data->fs, fs, sizeof data->fs);
828 
829 	data->wp = wp;
830 	data->references = 1;
831 
832 	if (args == NULL || !args_has(args, 'F'))
833 		data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
834 	else
835 		data->format = xstrdup(args_get(args, 'F'));
836 	if (args == NULL || args->argc == 0)
837 		data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
838 	else
839 		data->command = xstrdup(args->argv[0]);
840 	data->squash_groups = !args_has(args, 'G');
841 
842 	data->data = mode_tree_start(wp, args, window_tree_build,
843 	    window_tree_draw, window_tree_search, data, window_tree_sort_list,
844 	    nitems(window_tree_sort_list), &s);
845 	mode_tree_zoom(data->data, args);
846 
847 	mode_tree_build(data->data);
848 	mode_tree_draw(data->data);
849 
850 	data->type = WINDOW_TREE_NONE;
851 
852 	return (s);
853 }
854 
855 static void
856 window_tree_destroy(struct window_tree_modedata *data)
857 {
858 	u_int	i;
859 
860 	if (--data->references != 0)
861 		return;
862 
863 	for (i = 0; i < data->item_size; i++)
864 		window_tree_free_item(data->item_list[i]);
865 	free(data->item_list);
866 
867 	free(data->format);
868 	free(data->command);
869 
870 	free(data);
871 }
872 
873 static void
874 window_tree_free(struct window_pane *wp)
875 {
876 	struct window_tree_modedata *data = wp->modedata;
877 
878 	if (data == NULL)
879 		return;
880 
881 	data->dead = 1;
882 	mode_tree_free(data->data);
883 	window_tree_destroy(data);
884 }
885 
886 static void
887 window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
888 {
889 	struct window_tree_modedata	*data = wp->modedata;
890 
891 	mode_tree_resize(data->data, sx, sy);
892 }
893 
894 static char *
895 window_tree_get_target(struct window_tree_itemdata *item,
896     struct cmd_find_state *fs)
897 {
898 	struct session		*s;
899 	struct winlink		*wl;
900 	struct window_pane	*wp;
901 	char			*target;
902 
903 	window_tree_pull_item(item, &s, &wl, &wp);
904 
905 	target = NULL;
906 	switch (item->type) {
907 	case WINDOW_TREE_NONE:
908 		break;
909 	case WINDOW_TREE_SESSION:
910 		if (s == NULL)
911 			break;
912 		xasprintf(&target, "=%s:", s->name);
913 		break;
914 	case WINDOW_TREE_WINDOW:
915 		if (s == NULL || wl == NULL)
916 			break;
917 		xasprintf(&target, "=%s:%u.", s->name, wl->idx);
918 		break;
919 	case WINDOW_TREE_PANE:
920 		if (s == NULL || wl == NULL || wp == NULL)
921 			break;
922 		xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
923 		break;
924 	}
925 	if (target == NULL)
926 		cmd_find_clear_state(fs, 0);
927 	else
928 		cmd_find_from_winlink_pane(fs, wl, wp, 0);
929 	return (target);
930 }
931 
932 static void
933 window_tree_command_each(void* modedata, void* itemdata, struct client *c,
934     __unused key_code key)
935 {
936 	struct window_tree_modedata	*data = modedata;
937 	struct window_tree_itemdata	*item = itemdata;
938 	char				*name;
939 	struct cmd_find_state		 fs;
940 
941 	name = window_tree_get_target(item, &fs);
942 	if (name != NULL)
943 		mode_tree_run_command(c, &fs, data->entered, name);
944 	free(name);
945 }
946 
947 static enum cmd_retval
948 window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
949 {
950 	struct window_tree_modedata	*data = modedata;
951 
952 	if (!data->dead) {
953 		mode_tree_build(data->data);
954 		mode_tree_draw(data->data);
955 		data->wp->flags |= PANE_REDRAW;
956 	}
957 	window_tree_destroy(data);
958 	return (CMD_RETURN_NORMAL);
959 }
960 
961 static int
962 window_tree_command_callback(struct client *c, void *modedata, const char *s,
963     __unused int done)
964 {
965 	struct window_tree_modedata	*data = modedata;
966 
967 	if (s == NULL || *s == '\0' || data->dead)
968 		return (0);
969 
970 	data->entered = s;
971 	mode_tree_each_tagged(data->data, window_tree_command_each, c,
972 	    KEYC_NONE, 1);
973 	data->entered = NULL;
974 
975 	data->references++;
976 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
977 
978 	return (0);
979 }
980 
981 static void
982 window_tree_command_free(void *modedata)
983 {
984 	struct window_tree_modedata	*data = modedata;
985 
986 	window_tree_destroy(data);
987 }
988 
989 static void
990 window_tree_kill_each(__unused void* modedata, void* itemdata,
991     __unused struct client *c, __unused key_code key)
992 {
993 	struct window_tree_itemdata	*item = itemdata;
994 	struct session			*s;
995 	struct winlink			*wl;
996 	struct window_pane		*wp;
997 
998 	window_tree_pull_item(item, &s, &wl, &wp);
999 
1000 	switch (item->type) {
1001 	case WINDOW_TREE_NONE:
1002 		break;
1003 	case WINDOW_TREE_SESSION:
1004 		if (s != NULL) {
1005 			server_destroy_session(s);
1006 			session_destroy(s, __func__);
1007 		}
1008 		break;
1009 	case WINDOW_TREE_WINDOW:
1010 		if (wl != NULL)
1011 			server_kill_window(wl->window);
1012 		break;
1013 	case WINDOW_TREE_PANE:
1014 		if (wp != NULL)
1015 			server_kill_pane(wp);
1016 		break;
1017 	}
1018 }
1019 
1020 static int
1021 window_tree_kill_current_callback(struct client *c, void *modedata,
1022     const char *s, __unused int done)
1023 {
1024 	struct window_tree_modedata	*data = modedata;
1025 	struct mode_tree_data		*mtd = data->data;
1026 
1027 	if (s == NULL || *s == '\0' || data->dead)
1028 		return (0);
1029 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
1030 		return (0);
1031 
1032 	window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE);
1033 
1034 	data->references++;
1035 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1036 
1037 	return (0);
1038 }
1039 
1040 static int
1041 window_tree_kill_tagged_callback(struct client *c, void *modedata,
1042     const char *s, __unused int done)
1043 {
1044 	struct window_tree_modedata	*data = modedata;
1045 	struct mode_tree_data		*mtd = data->data;
1046 
1047 	if (s == NULL || *s == '\0' || data->dead)
1048 		return (0);
1049 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
1050 		return (0);
1051 
1052 	mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1);
1053 
1054 	data->references++;
1055 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1056 
1057 	return (0);
1058 }
1059 
1060 static key_code
1061 window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x,
1062     struct window_tree_itemdata *item)
1063 {
1064 	struct session		*s;
1065 	struct winlink		*wl;
1066 	struct window_pane	*wp;
1067 	u_int			 loop;
1068 
1069 	if (key != KEYC_MOUSEDOWN1_PANE)
1070 		return (KEYC_NONE);
1071 
1072 	if (data->left != -1 && x <= (u_int)data->left)
1073 		return ('<');
1074 	if (data->right != -1 && x >= (u_int)data->right)
1075 		return ('>');
1076 
1077 	if (data->left != -1)
1078 		x -= data->left;
1079 	else if (x != 0)
1080 		x--;
1081 	if (x == 0 || data->end == 0)
1082 		x = 0;
1083 	else {
1084 		x = x / data->each;
1085 		if (data->start + x >= data->end)
1086 			x = data->end - 1;
1087 	}
1088 
1089 	window_tree_pull_item(item, &s, &wl, &wp);
1090 	if (item->type == WINDOW_TREE_SESSION) {
1091 		if (s == NULL)
1092 			return (KEYC_NONE);
1093 		mode_tree_expand_current(data->data);
1094 		loop = 0;
1095 		RB_FOREACH(wl, winlinks, &s->windows) {
1096 			if (loop == data->start + x)
1097 				break;
1098 			loop++;
1099 		}
1100 		if (wl != NULL)
1101 			mode_tree_set_current(data->data, (uint64_t)wl);
1102 		return ('\r');
1103 	}
1104 	if (item->type == WINDOW_TREE_WINDOW) {
1105 		if (wl == NULL)
1106 			return (KEYC_NONE);
1107 		mode_tree_expand_current(data->data);
1108 		loop = 0;
1109 		TAILQ_FOREACH(wp, &wl->window->panes, entry) {
1110 			if (loop == data->start + x)
1111 				break;
1112 			loop++;
1113 		}
1114 		if (wp != NULL)
1115 			mode_tree_set_current(data->data, (uint64_t)wp);
1116 		return ('\r');
1117 	}
1118 	return (KEYC_NONE);
1119 }
1120 
1121 static void
1122 window_tree_key(struct window_pane *wp, struct client *c,
1123     __unused struct session *s, __unused struct winlink *wl, key_code key,
1124     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