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