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