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