xref: /openbsd-src/usr.bin/tmux/window-tree.c (revision 8ead0783a05eee83ab02af2c7b14b10fbcdce47d)
1 /* $OpenBSD: window-tree.c,v 1.22 2017/10/26 08:17:12 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "tmux.h"
25 
26 static struct screen	*window_tree_init(struct window_pane *,
27 			     struct cmd_find_state *, struct args *);
28 static void		 window_tree_free(struct window_pane *);
29 static void		 window_tree_resize(struct window_pane *, u_int, u_int);
30 static void		 window_tree_key(struct window_pane *,
31 			     struct client *, struct session *, key_code,
32 			     struct mouse_event *);
33 
34 #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'"
35 
36 #define WINDOW_TREE_DEFAULT_FORMAT \
37 	"#{?pane_format," \
38 		"#{pane_current_command} \"#{pane_title}\"" \
39 	"," \
40 		"#{?window_format," \
41 			"#{window_name}#{window_flags} " \
42 			"(#{window_panes} panes)" \
43 			"#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \
44 		"," \
45 			"#{session_windows} windows" \
46 			"#{?session_grouped, (group ,}" \
47 	    		"#{session_group}#{?session_grouped,),}" \
48 			"#{?session_attached, (attached),}" \
49 		"}" \
50 	"}"
51 
52 const struct window_mode window_tree_mode = {
53 	.name = "tree-mode",
54 
55 	.init = window_tree_init,
56 	.free = window_tree_free,
57 	.resize = window_tree_resize,
58 	.key = window_tree_key,
59 };
60 
61 enum window_tree_sort_type {
62 	WINDOW_TREE_BY_INDEX,
63 	WINDOW_TREE_BY_NAME,
64 	WINDOW_TREE_BY_TIME,
65 };
66 static const char *window_tree_sort_list[] = {
67 	"index",
68 	"name",
69 	"time"
70 };
71 
72 enum window_tree_type {
73 	WINDOW_TREE_NONE,
74 	WINDOW_TREE_SESSION,
75 	WINDOW_TREE_WINDOW,
76 	WINDOW_TREE_PANE,
77 };
78 
79 struct window_tree_itemdata {
80 	enum window_tree_type	type;
81 	int			session;
82 	int			winlink;
83 	int			pane;
84 };
85 
86 struct window_tree_modedata {
87 	struct window_pane		 *wp;
88 	int				  dead;
89 	int				  references;
90 
91 	struct mode_tree_data		 *data;
92 	char				 *format;
93 	char				 *command;
94 
95 	struct window_tree_itemdata	**item_list;
96 	u_int				  item_size;
97 
98 	const char			 *entered;
99 
100 	struct cmd_find_state		  fs;
101 	enum window_tree_type		  type;
102 
103 	int				  offset;
104 };
105 
106 static void
107 window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
108     struct winlink **wlp, struct window_pane **wp)
109 {
110 	*wp = NULL;
111 	*wlp = NULL;
112 	*sp = session_find_by_id(item->session);
113 	if (*sp == NULL)
114 		return;
115 	if (item->type == WINDOW_TREE_SESSION) {
116 		*wlp = (*sp)->curw;
117 		*wp = (*wlp)->window->active;
118 		return;
119 	}
120 
121 	*wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
122 	if (*wlp == NULL) {
123 		*sp = NULL;
124 		return;
125 	}
126 	if (item->type == WINDOW_TREE_WINDOW) {
127 		*wp = (*wlp)->window->active;
128 		return;
129 	}
130 
131 	*wp = window_pane_find_by_id(item->pane);
132 	if (!window_has_pane((*wlp)->window, *wp))
133 		*wp = NULL;
134 	if (*wp == NULL) {
135 		*sp = NULL;
136 		*wlp = NULL;
137 		return;
138 	}
139 }
140 
141 static struct window_tree_itemdata *
142 window_tree_add_item(struct window_tree_modedata *data)
143 {
144 	struct window_tree_itemdata	*item;
145 
146 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
147 	    sizeof *data->item_list);
148 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
149 	return (item);
150 }
151 
152 static void
153 window_tree_free_item(struct window_tree_itemdata *item)
154 {
155 	free(item);
156 }
157 
158 static int
159 window_tree_cmp_session_name(const void *a0, const void *b0)
160 {
161 	const struct session *const *a = a0;
162 	const struct session *const *b = b0;
163 
164 	return (strcmp((*a)->name, (*b)->name));
165 }
166 
167 static int
168 window_tree_cmp_session_time(const void *a0, const void *b0)
169 {
170 	const struct session *const *a = a0;
171 	const struct session *const *b = b0;
172 
173 	if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
174 		return (-1);
175 	if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
176 		return (1);
177 	return (strcmp((*a)->name, (*b)->name));
178 }
179 
180 static int
181 window_tree_cmp_window_name(const void *a0, const void *b0)
182 {
183 	const struct winlink *const *a = a0;
184 	const struct winlink *const *b = b0;
185 
186 	return (strcmp((*a)->window->name, (*b)->window->name));
187 }
188 
189 static int
190 window_tree_cmp_window_time(const void *a0, const void *b0)
191 {
192 	const struct winlink *const *a = a0;
193 	const struct winlink *const *b = b0;
194 
195 	if (timercmp(&(*a)->window->activity_time,
196 	    &(*b)->window->activity_time, >))
197 		return (-1);
198 	if (timercmp(&(*a)->window->activity_time,
199 	    &(*b)->window->activity_time, <))
200 		return (1);
201 	return (strcmp((*a)->window->name, (*b)->window->name));
202 }
203 
204 static int
205 window_tree_cmp_pane_time(const void *a0, const void *b0)
206 {
207 	const struct window_pane *const *a = a0;
208 	const struct window_pane *const *b = b0;
209 
210 	if ((*a)->active_point < (*b)->active_point)
211 		return (-1);
212 	if ((*a)->active_point > (*b)->active_point)
213 		return (1);
214 	return (0);
215 }
216 
217 static void
218 window_tree_build_pane(struct session *s, struct winlink *wl,
219     struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
220 {
221 	struct window_tree_modedata	*data = modedata;
222 	struct window_tree_itemdata	*item;
223 	char				*name, *text;
224 	u_int				 idx;
225 
226 	window_pane_index(wp, &idx);
227 
228 	item = window_tree_add_item(data);
229 	item->type = WINDOW_TREE_PANE;
230 	item->session = s->id;
231 	item->winlink = wl->idx;
232 	item->pane = wp->id;
233 
234 	text = format_single(NULL, data->format, NULL, s, wl, wp);
235 	xasprintf(&name, "%u", idx);
236 
237 	mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1);
238 	free(text);
239 	free(name);
240 }
241 
242 static int
243 window_tree_filter_pane(struct session *s, struct winlink *wl,
244     struct window_pane *wp, const char *filter)
245 {
246 	char	*cp;
247 	int	 result;
248 
249 	if (filter == NULL)
250 		return (1);
251 
252 	cp = format_single(NULL, filter, NULL, s, wl, wp);
253 	result = format_true(cp);
254 	free(cp);
255 
256 	return (result);
257 }
258 
259 static int
260 window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
261     u_int sort_type, struct mode_tree_item *parent, const char *filter)
262 {
263 	struct window_tree_modedata	*data = modedata;
264 	struct window_tree_itemdata	*item;
265 	struct mode_tree_item		*mti;
266 	char				*name, *text;
267 	struct window_pane		*wp, **l;
268 	u_int				 n, i;
269 	int				 expanded;
270 
271 	item = window_tree_add_item(data);
272 	item->type = WINDOW_TREE_WINDOW;
273 	item->session = s->id;
274 	item->winlink = wl->idx;
275 	item->pane = -1;
276 
277 	text = format_single(NULL, data->format, NULL, s, wl, NULL);
278 	xasprintf(&name, "%u", wl->idx);
279 
280 	if (data->type == WINDOW_TREE_SESSION ||
281 	    data->type == WINDOW_TREE_WINDOW)
282 		expanded = 0;
283 	else
284 		expanded = 1;
285 	mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text,
286 	    expanded);
287 	free(text);
288 	free(name);
289 
290 	wp = TAILQ_FIRST(&wl->window->panes);
291 	if (TAILQ_NEXT(wp, entry) == NULL) {
292 		if (!window_tree_filter_pane(s, wl, wp, filter))
293 			goto empty;
294 		return (1);
295 	}
296 
297 	l = NULL;
298 	n = 0;
299 
300 	TAILQ_FOREACH(wp, &wl->window->panes, entry) {
301 		if (!window_tree_filter_pane(s, wl, wp, filter))
302 			continue;
303 		l = xreallocarray(l, n + 1, sizeof *l);
304 		l[n++] = wp;
305 	}
306 	if (n == 0)
307 		goto empty;
308 
309 	switch (sort_type) {
310 	case WINDOW_TREE_BY_INDEX:
311 		break;
312 	case WINDOW_TREE_BY_NAME:
313 		/* Panes don't have names, so leave in number order. */
314 		break;
315 	case WINDOW_TREE_BY_TIME:
316 		qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
317 		break;
318 	}
319 
320 	for (i = 0; i < n; i++)
321 		window_tree_build_pane(s, wl, l[i], modedata, mti);
322 	free(l);
323 	return (1);
324 
325 empty:
326 	window_tree_free_item(item);
327 	data->item_size--;
328 	mode_tree_remove(data->data, mti);
329 	return (0);
330 }
331 
332 static void
333 window_tree_build_session(struct session *s, void* modedata,
334     u_int sort_type, const char *filter)
335 {
336 	struct window_tree_modedata	*data = modedata;
337 	struct window_tree_itemdata	*item;
338 	struct mode_tree_item		*mti;
339 	char				*text;
340 	struct winlink			*wl, **l;
341 	u_int				 n, i, empty;
342 	int				 expanded;
343 
344 	item = window_tree_add_item(data);
345 	item->type = WINDOW_TREE_SESSION;
346 	item->session = s->id;
347 	item->winlink = -1;
348 	item->pane = -1;
349 
350 	text = format_single(NULL, data->format, NULL, s, NULL, NULL);
351 
352 	if (data->type == WINDOW_TREE_SESSION)
353 		expanded = 0;
354 	else
355 		expanded = 1;
356 	mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
357 	    expanded);
358 	free(text);
359 
360 	l = NULL;
361 	n = 0;
362 	RB_FOREACH(wl, winlinks, &s->windows) {
363 		l = xreallocarray(l, n + 1, sizeof *l);
364 		l[n++] = wl;
365 	}
366 	switch (sort_type) {
367 	case WINDOW_TREE_BY_INDEX:
368 		break;
369 	case WINDOW_TREE_BY_NAME:
370 		qsort(l, n, sizeof *l, window_tree_cmp_window_name);
371 		break;
372 	case WINDOW_TREE_BY_TIME:
373 		qsort(l, n, sizeof *l, window_tree_cmp_window_time);
374 		break;
375 	}
376 
377 	empty = 0;
378 	for (i = 0; i < n; i++) {
379 		if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
380 		    filter))
381 			empty++;
382 	}
383 	if (empty == n) {
384 		window_tree_free_item(item);
385 		data->item_size--;
386 		mode_tree_remove(data->data, mti);
387 	}
388 	free(l);
389 }
390 
391 static void
392 window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
393     const char *filter)
394 {
395 	struct window_tree_modedata	*data = modedata;
396 	struct session			*s, **l;
397 	u_int				 n, i;
398 
399 	for (i = 0; i < data->item_size; i++)
400 		window_tree_free_item(data->item_list[i]);
401 	free(data->item_list);
402 	data->item_list = NULL;
403 	data->item_size = 0;
404 
405 	l = NULL;
406 	n = 0;
407 	RB_FOREACH(s, sessions, &sessions) {
408 		l = xreallocarray(l, n + 1, sizeof *l);
409 		l[n++] = s;
410 	}
411 	switch (sort_type) {
412 	case WINDOW_TREE_BY_INDEX:
413 		break;
414 	case WINDOW_TREE_BY_NAME:
415 		qsort(l, n, sizeof *l, window_tree_cmp_session_name);
416 		break;
417 	case WINDOW_TREE_BY_TIME:
418 		qsort(l, n, sizeof *l, window_tree_cmp_session_time);
419 		break;
420 	}
421 
422 	for (i = 0; i < n; i++)
423 		window_tree_build_session(l[i], modedata, sort_type, filter);
424 	free(l);
425 
426 	switch (data->type) {
427 	case WINDOW_TREE_NONE:
428 		break;
429 	case WINDOW_TREE_SESSION:
430 		*tag = (uint64_t)data->fs.s;
431 		break;
432 	case WINDOW_TREE_WINDOW:
433 		*tag = (uint64_t)data->fs.wl;
434 		break;
435 	case WINDOW_TREE_PANE:
436 		if (window_count_panes(data->fs.wl->window) == 1)
437 			*tag = (uint64_t)data->fs.wl;
438 		else
439 			*tag = (uint64_t)data->fs.wp;
440 		break;
441 	}
442 }
443 
444 
445 static void
446 window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
447     u_int sx, u_int sy, const struct grid_cell *gc, const char *label)
448 {
449 	size_t	 len;
450 	u_int	 ox, oy;
451 
452 	len = strlen(label);
453 	if (sx == 0 || sy == 1 || len > sx)
454 		return;
455 	ox = (sx - len + 1) / 2;
456 	oy = (sy + 1) / 2;
457 
458 	if (ox > 1 && ox + len < sx - 1 && sy >= 3) {
459 		screen_write_cursormove(ctx, px + ox - 1, py + oy - 1);
460 		screen_write_box(ctx, len + 2, 3);
461 	}
462 	screen_write_cursormove(ctx, px + ox, py + oy);
463 	screen_write_puts(ctx, gc, "%s", label);
464 }
465 
466 static void
467 window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
468     struct screen_write_ctx *ctx, u_int sx, u_int sy)
469 {
470 	struct options		*oo = s->options;
471 	struct winlink		*wl;
472 	struct window		*w;
473 	u_int			 loop, total, visible, each, width, offset;
474 	u_int			 current, start, end, remaining, i;
475 	struct grid_cell	 gc;
476 	int			 colour, active_colour, left, right;
477 	char			*label;
478 
479 	total = winlink_count(&s->windows);
480 
481 	memcpy(&gc, &grid_default_cell, sizeof gc);
482 	colour = options_get_number(oo, "display-panes-colour");
483 	active_colour = options_get_number(oo, "display-panes-active-colour");
484 
485 	if (sx / total < 24) {
486 		visible = sx / 24;
487 		if (visible == 0)
488 			visible = 1;
489 	} else
490 		visible = total;
491 
492 	current = 0;
493 	RB_FOREACH(wl, winlinks, &s->windows) {
494 		if (wl == s->curw)
495 			break;
496 		current++;
497 	}
498 
499 	if (current < visible) {
500 		start = 0;
501 		end = visible;
502 	} else if (current >= total - visible) {
503 		start = total - visible;
504 		end = total;
505 	} else {
506 		start = current - (visible / 2);
507 		end = start + visible;
508 	}
509 
510 	if (data->offset < -(int)start)
511 		data->offset = -(int)start;
512 	if (data->offset > (int)(total - end))
513 		data->offset = (int)(total - end);
514 	start += data->offset;
515 	end += data->offset;
516 
517 	left = (start != 0);
518 	right = (end != total);
519 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
520 		left = right = 0;
521 	if (left && right) {
522 		each = (sx - 6) / visible;
523 		remaining = (sx - 6) - (visible * each);
524 	} else if (left || right) {
525 		each = (sx - 3) / visible;
526 		remaining = (sx - 3) - (visible * each);
527 	} else {
528 		each = sx / visible;
529 		remaining = sx - (visible * each);
530 	}
531 	if (each == 0)
532 		return;
533 
534 	if (left) {
535 		screen_write_cursormove(ctx, 2, 0);
536 		screen_write_vline(ctx, sy, 0, 0);
537 		screen_write_cursormove(ctx, 0, sy / 2);
538 		screen_write_puts(ctx, &grid_default_cell, "<");
539 	}
540 	if (right) {
541 		screen_write_cursormove(ctx, sx - 3, 0);
542 		screen_write_vline(ctx, sy, 0, 0);
543 		screen_write_cursormove(ctx, sx - 1, sy / 2);
544 		screen_write_puts(ctx, &grid_default_cell, ">");
545 	}
546 
547 	i = loop = 0;
548 	RB_FOREACH(wl, winlinks, &s->windows) {
549 		if (loop == end)
550 			break;
551 		if (loop < start) {
552 			loop++;
553 			continue;
554 		}
555 		w = wl->window;
556 
557 		if (wl == s->curw)
558 			gc.fg = active_colour;
559 		else
560 			gc.fg = colour;
561 
562 		if (left)
563 			offset = 3 + (i * each);
564 		else
565 			offset = (i * each);
566 		if (loop == end - 1)
567 			width = each + remaining;
568 		else
569 			width = each - 1;
570 
571 		screen_write_cursormove(ctx, offset, 0);
572 		screen_write_preview(ctx, &w->active->base, width, sy);
573 
574 		xasprintf(&label, " %u:%s ", wl->idx, w->name);
575 		if (strlen(label) > width)
576 			xasprintf(&label, " %u ", wl->idx);
577 		window_tree_draw_label(ctx, offset, 0, width, sy, &gc, label);
578 		free(label);
579 
580 		if (loop != end - 1) {
581 			screen_write_cursormove(ctx, offset + width, 0);
582 			screen_write_vline(ctx, sy, 0, 0);
583 		}
584 		loop++;
585 
586 		i++;
587 	}
588 }
589 
590 static void
591 window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
592     struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
593 {
594 	struct options		*oo = s->options;
595 	struct window_pane	*wp;
596 	u_int			 loop, total, visible, each, width, offset;
597 	u_int			 current, start, end, remaining, i;
598 	struct grid_cell	 gc;
599 	int			 colour, active_colour, left, right, pane_idx;
600 	char			*label;
601 
602 	total = window_count_panes(w);
603 
604 	memcpy(&gc, &grid_default_cell, sizeof gc);
605 	colour = options_get_number(oo, "display-panes-colour");
606 	active_colour = options_get_number(oo, "display-panes-active-colour");
607 
608 	if (sx / total < 24) {
609 		visible = sx / 24;
610 		if (visible == 0)
611 			visible = 1;
612 	} else
613 		visible = total;
614 
615 	current = 0;
616 	TAILQ_FOREACH(wp, &w->panes, entry) {
617 		if (wp == w->active)
618 			break;
619 		current++;
620 	}
621 
622 	if (current < visible) {
623 		start = 0;
624 		end = visible;
625 	} else if (current >= total - visible) {
626 		start = total - visible;
627 		end = total;
628 	} else {
629 		start = current - (visible / 2);
630 		end = start + visible;
631 	}
632 
633 	if (data->offset < -(int)start)
634 		data->offset = -(int)start;
635 	if (data->offset > (int)(total - end))
636 		data->offset = (int)(total - end);
637 	start += data->offset;
638 	end += data->offset;
639 
640 	left = (start != 0);
641 	right = (end != total);
642 	if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
643 		left = right = 0;
644 	if (left && right) {
645 		each = (sx - 6) / visible;
646 		remaining = (sx - 6) - (visible * each);
647 	} else if (left || right) {
648 		each = (sx - 3) / visible;
649 		remaining = (sx - 3) - (visible * each);
650 	} else {
651 		each = sx / visible;
652 		remaining = sx - (visible * each);
653 	}
654 	if (each == 0)
655 		return;
656 
657 	if (left) {
658 		screen_write_cursormove(ctx, 2, 0);
659 		screen_write_vline(ctx, sy, 0, 0);
660 		screen_write_cursormove(ctx, 0, sy / 2);
661 		screen_write_puts(ctx, &grid_default_cell, "<");
662 	}
663 	if (right) {
664 		screen_write_cursormove(ctx, sx - 3, 0);
665 		screen_write_vline(ctx, sy, 0, 0);
666 		screen_write_cursormove(ctx, sx - 1, sy / 2);
667 		screen_write_puts(ctx, &grid_default_cell, ">");
668 	}
669 
670 	i = loop = 0;
671 	TAILQ_FOREACH(wp, &w->panes, entry) {
672 		if (loop == end)
673 			break;
674 		if (loop < start) {
675 			loop++;
676 			continue;
677 		}
678 
679 		if (wp == w->active)
680 			gc.fg = active_colour;
681 		else
682 			gc.fg = colour;
683 
684 		if (left)
685 			offset = 3 + (i * each);
686 		else
687 			offset = (i * each);
688 		if (loop == end - 1)
689 			width = each + remaining;
690 		else
691 			width = each - 1;
692 
693 		screen_write_cursormove(ctx, offset, 0);
694 		screen_write_preview(ctx, &wp->base, width, sy);
695 
696 		if (window_pane_index(wp, &pane_idx) != 0)
697 			pane_idx = loop;
698 		xasprintf(&label, " %u ", pane_idx);
699 		window_tree_draw_label(ctx, offset, 0, each, sy, &gc, label);
700 		free(label);
701 
702 		if (loop != end - 1) {
703 			screen_write_cursormove(ctx, offset + width, 0);
704 			screen_write_vline(ctx, sy, 0, 0);
705 		}
706 		loop++;
707 
708 		i++;
709 	}
710 }
711 
712 static struct screen *
713 window_tree_draw(void *modedata, void *itemdata, u_int sx, u_int sy)
714 {
715 	struct window_tree_itemdata	*item = itemdata;
716 	struct session			*sp;
717 	struct winlink			*wlp;
718 	struct window_pane		*wp;
719 	static struct screen		 s;
720 	struct screen_write_ctx		 ctx;
721 
722 	window_tree_pull_item(item, &sp, &wlp, &wp);
723 	if (wp == NULL)
724 		return (NULL);
725 
726 	screen_init(&s, sx, sy, 0);
727 	screen_write_start(&ctx, NULL, &s);
728 
729 	switch (item->type) {
730 	case WINDOW_TREE_NONE:
731 		return (0);
732 	case WINDOW_TREE_SESSION:
733 		window_tree_draw_session(modedata, sp, &ctx, sx, sy);
734 		break;
735 	case WINDOW_TREE_WINDOW:
736 		window_tree_draw_window(modedata, sp, wlp->window, &ctx, sx, sy);
737 		break;
738 	case WINDOW_TREE_PANE:
739 		screen_write_preview(&ctx, &wp->base, sx, sy);
740 		break;
741 	}
742 
743 	screen_write_stop(&ctx);
744 	return (&s);
745 }
746 
747 static int
748 window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
749 {
750 	struct window_tree_itemdata	*item = itemdata;
751 	struct session			*s;
752 	struct winlink			*wl;
753 	struct window_pane		*wp;
754 	const char			*cmd;
755 
756 	window_tree_pull_item(item, &s, &wl, &wp);
757 
758 	switch (item->type) {
759 	case WINDOW_TREE_NONE:
760 		return (0);
761 	case WINDOW_TREE_SESSION:
762 		if (s == NULL)
763 			return (0);
764 		return (strstr(s->name, ss) != NULL);
765 	case WINDOW_TREE_WINDOW:
766 		if (s == NULL || wl == NULL)
767 			return (0);
768 		return (strstr(wl->window->name, ss) != NULL);
769 	case WINDOW_TREE_PANE:
770 		if (s == NULL || wl == NULL || wp == NULL)
771 			break;
772 		cmd = get_proc_name(wp->fd, wp->tty);
773 		if (cmd == NULL || *cmd == '\0')
774 			return (0);
775 		return (strstr(cmd, ss) != NULL);
776 	}
777 	return (0);
778 }
779 
780 static struct screen *
781 window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
782     struct args *args)
783 {
784 	struct window_tree_modedata	*data;
785 	struct screen			*s;
786 
787 	wp->modedata = data = xcalloc(1, sizeof *data);
788 
789 	if (args_has(args, 's'))
790 		data->type = WINDOW_TREE_SESSION;
791 	else if (args_has(args, 'w'))
792 		data->type = WINDOW_TREE_WINDOW;
793 	else
794 		data->type = WINDOW_TREE_PANE;
795 	memcpy(&data->fs, fs, sizeof data->fs);
796 
797 	data->wp = wp;
798 	data->references = 1;
799 
800 	if (args == NULL || !args_has(args, 'F'))
801 		data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
802 	else
803 		data->format = xstrdup(args_get(args, 'F'));
804 	if (args == NULL || args->argc == 0)
805 		data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
806 	else
807 		data->command = xstrdup(args->argv[0]);
808 
809 	data->data = mode_tree_start(wp, args, window_tree_build,
810 	    window_tree_draw, window_tree_search, data, window_tree_sort_list,
811 	    nitems(window_tree_sort_list), &s);
812 
813 	mode_tree_build(data->data);
814 	mode_tree_draw(data->data);
815 
816 	data->type = WINDOW_TREE_NONE;
817 
818 	return (s);
819 }
820 
821 static void
822 window_tree_destroy(struct window_tree_modedata *data)
823 {
824 	u_int	i;
825 
826 	if (--data->references != 0)
827 		return;
828 
829 	mode_tree_free(data->data);
830 
831 	for (i = 0; i < data->item_size; i++)
832 		window_tree_free_item(data->item_list[i]);
833 	free(data->item_list);
834 
835 	free(data->format);
836 	free(data->command);
837 
838 	free(data);
839 }
840 
841 static void
842 window_tree_free(struct window_pane *wp)
843 {
844 	struct window_tree_modedata *data = wp->modedata;
845 
846 	if (data == NULL)
847 		return;
848 
849 	data->dead = 1;
850 	window_tree_destroy(data);
851 }
852 
853 static void
854 window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
855 {
856 	struct window_tree_modedata	*data = wp->modedata;
857 
858 	mode_tree_resize(data->data, sx, sy);
859 }
860 
861 static char *
862 window_tree_get_target(struct window_tree_itemdata *item,
863     struct cmd_find_state *fs)
864 {
865 	struct session		*s;
866 	struct winlink		*wl;
867 	struct window_pane	*wp;
868 	char			*target;
869 
870 	window_tree_pull_item(item, &s, &wl, &wp);
871 
872 	target = NULL;
873 	switch (item->type) {
874 	case WINDOW_TREE_NONE:
875 		break;
876 	case WINDOW_TREE_SESSION:
877 		if (s == NULL)
878 			break;
879 		xasprintf(&target, "=%s:", s->name);
880 		break;
881 	case WINDOW_TREE_WINDOW:
882 		if (s == NULL || wl == NULL)
883 			break;
884 		xasprintf(&target, "=%s:%u.", s->name, wl->idx);
885 		break;
886 	case WINDOW_TREE_PANE:
887 		if (s == NULL || wl == NULL || wp == NULL)
888 			break;
889 		xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
890 		break;
891 	}
892 	if (target == NULL)
893 		cmd_find_clear_state(fs, 0);
894 	else
895 		cmd_find_from_winlink_pane(fs, wl, wp, 0);
896 	return (target);
897 }
898 
899 static void
900 window_tree_command_each(void* modedata, void* itemdata, struct client *c,
901     __unused key_code key)
902 {
903 	struct window_tree_modedata	*data = modedata;
904 	struct window_tree_itemdata	*item = itemdata;
905 	char				*name;
906 	struct cmd_find_state		 fs;
907 
908 	name = window_tree_get_target(item, &fs);
909 	if (name != NULL)
910 		mode_tree_run_command(c, &fs, data->entered, name);
911 	free(name);
912 }
913 
914 static enum cmd_retval
915 window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
916 {
917 	struct window_tree_modedata	*data = modedata;
918 
919 	if (!data->dead) {
920 		mode_tree_build(data->data);
921 		mode_tree_draw(data->data);
922 		data->wp->flags |= PANE_REDRAW;
923 	}
924 	window_tree_destroy(data);
925 	return (CMD_RETURN_NORMAL);
926 }
927 
928 static int
929 window_tree_command_callback(struct client *c, void *modedata, const char *s,
930     __unused int done)
931 {
932 	struct window_tree_modedata	*data = modedata;
933 
934 	if (s == NULL || data->dead)
935 		return (0);
936 
937 	data->entered = s;
938 	mode_tree_each_tagged(data->data, window_tree_command_each, c,
939 	    KEYC_NONE, 1);
940 	data->entered = NULL;
941 
942 	data->references++;
943 	cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
944 
945 	return (0);
946 }
947 
948 static void
949 window_tree_command_free(void *modedata)
950 {
951 	struct window_tree_modedata	*data = modedata;
952 
953 	window_tree_destroy(data);
954 }
955 
956 static void
957 window_tree_key(struct window_pane *wp, struct client *c,
958     __unused struct session *s, key_code key, struct mouse_event *m)
959 {
960 	struct window_tree_modedata	*data = wp->modedata;
961 	struct window_tree_itemdata	*item;
962 	char				*name, *prompt;
963 	struct cmd_find_state		 fs;
964 	int				 finished;
965 	u_int				 tagged;
966 
967 	item = mode_tree_get_current(data->data);
968 	finished = mode_tree_key(data->data, c, &key, m);
969 	if (item != mode_tree_get_current(data->data))
970 		data->offset = 0;
971 	switch (key) {
972 	case '<':
973 		data->offset--;
974 		break;
975 	case '>':
976 		data->offset++;
977 		break;
978 	case ':':
979 		tagged = mode_tree_count_tagged(data->data);
980 		if (tagged != 0)
981 			xasprintf(&prompt, "(%u tagged) ", tagged);
982 		else
983 			xasprintf(&prompt, "(current) ");
984 		data->references++;
985 		status_prompt_set(c, prompt, "", window_tree_command_callback,
986 		    window_tree_command_free, data, PROMPT_NOFORMAT);
987 		free(prompt);
988 		break;
989 	case '\r':
990 		item = mode_tree_get_current(data->data);
991 		name = window_tree_get_target(item, &fs);
992 		if (name != NULL)
993 			mode_tree_run_command(c, NULL, data->command, name);
994 		finished = 1;
995 		free(name);
996 		break;
997 	}
998 	if (finished)
999 		window_pane_reset_mode(wp);
1000 	else {
1001 		mode_tree_draw(data->data);
1002 		wp->flags |= PANE_REDRAW;
1003 	}
1004 }
1005