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